aboutsummaryrefslogtreecommitdiff
path: root/doc
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-07-23 08:08:33 +0800
committer魏曹先生 <1992414357@qq.com>2026-07-23 14:01:45 +0800
commit68a652ed2f51d366bb8033497e6dfe545895410e (patch)
treed463833846248410733e196a0939c59cd67ca8a2 /doc
feat: scaffold crate structure and implement core macros
Add the project skeleton, LICENSE files, README, Makefile, doc examples, and the initial implementation of `#[func]`, `invoke!`, and `select!` procedural macros.
Diffstat (limited to 'doc')
-rw-r--r--doc/args/func.md4
-rw-r--r--doc/args/invoke.md8
-rw-r--r--doc/args/select_arm.md7
-rw-r--r--doc/func.md12
-rw-r--r--doc/invoke.md17
-rw-r--r--doc/lib.md115
-rw-r--r--doc/select.md22
-rw-r--r--doc/usage/func.rs4
-rw-r--r--doc/usage/func_async_expand.rs3
-rw-r--r--doc/usage/func_async_expand.rs.lock3
-rw-r--r--doc/usage/func_expand.rs3
-rw-r--r--doc/usage/func_expand.rs.lock3
-rw-r--r--doc/usage/invoke.rs10
-rw-r--r--doc/usage/invoke_async_expand.rs6
-rw-r--r--doc/usage/invoke_async_expand.rs.lock6
-rw-r--r--doc/usage/invoke_expand.rs6
-rw-r--r--doc/usage/invoke_expand.rs.lock6
-rw-r--r--doc/usage/select.rs3
-rw-r--r--doc/usage/select_async_expand.rs5
-rw-r--r--doc/usage/select_async_expand.rs.lock5
-rw-r--r--doc/usage/select_expand.rs5
-rw-r--r--doc/usage/select_expand.rs.lock5
22 files changed, 258 insertions, 0 deletions
diff --git a/doc/args/func.md b/doc/args/func.md
new file mode 100644
index 0000000..7d718a2
--- /dev/null
+++ b/doc/args/func.md
@@ -0,0 +1,4 @@
+Arguments parsed from the `#[func]` attribute.
+
+If the attribute is empty (`#[func]`), defaults to feature name `"async"`.
+When a string literal is provided (`#[func("tokio_rt")]`), that value is used.
diff --git a/doc/args/invoke.md b/doc/args/invoke.md
new file mode 100644
index 0000000..74a1823
--- /dev/null
+++ b/doc/args/invoke.md
@@ -0,0 +1,8 @@
+Arguments parsed by the `invoke!` macro.
+
+Two forms:
+
+| Variant | Form | Behavior |
+| ---------- | ------------------------- | ---------------------------------- |
+| `Default` | `invoke!(expr)` | Feature name defaults to `"async"` |
+| `Explicit` | `invoke!("feat" => expr)` | Uses the given feature name |
diff --git a/doc/args/select_arm.md b/doc/args/select_arm.md
new file mode 100644
index 0000000..c371d22
--- /dev/null
+++ b/doc/args/select_arm.md
@@ -0,0 +1,7 @@
+A single arm inside the `select!` macro.
+
+| Variant | Example | Meaning |
+| ---------- | ----------------- | ----------------------------------------------------- |
+| `Explicit` | `"async" => expr` | Arm guarded by `feature = "async"` |
+| `Not` | `! => expr` | Arm guarded by `not(feature = <other_arm's_feature>)` |
+| `Implicit` | `expr` | Feature determined by `.await` detection |
diff --git a/doc/func.md b/doc/func.md
new file mode 100644
index 0000000..86925d7
--- /dev/null
+++ b/doc/func.md
@@ -0,0 +1,12 @@
+Attribute macro that generates both a sync and an async version of a function,
+gated by a Cargo feature flag.
+
+The function is written as a regular (non-async) `fn`. The macro duplicates it
+internally into two implementations:
+
+- When the feature is **disabled**: the original `fn` is used as-is.
+- When the feature is **enabled**: the function is promoted to `async fn`.
+
+By default the feature name is `"async"`. A different name can be supplied as
+an attribute argument, for example `#[func("tokio_rt")]` to gate on the feature
+`"tokio_rt"` instead.
diff --git a/doc/invoke.md b/doc/invoke.md
new file mode 100644
index 0000000..5784fba
--- /dev/null
+++ b/doc/invoke.md
@@ -0,0 +1,17 @@
+Procedural macro that wraps a call expression, adding `.await` when the async
+feature is enabled, and leaving it as a plain call otherwise.
+
+This macro is designed to be used **inside a `#[func]`-annotated function**.
+`#[func]` ensures that the enclosing function is either sync or async depending
+on the feature flag. `invoke!` mirrors that same gating on the call site:
+
+- Feature disabled, enclosing fn is sync → `invoke!(callee(args))` expands to
+ `callee(args)` — a plain synchronous call.
+- Feature enabled, enclosing fn is async → `invoke!(callee(args))` expands to
+ `callee(args).await` — an awaited asynchronous call.
+
+Both the callee and the caller should be produced by `#[func]` so that their
+sync/async nature changes in lockstep.
+
+An explicit feature name can be provided with the `=>` syntax. This is useful
+when different features control different parts of the pipeline.
diff --git a/doc/lib.md b/doc/lib.md
new file mode 100644
index 0000000..88715a5
--- /dev/null
+++ b/doc/lib.md
@@ -0,0 +1,115 @@
+# might_be_async
+
+A proc-macro crate that generates both synchronous and asynchronous versions of
+functions, gated by a Cargo feature flag.
+
+## Provided macros
+
+### `#[func]` — Attribute macro
+
+Generates both synchronous and asynchronous versions of a function, gated by a Cargo feature flag. When the feature is off, the function is a plain `fn`; when on, it becomes an `async fn`.
+
+Accepts an optional string argument specifying the feature name (defaults to `"async"`).
+
+```rust
+// Uses default feature name "async"
+#[might_be_async::func]
+fn compute(x: i32) -> i32 { x }
+
+// Custom feature name
+#[might_be_async::func("foo_async")]
+fn fetch(url: &str) -> String { "url".into() }
+```
+
+### `invoke!` — Proc macro
+
+Wraps a function call, automatically adding `.await` when the feature is enabled. This lets you call `#[func]`-annotated functions without worrying about whether they are sync or async at the call site.
+
+Accepts an optional feature name before `=>` (defaults to `"async"`).
+
+```rust
+use might_be_async::invoke;
+# use std::env::args;
+# #[might_be_async::func]
+# fn entry() {
+# let url = String::default();
+
+// Uses default feature name "async"
+invoke!(do_stuff(args()));
+
+// Custom feature name
+invoke!("foo_async" => fetch_data(url));
+# }
+# #[might_be_async::func]
+# fn fetch_data(url: String) {}
+# #[might_be_async::func]
+# fn do_stuff(url: std::env::Args) {}
+```
+
+### `select!` — Proc macro
+
+Chooses between two expressions at compile time based on whether the feature flag is active. The first branch (marked with `"async" =>`) is taken when the feature is enabled; the second branch (marked with `! =>`) is taken otherwise.
+
+Supports three arm syntaxes:
+
+```rust
+use might_be_async::select;
+# #[might_be_async::func("foo_async")]
+# fn entry() {
+
+// Explicit feature name arm and a negation arm
+select!("foo_async" => { async_expr().await } else ! => { sync_expr() });
+
+// Two explicit feature names (second can use ! prefix)
+select!("foo_sync" => expr_a() else "foo_async" => expr_b().await);
+
+// Implicit arms — auto-detects .await to decide async vs sync
+select!({ expr_with_await().await } else { expr_without_await() });
+
+# }
+# async fn expr_with_await () {}
+# fn expr_without_await () {}
+# async fn async_expr () {}
+# fn sync_expr () {}
+# async fn expr_b () {}
+# fn expr_a () {}
+```
+
+## Complete example
+
+The three macros are designed to work together. `#[func]` keeps a set of
+functions in sync: when the feature is off they are all plain `fn`, and when
+it is on they are all `async fn`. `invoke!` calls between them automatically
+add or omit `.await`, and `select!` picks the correct branch at compile time.
+
+```rust
+use might_be_async::{func, invoke, select};
+
+/// A simple computation, gated by the feature flag.
+#[func]
+fn double(x: i32) -> i32 {
+ x * 2
+}
+
+/// Calls `double` through `invoke!` — the call is plain or awaited
+/// depending on whether the feature is active.
+#[func]
+fn add_then_double(a: i32, b: i32) -> i32 {
+ let sum = a + b;
+ invoke!(double(sum))
+}
+
+/// Uses `select!` to return a different value in each mode.
+#[func]
+fn pick(flag: bool) -> i32 {
+ if flag {
+ select!("foo_async" => 100 else ! => 200)
+ } else {
+ 0
+ }
+}
+
+assert_eq!(add_then_double(3, 4), 14);
+assert_eq!(pick(true), 200);
+assert_eq!(pick(false), 0);
+```
diff --git a/doc/select.md b/doc/select.md
new file mode 100644
index 0000000..d2330bd
--- /dev/null
+++ b/doc/select.md
@@ -0,0 +1,22 @@
+Procedural macro that chooses between two expressions based on whether the
+async feature is enabled, producing a value.
+
+## Explicit mode
+
+Each arm is labelled with a feature name. When the named feature is enabled
+that arm is selected; otherwise the other arm is used. Since only one feature
+is ever active at a time, the arm labelled `"sync"` effectively acts as an else
+branch.
+
+The `!` token inverts the sense — it selects the arm when the corresponding
+feature is **not** enabled.
+
+## Implicit mode
+
+Feature names are omitted; the macro inspects the token stream to decide which
+expression is async. Whichever arm contains a `.await` call is treated as the
+async branch.
+
+When neither arm contains `.await`, the first expression is assigned to the
+async branch and the second to sync. The two branches trade places during
+expansion so the correct one is active in each mode.
diff --git a/doc/usage/func.rs b/doc/usage/func.rs
new file mode 100644
index 0000000..f407019
--- /dev/null
+++ b/doc/usage/func.rs
@@ -0,0 +1,4 @@
+#[func]
+pub fn greet(name: &str) -> String {
+ "Hello, {name}!".to_string()
+}
diff --git a/doc/usage/func_async_expand.rs b/doc/usage/func_async_expand.rs
new file mode 100644
index 0000000..ac52411
--- /dev/null
+++ b/doc/usage/func_async_expand.rs
@@ -0,0 +1,3 @@
+pub async fn greet(name: &str) -> String {
+ "Hello, {name}!".to_string()
+}
diff --git a/doc/usage/func_async_expand.rs.lock b/doc/usage/func_async_expand.rs.lock
new file mode 100644
index 0000000..ac52411
--- /dev/null
+++ b/doc/usage/func_async_expand.rs.lock
@@ -0,0 +1,3 @@
+pub async fn greet(name: &str) -> String {
+ "Hello, {name}!".to_string()
+}
diff --git a/doc/usage/func_expand.rs b/doc/usage/func_expand.rs
new file mode 100644
index 0000000..70dee21
--- /dev/null
+++ b/doc/usage/func_expand.rs
@@ -0,0 +1,3 @@
+pub fn greet(name: &str) -> String {
+ "Hello, {name}!".to_string()
+}
diff --git a/doc/usage/func_expand.rs.lock b/doc/usage/func_expand.rs.lock
new file mode 100644
index 0000000..70dee21
--- /dev/null
+++ b/doc/usage/func_expand.rs.lock
@@ -0,0 +1,3 @@
+pub fn greet(name: &str) -> String {
+ "Hello, {name}!".to_string()
+}
diff --git a/doc/usage/invoke.rs b/doc/usage/invoke.rs
new file mode 100644
index 0000000..0ae90d2
--- /dev/null
+++ b/doc/usage/invoke.rs
@@ -0,0 +1,10 @@
+#[func]
+fn compute(x: i32) -> i32 {
+ x * 2
+}
+
+#[func]
+fn example() -> i32 {
+ // invoke! macro should be used with #[func]
+ invoke!(compute(5))
+}
diff --git a/doc/usage/invoke_async_expand.rs b/doc/usage/invoke_async_expand.rs
new file mode 100644
index 0000000..4b8d93f
--- /dev/null
+++ b/doc/usage/invoke_async_expand.rs
@@ -0,0 +1,6 @@
+async fn compute(x: i32) -> i32 {
+ x * 2
+}
+async fn example() -> i32 {
+ { { compute(5).await } }
+}
diff --git a/doc/usage/invoke_async_expand.rs.lock b/doc/usage/invoke_async_expand.rs.lock
new file mode 100644
index 0000000..4b8d93f
--- /dev/null
+++ b/doc/usage/invoke_async_expand.rs.lock
@@ -0,0 +1,6 @@
+async fn compute(x: i32) -> i32 {
+ x * 2
+}
+async fn example() -> i32 {
+ { { compute(5).await } }
+}
diff --git a/doc/usage/invoke_expand.rs b/doc/usage/invoke_expand.rs
new file mode 100644
index 0000000..3274d77
--- /dev/null
+++ b/doc/usage/invoke_expand.rs
@@ -0,0 +1,6 @@
+fn compute(x: i32) -> i32 {
+ x * 2
+}
+fn example() -> i32 {
+ { { compute(5) } }
+}
diff --git a/doc/usage/invoke_expand.rs.lock b/doc/usage/invoke_expand.rs.lock
new file mode 100644
index 0000000..3274d77
--- /dev/null
+++ b/doc/usage/invoke_expand.rs.lock
@@ -0,0 +1,6 @@
+fn compute(x: i32) -> i32 {
+ x * 2
+}
+fn example() -> i32 {
+ { { compute(5) } }
+}
diff --git a/doc/usage/select.rs b/doc/usage/select.rs
new file mode 100644
index 0000000..8cffcf8
--- /dev/null
+++ b/doc/usage/select.rs
@@ -0,0 +1,3 @@
+fn example() {
+ select! [{ 100 } else { 200 }];
+}
diff --git a/doc/usage/select_async_expand.rs b/doc/usage/select_async_expand.rs
new file mode 100644
index 0000000..b2553f3
--- /dev/null
+++ b/doc/usage/select_async_expand.rs
@@ -0,0 +1,5 @@
+fn example() {
+ {
+ { { 100 } }
+ };
+}
diff --git a/doc/usage/select_async_expand.rs.lock b/doc/usage/select_async_expand.rs.lock
new file mode 100644
index 0000000..b2553f3
--- /dev/null
+++ b/doc/usage/select_async_expand.rs.lock
@@ -0,0 +1,5 @@
+fn example() {
+ {
+ { { 100 } }
+ };
+}
diff --git a/doc/usage/select_expand.rs b/doc/usage/select_expand.rs
new file mode 100644
index 0000000..8caed8e
--- /dev/null
+++ b/doc/usage/select_expand.rs
@@ -0,0 +1,5 @@
+fn example() {
+ {
+ { { 200 } }
+ };
+}
diff --git a/doc/usage/select_expand.rs.lock b/doc/usage/select_expand.rs.lock
new file mode 100644
index 0000000..8caed8e
--- /dev/null
+++ b/doc/usage/select_expand.rs.lock
@@ -0,0 +1,5 @@
+fn example() {
+ {
+ { { 200 } }
+ };
+}