aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/config.rs73
-rw-r--r--src/func.rs12
-rw-r--r--src/invoke.rs17
-rw-r--r--test/src/test_func.rs32
-rw-r--r--test/src/test_select.rs25
5 files changed, 153 insertions, 6 deletions
diff --git a/src/config.rs b/src/config.rs
index 06aa088..5962103 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -22,17 +22,78 @@ fn read_default_feature_name() -> String {
Err(_) => return "async".to_string(),
};
- let value: toml::Value = match content.parse() {
- Ok(v) => v,
- Err(_) => return "async".to_string(),
- };
+ parse_feature_name_from_toml(&content).unwrap_or_else(|| "async".to_string())
+}
+/// Pure function: parse `default_feature_name` from TOML content.
+/// Returns `None` if the key is missing, unreadable, or not a string.
+fn parse_feature_name_from_toml(content: &str) -> Option<String> {
+ let value: toml::Value = content.parse().ok()?;
value
.get("package")
.and_then(|p| p.get("metadata"))
.and_then(|m| m.get("might_be_async"))
.and_then(|a| a.get("default_feature_name"))
.and_then(|v| v.as_str())
- .unwrap_or("async")
- .to_string()
+ .map(|s| s.to_string())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn parses_custom_feature_name() {
+ let toml = r#"
+ [package]
+ [package.metadata.might_be_async]
+ default_feature_name = "my_async"
+ "#;
+ assert_eq!(
+ parse_feature_name_from_toml(toml),
+ Some("my_async".to_string())
+ );
+ }
+
+ #[test]
+ fn missing_metadata_returns_none() {
+ let toml = "[package]\nname = \"foo\"\n";
+ assert_eq!(parse_feature_name_from_toml(toml), None);
+ }
+
+ #[test]
+ fn missing_might_be_async_section_returns_none() {
+ let toml = r#"
+ [package]
+ [package.metadata]
+ some_other_key = "value"
+ "#;
+ assert_eq!(parse_feature_name_from_toml(toml), None);
+ }
+
+ #[test]
+ fn empty_string_returns_none() {
+ assert_eq!(parse_feature_name_from_toml(""), None);
+ }
+
+ #[test]
+ fn invalid_toml_returns_none() {
+ assert_eq!(parse_feature_name_from_toml("not valid toml {{"), None);
+ }
+
+ #[test]
+ fn value_is_not_string_returns_none() {
+ let toml = r#"
+ [package.metadata.might_be_async]
+ default_feature_name = 42
+ "#;
+ assert_eq!(parse_feature_name_from_toml(toml), None);
+ }
+
+ #[test]
+ fn env_default_returns_foo_async() {
+ // In `cargo test`, CARGO_MANIFEST_DIR is set to this crate's directory,
+ // whose Cargo.toml has `default_feature_name = "foo_async"`.
+ assert_eq!(default_feature_name(), "foo_async");
+ }
}
diff --git a/src/func.rs b/src/func.rs
index 5557992..a502e36 100644
--- a/src/func.rs
+++ b/src/func.rs
@@ -78,4 +78,16 @@ mod tests {
let args: FuncArgs = syn::parse2(input).unwrap();
assert_eq!(args.feature_name, "custom_name");
}
+
+ #[test]
+ fn integer_literal_is_rejected() {
+ let input: proc_macro2::TokenStream = "42".parse().unwrap();
+ assert!(syn::parse2::<FuncArgs>(input).is_err());
+ }
+
+ #[test]
+ fn multiple_args_are_rejected() {
+ let input: proc_macro2::TokenStream = r#""a" "b""#.parse().unwrap();
+ assert!(syn::parse2::<FuncArgs>(input).is_err());
+ }
}
diff --git a/src/invoke.rs b/src/invoke.rs
index f7da20a..2ada0b4 100644
--- a/src/invoke.rs
+++ b/src/invoke.rs
@@ -86,4 +86,21 @@ mod tests {
_ => panic!("expected Explicit variant"),
}
}
+
+ #[test]
+ fn explicit_missing_arrow_is_rejected() {
+ let input: proc_macro2::TokenStream = r#""ft" expr()"#.parse().unwrap();
+ assert!(syn::parse2::<InvokeArgs>(input).is_err());
+ }
+
+ #[test]
+ fn integer_parses_as_default_variant() {
+ // integer literal is not a LitStr, so it falls through to Default variant
+ let input: proc_macro2::TokenStream = "42".parse().unwrap();
+ let args: InvokeArgs = syn::parse2(input).unwrap();
+ match args {
+ InvokeArgs::Default(_) => {} // expected
+ _ => panic!("expected Default variant"),
+ }
+ }
}
diff --git a/test/src/test_func.rs b/test/src/test_func.rs
index 71fe365..666a99f 100644
--- a/test/src/test_func.rs
+++ b/test/src/test_func.rs
@@ -34,3 +34,35 @@ fn triple(x: i32) -> i32 {
fn test_func_custom_feature() {
assert_eq!(triple(3), 9);
}
+
+#[cfg(feature = "metadata_async")]
+#[test]
+fn test_func_async_identity() {
+ let result = futures::executor::block_on(async { identity(42).await });
+ assert_eq!(result, 42);
+}
+
+#[cfg(feature = "metadata_async")]
+#[test]
+fn test_func_async_generic() {
+ let r = futures::executor::block_on(async { first(10, 20).await });
+ assert_eq!(r, 10);
+}
+
+#[might_be_async::func]
+fn greet_async(name: &str) -> String {
+ format!("Hello, {name}!")
+}
+
+#[cfg(not(feature = "metadata_async"))]
+#[test]
+fn test_func_sync_greet() {
+ assert_eq!(greet_async("world"), "Hello, world!");
+}
+
+#[cfg(feature = "metadata_async")]
+#[test]
+fn test_func_async_greet() {
+ let r = futures::executor::block_on(greet_async("world"));
+ assert_eq!(r, "Hello, world!");
+}
diff --git a/test/src/test_select.rs b/test/src/test_select.rs
index 2cf3bfe..901ee35 100644
--- a/test/src/test_select.rs
+++ b/test/src/test_select.rs
@@ -90,3 +90,28 @@ fn test_select_metadata_two_not() {
let r = might_be_async::select! { ! => { 70 } else ! => { 80 } };
assert_eq!(r, 80);
}
+
+#[might_be_async::func]
+fn pick(toggle: bool) -> &'static str {
+ might_be_async::select! { "metadata_async" => {
+ if toggle { "async_on_a" } else { "async_on_b" }
+ } else ! => {
+ if toggle { "sync_a" } else { "sync_b" }
+ }}
+}
+
+#[cfg(not(feature = "metadata_async"))]
+#[test]
+fn test_select_inside_func_sync() {
+ assert_eq!(pick(true), "sync_a");
+ assert_eq!(pick(false), "sync_b");
+}
+
+#[cfg(feature = "metadata_async")]
+#[test]
+fn test_select_inside_func_async() {
+ let r = futures::executor::block_on(async { pick(true).await });
+ assert_eq!(r, "async_on_a");
+ let r = futures::executor::block_on(async { pick(false).await });
+ assert_eq!(r, "async_on_b");
+}