feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
+1
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"0f881b1debfcbb2e2dcbac6889ba2b704cccf89ed07cfdbc163a6a3baf64ed4c",".github/workflows/rust.yml":"aaefe089547089fb3015482ca5ed0f6127630a13858a4b8f87d7b30f754f24fd",".rustfmt.toml":"49d27bdee7fdfa70ce80d636daf95c185c3454da8293963844f9f47b16d81b49",".versionrc":"96d3c71c6c827359bae1387f84e1feeea9796bc5338e69116d3b0e1ae0fa8178","Cargo.lock":"f0c5e49df4b3416bd95fded4bd5dc8cf9a30df8da3782a45aa74530485c26a28","Cargo.toml":"888faffdd606335c3a5956d8f815390f8fd6c7128dccee834fa69c8aa9a7cc35","Cargo.toml.orig":"c92dc50944131328434d5fdbd40282cce5021792ee8de5e9a6bf89b21560875f","LICENSE":"1041942721c04225d9d7d932a2d5e8dafa2f0585f6471373af522bca69b9799d","README.md":"00835b701dd11f199817859444719bf536b136bb12bc1b97448f7e96e6544f9c","examples/service_client.rs":"4f902609ec3ed9a4fceb3bb3b9850df97cffaae29e175ee28edce22f180beee3","examples/service_client_both.rs":"0be4fbab808b727ae8f55b151e7556278ad9d0d05abaf7d86ad03e300cbd6eba","src/debug.rs":"eca0ea329d0513aae27a551bb432af63ae7857b4ed5e2dfe55512f2956f035f7","src/doctests.rs":"7d22866c548843b8ded903b8b69852aec95306cdb6994fb0456c9686d24b2988","src/lib.rs":"ac9b69b6b3c9c75dddd01fb2baa86379f0f31243c8c5d9608b529de2f1ea0394","src/macros.rs":"dfc1d6938863ba12f4d41db2d83770d63286999998edc04591afb45b5fe0e2cf","src/params.rs":"8f0dba24a7261aa5c9eb3c523b6a626246d0d783a2f1430400b3c1aacefacb0a","src/utils.rs":"08bece94dae4e648c05469d6ac381fd26d4db7096fb5de415c67dd9cc40400ad","src/visit_ext.rs":"e183d53f2e466a246d1b9d5616ceeb59f811d281239758e692aaa2744e8cd83b","src/visitor_async.rs":"c34237dc0435fd18e99ba8d86ef43a0246f14e44cd21a298f1315f8aedc408be","src/visitor_content.rs":"74344701237ac81cfb973c73dc21093a9c66e083a91eaa628469662a4c8ea9c2","tests/doctests.rs":"a3e0f8ce4dbfa92297ca6353da89990c31f41087e0a0288f8c81f79c86cc2a6f","tests/test.rs":"479fe748e7a8db9e14f97d5b334f7545d8caefb2069ea0be4170b05525449aa1","tests/ui/01-maybe-async.rs":"05ef981ca2f51b6b49838c81c48f2658774839198d6e2097060879f6f96cadc0","tests/ui/02-must-be-async.rs":"545218e6316197451af0e232a04465e8225dc9645330329f333afc475d1ed99c","tests/ui/03-must-be-sync.rs":"5b03775f18fbcb7c5acaf929ac70123746d1b34d4dd350148b6e915025bb5f62","tests/ui/04-unit-test-util.rs":"615e121b31bfbf68b225d3d8497754767d7d30e2e24ca9b553edea958727bdec","tests/ui/05-replace-future-generic-type-with-output.rs":"d677901f021742d2271e9c8581025a538e43624e6b7af40778c52b50d975f926","tests/ui/06-sync_impl_async_impl.rs":"4d98fdde60232eb4563745cbd33ce74a526eee742240ef87713041eb6cec8ff7","tests/ui/test_fail/01-empty-test.rs":"1a3a5530b5d0c0dd7e25b8ea8374c9446ab92a27a521504b34630b777b8ce7d9","tests/ui/test_fail/01-empty-test.stderr":"420a368b6fce1f5237b77a4dbca71081fc2e76b9267376ee98fb81aa246c4bb6","tests/ui/test_fail/02-unknown-path.rs":"d00438c3332ffb79d1d99311f9895c6bb9abcda83ac13b7979d6e9e3fd2575a1","tests/ui/test_fail/02-unknown-path.stderr":"e0b6dacaaf926dd7d363f200ed4780815706537f6343d6797b085139db116133","tests/ui/test_fail/03-async-gt2.rs":"dc95152506fe275143ebe4a08da31237d89c06061e111671731f134dbea872a4","tests/ui/test_fail/03-async-gt2.stderr":"316c26307e95ae41bd4796d80a94285ce7323cb2517a55e062d813e8968d3303","tests/ui/test_fail/04-bad-sync-cond.rs":"56ce741aef3c795440937da79dbef1808ca6fbb130b5cf57b7698309f31730cd","tests/ui/test_fail/04-bad-sync-cond.stderr":"7bd708d4ba7c85eb93c004c3d9309ce9cf54660d998356def7c567d53a26b355","tests/unit-test-util.rs":"ce5c5ca0bd2ce69234b75ca28b5ad3a734f90784e6e43af7b05ee24afa6da39a"},"package":"a1e083394889336bc66a4eaf1011ffbfa74893e910f902a9f271fa624c61e1b2"}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "384f6bd800420a8f42993271e5d7907848d29da9"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
reorder_imports = true
|
||||
format_code_in_doc_comments = true
|
||||
normalize_doc_attributes = true
|
||||
wrap_comments = true
|
||||
comment_width = 100
|
||||
format_strings = true
|
||||
imports_granularity = "Crate"
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"bumpFiles": [
|
||||
{
|
||||
"filename": "Cargo.toml",
|
||||
"updater": ".standard-version/cargo-updater.js"
|
||||
}
|
||||
],
|
||||
"packageFiles": [
|
||||
{
|
||||
"filename": "Cargo.toml",
|
||||
"updater": ".standard-version/cargo-updater.js"
|
||||
}
|
||||
]
|
||||
}
|
||||
+1170
File diff suppressed because it is too large.
Load diff
Vendored
+84
@@ -0,0 +1,84 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "maybe-async-cfg"
|
||||
version = "0.2.4"
|
||||
authors = ["Sergey V. Kokorin <nvksv@nvksv.ru>"]
|
||||
description = "A procedure macro to unify sync and async implementations depending on the features of your crate"
|
||||
documentation = "https://docs.rs/maybe-async-cfg"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"maybe",
|
||||
"async",
|
||||
"futures",
|
||||
"macros",
|
||||
"proc_macro",
|
||||
]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/nvksv/maybe-async-cfg"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[[test]]
|
||||
name = "doctests"
|
||||
required-features = ["doctests"]
|
||||
|
||||
[dependencies.proc-macro-error]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.proc-macro2]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.pulldown-cmark]
|
||||
version = "0.11"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[dependencies.quote]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "1.0"
|
||||
features = [
|
||||
"visit-mut",
|
||||
"full",
|
||||
"extra-traits",
|
||||
]
|
||||
|
||||
[dev-dependencies.async-std]
|
||||
version = "1"
|
||||
features = ["attributes"]
|
||||
|
||||
[dev-dependencies.async-trait]
|
||||
version = "0.1"
|
||||
|
||||
[dev-dependencies.tokio]
|
||||
version = "1"
|
||||
features = [
|
||||
"macros",
|
||||
"rt-multi-thread",
|
||||
]
|
||||
|
||||
[dev-dependencies.trybuild]
|
||||
version = "1"
|
||||
features = ["diff"]
|
||||
|
||||
[features]
|
||||
debug = []
|
||||
default = ["doctests"]
|
||||
doctests = ["pulldown-cmark"]
|
||||
no-debug = []
|
||||
|
||||
[badges.maintenance]
|
||||
status = "actively-developed"
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2020 Guoli Lyu
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
Vendored
+227
@@ -0,0 +1,227 @@
|
||||
<!-- cargo-sync-readme start -->
|
||||
|
||||
|
||||
# Maybe-Async-Cfg Procedure Macro
|
||||
|
||||
**Why bother writing similar code twice for blocking and async code?**
|
||||
|
||||
[](https://github.com/nvksv/maybe-async-cfg/actions)
|
||||
[](./LICENSE)
|
||||
[](https://crates.io/crates/maybe-async-cfg)
|
||||
[](https://docs.rs/maybe-async-cfg)
|
||||
|
||||
When implementing both sync and async versions of API in a crate, most API of the two version
|
||||
are almost the same except for some async/await keyword.
|
||||
|
||||
`maybe-async-cfg` help unifying async and sync implementation by **procedural macro**.
|
||||
- Write async code with normal `async`, `await`, and let `maybe_async_cfg` handles those `async`
|
||||
and `await` when you need a blocking code.
|
||||
- Add `maybe` attributes and specify feature-based conditions under which sync or async code
|
||||
should be generated.
|
||||
- Use `only_if` (or `remove_if`) to keep code in specified version if necessary.
|
||||
|
||||
The `maybe` procedural macro can be applied to the following codes:
|
||||
- use declaration
|
||||
- trait declaration
|
||||
- trait implementation
|
||||
- function definition
|
||||
- struct and enum definition
|
||||
- modules
|
||||
|
||||
**RECOMMENDATION**: Enable **resolver ver2** in your crate, which is introduced in Rust 1.51. If
|
||||
not, two crates in dependency with conflict version (one async and another blocking) can fail
|
||||
compilation.
|
||||
|
||||
|
||||
## Motivation
|
||||
|
||||
The async/await language feature alters the async world of rust. Comparing with the map/and_then
|
||||
style, now the async code really resembles sync version code.
|
||||
|
||||
In many crates, the async and sync version of crates shares the same API, but the minor
|
||||
difference that all async code must be awaited prevent the unification of async and sync code.
|
||||
In other words, we are forced to write an async and an sync implementation respectively.
|
||||
|
||||
|
||||
## Macros in Detail
|
||||
|
||||
To use `maybe-async-cfg`, we must know which block of codes is only used on sync implementation,
|
||||
and which on async. These two versions of the implementation should share the same function
|
||||
signatures except for async/await keywords.
|
||||
|
||||
Use `maybe` macro for code that is the same in both async and sync versions except for
|
||||
async/await keywords. Specify in the macro parameters the conditions (based on features) under
|
||||
which async and/or sync versions of the code should appear.
|
||||
|
||||
- attribute macro **`maybe`**
|
||||
|
||||
Offers a unified way to provide sync and async conversion on demand depending on features,
|
||||
enabled for your crate, with **async first** policy.
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
maybe_async_cfg = "0.2"
|
||||
|
||||
[features]
|
||||
use_sync = []
|
||||
use_async = []
|
||||
```
|
||||
|
||||
In this and all the following examples, we use two features. But you can use any conditions
|
||||
that are convenient for you, for example, replacing `feature="use_sync"` with
|
||||
`not(feature="use_async")` everywhere. Feel free, `maybe-async-cfg` does not analyze the
|
||||
conditions in any way, just substituting them as is.
|
||||
|
||||
Add the `maybe` attribute before all the items that need to be changed in different versions
|
||||
of the code (sync or async).
|
||||
|
||||
Want to keep async code? Specify the `async` parameter with the condition (based on
|
||||
features) when your code should be async.
|
||||
|
||||
Wanna convert async code to sync? Specify the `sync` parameter with the condition when the
|
||||
sync code should be generated.
|
||||
|
||||
```rust
|
||||
#[maybe_async_cfg::maybe(
|
||||
idents(Foo),
|
||||
sync(feature="use_sync"),
|
||||
async(feature="use_async")
|
||||
)]
|
||||
struct Struct {
|
||||
f: Foo,
|
||||
}
|
||||
```
|
||||
After conversion:
|
||||
```rust
|
||||
#[cfg(feature="use_sync")]
|
||||
struct StructSync {
|
||||
f: FooSync,
|
||||
}
|
||||
#[cfg(feature="use_async")]
|
||||
struct StructAsync {
|
||||
f: FooAsync,
|
||||
}
|
||||
```
|
||||
|
||||
- procedural macro **`content`**
|
||||
|
||||
The `content` macro allows you to specify common parameters for many `maybe` macros. Use the
|
||||
internal `default` attribute with the required parameters inside the `content` macro.
|
||||
|
||||
```rust
|
||||
maybe_async_cfg::content!{
|
||||
#![maybe_async_cfg::default(
|
||||
idents(Foo, Bar),
|
||||
)]
|
||||
|
||||
#[maybe_async_cfg::maybe(
|
||||
sync(feature="use_sync"),
|
||||
async(feature="use_async")
|
||||
)]
|
||||
struct Struct {
|
||||
f: Foo,
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(
|
||||
sync(feature="use_sync"),
|
||||
async(feature="use_async")
|
||||
)]
|
||||
async fn func(b: Bar) {
|
||||
todo!()
|
||||
}
|
||||
} // content!
|
||||
```
|
||||
After conversion:
|
||||
```rust
|
||||
#[cfg(feature="use_sync")]
|
||||
struct StructSync {
|
||||
f: FooSync,
|
||||
}
|
||||
#[cfg(feature="use_async")]
|
||||
struct StructAsync {
|
||||
f: FooAsync,
|
||||
}
|
||||
|
||||
#[cfg(feature="use_sync")]
|
||||
fn func_sync(b: BarSync) {
|
||||
todo!()
|
||||
}
|
||||
#[cfg(feature="use_async")]
|
||||
async fn func_async(b: BarAsync) {
|
||||
todo!()
|
||||
}
|
||||
```
|
||||
|
||||
## Doctests
|
||||
|
||||
When writing doctests, you can mark them as applicable only in the corresponding code version.
|
||||
To do this, specify `only_if(`_VARIANT_KEY_`)` in the doctest attributes. Then in all other
|
||||
versions of the code, this doctest will be replaced with an empty string.
|
||||
|
||||
```rust
|
||||
#[maybe_async_cfg::maybe(
|
||||
idents(Foo),
|
||||
sync(feature="use_sync"),
|
||||
async(feature="use_async")
|
||||
)]
|
||||
/// This is a structure.
|
||||
/// ```rust, only_if(sync)
|
||||
/// let s = StructSync{ f: FooSync::new() };
|
||||
/// ```
|
||||
/// ```rust, only_if(async)
|
||||
/// let s = StructAsync{ f: FooAsync::new().await };
|
||||
/// ```
|
||||
struct Struct {
|
||||
f: Foo,
|
||||
}
|
||||
```
|
||||
After conversion:
|
||||
```rust
|
||||
#[cfg(feature="use_sync")]
|
||||
/// This is a structure.
|
||||
/// ```rust, only_if(sync)
|
||||
/// let s = StructSync{ f: FooSync::new() };
|
||||
/// ```
|
||||
///
|
||||
struct StructSync {
|
||||
f: FooSync,
|
||||
}
|
||||
#[cfg(feature="use_async")]
|
||||
/// This is a structure.
|
||||
///
|
||||
/// ```rust, only_if(async)
|
||||
/// let s = StructAsync{ f: FooAsync::new().await };
|
||||
/// ```
|
||||
struct StructAsync {
|
||||
f: FooAsync,
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### rust client for services
|
||||
|
||||
When implementing rust client for any services, like awz3. The higher level API of async and
|
||||
sync version is almost the same, such as creating or deleting a bucket, retrieving an object and
|
||||
etc.
|
||||
|
||||
The example `service_client` is a proof of concept that `maybe_async_cfg` can actually free us
|
||||
from writing almost the same code for sync and async. We can toggle between a sync AWZ3 client
|
||||
and async one by `is_sync` feature gate when we add `maybe-async-cfg` to dependency.
|
||||
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
This crate is a redesigned fork of these wonderful crates:
|
||||
|
||||
- [fMeow/maybe-async-rs](https://github.com/fMeow/maybe-async-rs)
|
||||
|
||||
- [marioortizmanero/maybe-async-rs](https://github.com/marioortizmanero/maybe-async-rs)
|
||||
|
||||
Thanks!
|
||||
|
||||
|
||||
# License
|
||||
MIT
|
||||
|
||||
<!-- cargo-sync-readme end -->
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
maybe_async_cfg::content!{
|
||||
|
||||
#![maybe_async_cfg::default(
|
||||
idents(
|
||||
InnerClient,
|
||||
ServiceClient,
|
||||
)
|
||||
)]
|
||||
|
||||
type Response = String;
|
||||
type Url = &'static str;
|
||||
type Method = String;
|
||||
|
||||
/// To use `maybe-async-cfg`, we must know which block of codes is only used on
|
||||
/// blocking implementation, and which on async. These two implementation should
|
||||
/// share the same API except for async/await keywords.
|
||||
|
||||
/// This will generate two traits: `InnerClientSync` and `InnerClientAsync`
|
||||
#[maybe_async_cfg::maybe(
|
||||
sync(feature = "is_sync"),
|
||||
async(feature = "is_async", async_trait::async_trait),
|
||||
)]
|
||||
trait InnerClient {
|
||||
async fn request(method: Method, url: Url, data: String) -> Response;
|
||||
|
||||
#[inline]
|
||||
async fn post(url: Url, data: String) -> Response {
|
||||
Self::request(String::from("post"), url, data).await
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn delete(url: Url, data: String) -> Response {
|
||||
Self::request(String::from("delete"), url, data).await
|
||||
}
|
||||
}
|
||||
|
||||
/// This will generate a `ServiceClientSync`, which will implement
|
||||
/// `InnerClientSync`, and a `ServiceClientAsync`, which will implement
|
||||
/// `InnerClientAsync`.
|
||||
///
|
||||
/// If we had a single `ServiceClient` which implemented both `InnerClientSync`
|
||||
/// and `InnerClientAsync`, calls to methods like `request` would be ambiguous
|
||||
/// when both async and sync were enabled.
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(feature = "is_async"))]
|
||||
pub struct ServiceClient;
|
||||
|
||||
/// Synchronous implementation.
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(feature = "is_async"))]
|
||||
#[maybe_async_cfg::only_if(sync)]
|
||||
impl InnerClient for ServiceClient {
|
||||
fn request(method: Method, url: Url, data: String) -> Response {
|
||||
// your implementation for sync, like use `reqwest::blocking` to send
|
||||
// the request
|
||||
String::from("pretend we have a response")
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronous implementation only.
|
||||
#[maybe_async_cfg::maybe(
|
||||
sync(feature = "is_sync"),
|
||||
async(feature = "is_async", async_trait::async_trait),
|
||||
)]
|
||||
#[maybe_async_cfg::only_if(async)]
|
||||
impl InnerClient for ServiceClient {
|
||||
async fn request(method: Method, url: Url, data: String) -> Response {
|
||||
// your implementation for async, like use `reqwest::client` or
|
||||
// `async_std` to send the request
|
||||
String::from("pretend we have a response")
|
||||
}
|
||||
}
|
||||
|
||||
/// Code of upstream API are almost the same for sync and async, except for
|
||||
/// async/await keyword. This will generate the same `impl` but for both
|
||||
/// `ServiceClientAsync` and `ServiceClientSync`.
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(feature = "is_async"))]
|
||||
impl ServiceClient {
|
||||
async fn create_bucket(name: String) -> Response {
|
||||
Self::post("http://correct_url4create", String::from("my_bucket")).await
|
||||
// When `is_sync` is toggle on, this block will compiles to:
|
||||
// Self::post("http://correct_url4create", String::from("my_bucket"))
|
||||
}
|
||||
|
||||
async fn delete_bucket(name: String) -> Response {
|
||||
Self::delete("http://correct_url4delete", String::from("my_bucket")).await
|
||||
}
|
||||
// and another thousands of functions that interact with service side
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(feature = "is_async"))]
|
||||
#[maybe_async_cfg::only_if(sync)]
|
||||
fn run_sync() {
|
||||
println!("sync impl running");
|
||||
let _ = ServiceClientSync::create_bucket("bucket".to_owned());
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(feature = "is_async"))]
|
||||
#[maybe_async_cfg::only_if(async)]
|
||||
async fn run_async() {
|
||||
println!("async impl running");
|
||||
let _ = ServiceClientAsync::create_bucket("bucket".to_owned()).await;
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
#[cfg(feature = "is_sync")]
|
||||
run_sync();
|
||||
|
||||
#[cfg(feature = "is_async")]
|
||||
run_async().await;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
//! Manually expanded version of `service_client.rs` for both sync and async
|
||||
//! features.
|
||||
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
type Response = String;
|
||||
type Url = &'static str;
|
||||
type Method = String;
|
||||
|
||||
/// To use `maybe-async-cfg`, we must know which block of codes is only used on
|
||||
/// blocking implementation, and which on async. These two implementation should
|
||||
/// share the same API except for async/await keywords.
|
||||
|
||||
/// InnerClient are used to actually send request,
|
||||
/// which differ a lot between sync and async.
|
||||
#[cfg(feature = "is_sync")]
|
||||
trait InnerClientSync {
|
||||
fn request(method: Method, url: Url, data: String) -> Response;
|
||||
#[inline]
|
||||
fn post(url: Url, data: String) -> Response {
|
||||
Self::request(String::from("post"), url, data)
|
||||
}
|
||||
#[inline]
|
||||
fn delete(url: Url, data: String) -> Response {
|
||||
Self::request(String::from("delete"), url, data)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "is_async")]
|
||||
#[async_trait::async_trait]
|
||||
trait InnerClientAsync {
|
||||
async fn request(method: Method, url: Url, data: String) -> Response;
|
||||
#[inline]
|
||||
async fn post(url: Url, data: String) -> Response {
|
||||
Self::request(String::from("post"), url, data).await
|
||||
}
|
||||
#[inline]
|
||||
async fn delete(url: Url, data: String) -> Response {
|
||||
Self::request(String::from("delete"), url, data).await
|
||||
}
|
||||
}
|
||||
|
||||
/// The higher level API for end user, synchronous version.
|
||||
#[cfg(feature = "is_sync")]
|
||||
pub struct ServiceClientSync;
|
||||
|
||||
/// The higher level API for end user, asynchronous version.
|
||||
#[cfg(feature = "is_async")]
|
||||
pub struct ServiceClientAsync;
|
||||
|
||||
/// Synchronous implementation, only compiles when `is_sync` feature is on.
|
||||
#[cfg(feature = "is_sync")]
|
||||
impl InnerClientSync for ServiceClientSync {
|
||||
fn request(method: Method, url: Url, data: String) -> Response {
|
||||
// your implementation for sync, like use
|
||||
// `reqwest::blocking` to send request
|
||||
String::from("pretend we have a response")
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronous implementation, only compiles when `is_async` feature is on.
|
||||
#[cfg(feature = "is_async")]
|
||||
#[async_trait::async_trait]
|
||||
impl InnerClientAsync for ServiceClientAsync {
|
||||
async fn request(method: Method, url: Url, data: String) -> Response {
|
||||
// your implementation for async, like use `reqwest::client`
|
||||
// or `async_std` to send request
|
||||
String::from("pretend we have a response")
|
||||
}
|
||||
}
|
||||
|
||||
/// Code of upstream API are almost the same for sync and async,
|
||||
/// except for async/await keyword.
|
||||
#[cfg(feature = "is_sync")]
|
||||
impl ServiceClientSync {
|
||||
fn create_bucket(name: String) -> Response {
|
||||
Self::post("http://correct_url4create", String::from("my_bucket"))
|
||||
}
|
||||
|
||||
fn delete_bucket(name: String) -> Response {
|
||||
Self::delete("http://correct_url4delete", String::from("my_bucket"))
|
||||
}
|
||||
// and another thousands of functions that interact with service side
|
||||
}
|
||||
|
||||
#[cfg(feature = "is_async")]
|
||||
impl ServiceClientAsync {
|
||||
async fn create_bucket(name: String) -> Response {
|
||||
Self::post("http://correct_url4create", String::from("my_bucket")).await
|
||||
}
|
||||
|
||||
async fn delete_bucket(name: String) -> Response {
|
||||
Self::delete("http://correct_url4delete", String::from("my_bucket")).await
|
||||
}
|
||||
// and another thousands of functions that interact with service side
|
||||
}
|
||||
|
||||
#[cfg(feature = "is_sync")]
|
||||
fn run_sync() {
|
||||
println!("sync impl running");
|
||||
let _ = ServiceClientSync::create_bucket("bucket".to_owned());
|
||||
}
|
||||
|
||||
#[cfg(feature = "is_async")]
|
||||
async fn run_async() {
|
||||
println!("async impl running");
|
||||
let _ = ServiceClientAsync::create_bucket("bucket".to_owned()).await;
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
#[cfg(feature = "is_sync")]
|
||||
run_sync();
|
||||
|
||||
#[cfg(feature = "is_async")]
|
||||
run_async().await;
|
||||
}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
#[cfg(feature="debug")]
|
||||
pub mod inner {
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
|
||||
use crate::params::MacroParameters;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub fn dump_maybe(args: &TokenStream, input: &TokenStream) {
|
||||
println!("--vvv------------------------------------------");
|
||||
println!("maybe:");
|
||||
println!("args:");
|
||||
println!("{}", args);
|
||||
println!("input:");
|
||||
println!("{}", input);
|
||||
println!("--^^^------------------------------------------");
|
||||
println!("");
|
||||
}
|
||||
|
||||
pub fn dump_tokens(name: &str, ts: &TokenStream) {
|
||||
println!("--vvv------------------------------------------");
|
||||
println!("{}:", name);
|
||||
println!("{}", ts);
|
||||
println!("--^^^------------------------------------------");
|
||||
println!("");
|
||||
}
|
||||
|
||||
pub fn dump_tokens2(name: &str, ts: &TokenStream2) {
|
||||
println!("--vvv------------------------------------------");
|
||||
println!("{}:", name);
|
||||
println!("{}", ts);
|
||||
println!("--^^^------------------------------------------");
|
||||
println!("");
|
||||
}
|
||||
|
||||
pub fn dump_params(name: &str, params: &MacroParameters) {
|
||||
println!("--vvv------------------------------------------");
|
||||
println!("{}:", name);
|
||||
println!("{:#?}", params);
|
||||
println!("--^^^------------------------------------------");
|
||||
println!("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
macro_rules! dump_maybe {
|
||||
($args:expr, $input:expr) => {
|
||||
#[cfg(feature="debug")]
|
||||
crate::debug::inner::dump_maybe($args, $input);
|
||||
}
|
||||
}
|
||||
pub(crate) use dump_maybe;
|
||||
|
||||
macro_rules! dump_tokens {
|
||||
($name:expr, $ts:expr) => {
|
||||
#[cfg(feature="debug")]
|
||||
crate::debug::inner::dump_tokens($name, $ts);
|
||||
}
|
||||
}
|
||||
pub(crate) use dump_tokens;
|
||||
|
||||
macro_rules! dump_tokens2 {
|
||||
($name:expr, $ts:expr) => {
|
||||
#[cfg(feature="debug")]
|
||||
crate::debug::inner::dump_tokens2($name, $ts);
|
||||
}
|
||||
}
|
||||
pub(crate) use dump_tokens2;
|
||||
|
||||
macro_rules! dump_params {
|
||||
($name:expr, $params:expr) => {
|
||||
#[cfg(feature="debug")]
|
||||
crate::debug::inner::dump_params($name, $params);
|
||||
}
|
||||
}
|
||||
pub(crate) use dump_params;
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
use pulldown_cmark::{Parser, Event, Tag, TagEnd, CodeBlockKind};
|
||||
|
||||
fn as_lang_tokens(string: &str) -> impl Iterator<Item = &str> {
|
||||
// Pandoc, which Rust once used for generating documentation,
|
||||
// expects lang strings to be surrounded by `{}` and for each token
|
||||
// to be proceeded by a `.`. Since some of these lang strings are still
|
||||
// loose in the wild, we strip a pair of surrounding `{}` from the lang
|
||||
// string and a leading `.` from each token.
|
||||
|
||||
let string = string.trim();
|
||||
|
||||
let first = string.chars().next();
|
||||
let last = string.chars().last();
|
||||
|
||||
let string = if first == Some('{') && last == Some('}') {
|
||||
&string[1..string.len() - 1]
|
||||
} else {
|
||||
string
|
||||
};
|
||||
|
||||
string
|
||||
.split(|c| c == ',' || c == ' ' || c == '\t')
|
||||
.map(str::trim)
|
||||
.map(|token| token.strip_prefix('.').unwrap_or(token))
|
||||
.filter(|token| !token.is_empty())
|
||||
}
|
||||
|
||||
fn parse_lang(lang: &str) -> Option<(String, String)> {
|
||||
let our_prefix = "only_if(";
|
||||
|
||||
let mut has_our_attr = false;
|
||||
let mut key = String::new();
|
||||
let mut new_lang = String::new();
|
||||
|
||||
for token in as_lang_tokens(lang) {
|
||||
if token.starts_with(our_prefix) && token.ends_with(")") {
|
||||
has_our_attr = true;
|
||||
key = token[our_prefix.len() .. token.len()-1].to_string();
|
||||
continue;
|
||||
}
|
||||
|
||||
if !new_lang.is_empty() {
|
||||
new_lang.push_str(", ");
|
||||
}
|
||||
|
||||
new_lang.push_str(token);
|
||||
}
|
||||
|
||||
if has_our_attr {
|
||||
Some((key, new_lang))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn paste_code(new_lang: &str, code: &str, indent: Option<&str>) -> String {
|
||||
let mut res = String::new();
|
||||
|
||||
res.push_str("\n``` ");
|
||||
res.push_str(new_lang);
|
||||
res.push_str("\n");
|
||||
res.push_str(code);
|
||||
res.push_str("```");
|
||||
|
||||
if let Some(indent) = indent {
|
||||
let mut first = true;
|
||||
res = res.lines().map(|s| {
|
||||
if first {
|
||||
first = false;
|
||||
return s.to_string()
|
||||
};
|
||||
|
||||
let mut res = String::new();
|
||||
res.push('\n');
|
||||
res.push_str(indent);
|
||||
res.push_str(s);
|
||||
|
||||
res
|
||||
}).collect();
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
fn get_indent_from_content(content: &str) -> Option<String> {
|
||||
let content = content.as_bytes();
|
||||
let mut len = content.len();
|
||||
|
||||
if !(content[len-1] == b'`' && content[len-2] == b'`' && content[len-3] == b'`') {
|
||||
return None;
|
||||
}
|
||||
|
||||
len -= 3;
|
||||
|
||||
let mut rres: Vec<u8> = vec![];
|
||||
|
||||
while len >= 1 {
|
||||
let b = content[len-1];
|
||||
|
||||
if b.is_ascii_whitespace() && !(b == b'\n' || b == b'\r') {
|
||||
rres.push(b);
|
||||
len -= 1;
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let res: String = rres.into_iter().rev().map(|b| b as char).collect();
|
||||
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn process_doctests(doc: &str, processor: impl Fn(&str, &str) -> Option<Option<String>>) -> Option<String> {
|
||||
let parser = Parser::new(doc);
|
||||
|
||||
let mut prev_offset = 0usize;
|
||||
let mut level = 0usize;
|
||||
let mut block_key = String::new();
|
||||
let mut block_new_lang = String::new();
|
||||
let mut inside_code = false;
|
||||
let mut code = String::new();
|
||||
let mut has_changes: bool = false;
|
||||
|
||||
let mut new_doc = String::new();
|
||||
|
||||
for (event, ref offset) in parser.into_offset_iter() {
|
||||
match event {
|
||||
Event::Start(Tag::CodeBlock(ref kind)) => {
|
||||
level += 1;
|
||||
|
||||
if level == 1 {
|
||||
match match kind {
|
||||
CodeBlockKind::Fenced(ref lang) => {
|
||||
parse_lang(lang)
|
||||
},
|
||||
CodeBlockKind::Indented => {
|
||||
None
|
||||
}
|
||||
} {
|
||||
Some((key, new_lang)) => {
|
||||
let mut new_start = offset.start;
|
||||
let mut success = false;
|
||||
while prev_offset < new_start {
|
||||
let b = doc.as_bytes()[new_start-1];
|
||||
if !b.is_ascii_whitespace() {
|
||||
break;
|
||||
}
|
||||
|
||||
if b == b'\n' {
|
||||
new_start -= 1;
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
new_start -= 1;
|
||||
}
|
||||
if !success {
|
||||
new_start = offset.start;
|
||||
}
|
||||
|
||||
new_doc.push_str(&doc[prev_offset..new_start]);
|
||||
prev_offset = new_start;
|
||||
|
||||
block_key = key;
|
||||
block_new_lang = new_lang;
|
||||
code.clear();
|
||||
inside_code = true;
|
||||
},
|
||||
None => {},
|
||||
};
|
||||
}
|
||||
},
|
||||
Event::End(TagEnd::CodeBlock) => {
|
||||
if level == 1 && inside_code {
|
||||
let content = &doc[prev_offset..offset.end];
|
||||
prev_offset = offset.end;
|
||||
|
||||
|
||||
match processor(block_key.as_str(), code.as_str()) {
|
||||
Some(Some(new_code)) => {
|
||||
let indent = get_indent_from_content(content);
|
||||
let new_code = paste_code(block_new_lang.as_str(), new_code.as_str(), indent.as_ref().map(|s| s.as_str()));
|
||||
new_doc.push_str(new_code.as_str());
|
||||
has_changes = true;
|
||||
},
|
||||
Some(None) => {
|
||||
has_changes = true;
|
||||
},
|
||||
None => {
|
||||
new_doc.push_str(content);
|
||||
}
|
||||
}
|
||||
|
||||
inside_code = false;
|
||||
};
|
||||
level -= 1;
|
||||
},
|
||||
Event::Text(ref text) if inside_code => {
|
||||
code.push_str(&text);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
|
||||
if has_changes {
|
||||
if prev_offset < doc.len() {
|
||||
new_doc.push_str(&doc[prev_offset..doc.len()]);
|
||||
}
|
||||
|
||||
Some(new_doc)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Vendored
+712
@@ -0,0 +1,712 @@
|
||||
//!
|
||||
//! # Maybe-Async-Cfg Procedure Macro
|
||||
//!
|
||||
//! **Why bother writing similar code twice for blocking and async code?**
|
||||
//!
|
||||
//! [](https://github.com/nvksv/maybe-async-cfg/actions)
|
||||
//! [](./LICENSE)
|
||||
//! [](https://crates.io/crates/maybe-async-cfg)
|
||||
//! [](https://docs.rs/maybe-async-cfg)
|
||||
//!
|
||||
//! When implementing both sync and async versions of API in a crate, most API of the two version
|
||||
//! are almost the same except for some async/await keyword.
|
||||
//!
|
||||
//! `maybe-async-cfg` help unifying async and sync implementation by **procedural macro**.
|
||||
//! - Write async code with normal `async`, `await`, and let `maybe_async_cfg` handles those `async`
|
||||
//! and `await` when you need a blocking code.
|
||||
//! - Add `maybe` attributes and specify feature-based conditions under which sync or async code
|
||||
//! should be generated.
|
||||
//! - Use `only_if` (or `remove_if`) to keep code in specified version if necessary.
|
||||
//!
|
||||
//! The `maybe` procedural macro can be applied to the following codes:
|
||||
//! - use declaration
|
||||
//! - trait declaration
|
||||
//! - trait implementation
|
||||
//! - function definition
|
||||
//! - struct and enum definition
|
||||
//! - modules
|
||||
//!
|
||||
//! **RECOMMENDATION**: Enable **resolver ver2** in your crate, which is introduced in Rust 1.51. If
|
||||
//! not, two crates in dependency with conflict version (one async and another blocking) can fail
|
||||
//! compilation.
|
||||
//!
|
||||
//!
|
||||
//! ## Motivation
|
||||
//!
|
||||
//! The async/await language feature alters the async world of rust. Comparing with the map/and_then
|
||||
//! style, now the async code really resembles sync version code.
|
||||
//!
|
||||
//! In many crates, the async and sync version of crates shares the same API, but the minor
|
||||
//! difference that all async code must be awaited prevent the unification of async and sync code.
|
||||
//! In other words, we are forced to write an async and an sync implementation respectively.
|
||||
//!
|
||||
//!
|
||||
//! ## Macros in Detail
|
||||
//!
|
||||
//! To use `maybe-async-cfg`, we must know which block of codes is only used on sync implementation,
|
||||
//! and which on async. These two versions of the implementation should share the same function
|
||||
//! signatures except for async/await keywords.
|
||||
//!
|
||||
//! Use `maybe` macro for code that is the same in both async and sync versions except for
|
||||
//! async/await keywords. Specify in the macro parameters the conditions (based on features) under
|
||||
//! which async and/or sync versions of the code should appear.
|
||||
//!
|
||||
//! - attribute macro **`maybe`**
|
||||
//!
|
||||
//! Offers a unified way to provide sync and async conversion on demand depending on features,
|
||||
//! enabled for your crate, with **async first** policy.
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! maybe_async_cfg = "0.2"
|
||||
//!
|
||||
//! [features]
|
||||
//! use_sync = []
|
||||
//! use_async = []
|
||||
//! ```
|
||||
//!
|
||||
//! In this and all the following examples, we use two features. But you can use any conditions
|
||||
//! that are convenient for you, for example, replacing `feature="use_sync"` with
|
||||
//! `not(feature="use_async")` everywhere. Feel free, `maybe-async-cfg` does not analyze the
|
||||
//! conditions in any way, just substituting them as is.
|
||||
//!
|
||||
//! Add the `maybe` attribute before all the items that need to be changed in different versions
|
||||
//! of the code (sync or async).
|
||||
//!
|
||||
//! Want to keep async code? Specify the `async` parameter with the condition (based on
|
||||
//! features) when your code should be async.
|
||||
//!
|
||||
//! Wanna convert async code to sync? Specify the `sync` parameter with the condition when the
|
||||
//! sync code should be generated.
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[maybe_async_cfg::maybe(
|
||||
//! idents(Foo),
|
||||
//! sync(feature="use_sync"),
|
||||
//! async(feature="use_async")
|
||||
//! )]
|
||||
//! struct Struct {
|
||||
//! f: Foo,
|
||||
//! }
|
||||
//! ```
|
||||
//! After conversion:
|
||||
//! ```rust
|
||||
//! #[cfg(feature="use_sync")]
|
||||
//! struct StructSync {
|
||||
//! f: FooSync,
|
||||
//! }
|
||||
//! #[cfg(feature="use_async")]
|
||||
//! struct StructAsync {
|
||||
//! f: FooAsync,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! - procedural macro **`content`**
|
||||
//!
|
||||
//! The `content` macro allows you to specify common parameters for many `maybe` macros. Use the
|
||||
//! internal `default` attribute with the required parameters inside the `content` macro.
|
||||
//!
|
||||
//! ```rust
|
||||
//! maybe_async_cfg::content!{
|
||||
//! #![maybe_async_cfg::default(
|
||||
//! idents(Foo, Bar),
|
||||
//! )]
|
||||
//!
|
||||
//! #[maybe_async_cfg::maybe(
|
||||
//! sync(feature="use_sync"),
|
||||
//! async(feature="use_async")
|
||||
//! )]
|
||||
//! struct Struct {
|
||||
//! f: Foo,
|
||||
//! }
|
||||
//!
|
||||
//! #[maybe_async_cfg::maybe(
|
||||
//! sync(feature="use_sync"),
|
||||
//! async(feature="use_async")
|
||||
//! )]
|
||||
//! async fn func(b: Bar) {
|
||||
//! todo!()
|
||||
//! }
|
||||
//! } // content!
|
||||
//! ```
|
||||
//! After conversion:
|
||||
//! ```rust
|
||||
//! #[cfg(feature="use_sync")]
|
||||
//! struct StructSync {
|
||||
//! f: FooSync,
|
||||
//! }
|
||||
//! #[cfg(feature="use_async")]
|
||||
//! struct StructAsync {
|
||||
//! f: FooAsync,
|
||||
//! }
|
||||
//!
|
||||
//! #[cfg(feature="use_sync")]
|
||||
//! fn func_sync(b: BarSync) {
|
||||
//! todo!()
|
||||
//! }
|
||||
//! #[cfg(feature="use_async")]
|
||||
//! async fn func_async(b: BarAsync) {
|
||||
//! todo!()
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Doctests
|
||||
//!
|
||||
//! When writing doctests, you can mark them as applicable only in the corresponding code version.
|
||||
//! To do this, specify `only_if(`_VARIANT_KEY_`)` in the doctest attributes. Then in all other
|
||||
//! versions of the code, this doctest will be replaced with an empty string.
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[maybe_async_cfg::maybe(
|
||||
//! idents(Foo),
|
||||
//! sync(feature="use_sync"),
|
||||
//! async(feature="use_async")
|
||||
//! )]
|
||||
//! /// This is a structure.
|
||||
//! /// ```rust, only_if(sync)
|
||||
//! /// let s = StructSync{ f: FooSync::new() };
|
||||
//! /// ```
|
||||
//! /// ```rust, only_if(async)
|
||||
//! /// let s = StructAsync{ f: FooAsync::new().await };
|
||||
//! /// ```
|
||||
//! struct Struct {
|
||||
//! f: Foo,
|
||||
//! }
|
||||
//! ```
|
||||
//! After conversion:
|
||||
//! ```rust
|
||||
//! #[cfg(feature="use_sync")]
|
||||
//! /// This is a structure.
|
||||
//! /// ```rust, only_if(sync)
|
||||
//! /// let s = StructSync{ f: FooSync::new() };
|
||||
//! /// ```
|
||||
//! ///
|
||||
//! struct StructSync {
|
||||
//! f: FooSync,
|
||||
//! }
|
||||
//! #[cfg(feature="use_async")]
|
||||
//! /// This is a structure.
|
||||
//! ///
|
||||
//! /// ```rust, only_if(async)
|
||||
//! /// let s = StructAsync{ f: FooAsync::new().await };
|
||||
//! /// ```
|
||||
//! struct StructAsync {
|
||||
//! f: FooAsync,
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Examples
|
||||
//!
|
||||
//! ### rust client for services
|
||||
//!
|
||||
//! When implementing rust client for any services, like awz3. The higher level API of async and
|
||||
//! sync version is almost the same, such as creating or deleting a bucket, retrieving an object and
|
||||
//! etc.
|
||||
//!
|
||||
//! The example `service_client` is a proof of concept that `maybe_async_cfg` can actually free us
|
||||
//! from writing almost the same code for sync and async. We can toggle between a sync AWZ3 client
|
||||
//! and async one by `is_sync` feature gate when we add `maybe-async-cfg` to dependency.
|
||||
//!
|
||||
//!
|
||||
//! ## Acknowledgements
|
||||
//!
|
||||
//! This crate is a redesigned fork of these wonderful crates:
|
||||
//!
|
||||
//! - [fMeow/maybe-async-rs](https://github.com/fMeow/maybe-async-rs)
|
||||
//!
|
||||
//! - [marioortizmanero/maybe-async-rs](https://github.com/marioortizmanero/maybe-async-rs)
|
||||
//!
|
||||
//! Thanks!
|
||||
//!
|
||||
//!
|
||||
//! # License
|
||||
//! MIT
|
||||
#![deny(missing_docs)]
|
||||
#![deny(rustdoc::missing_crate_level_docs)]
|
||||
|
||||
// note: the `rustdoc::missing_doc_code_examples` lint is unstable
|
||||
//#![deny(rustdoc::missing_doc_code_examples)]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro_error::proc_macro_error;
|
||||
|
||||
mod macros;
|
||||
mod params;
|
||||
mod utils;
|
||||
mod visit_ext;
|
||||
mod visitor_async;
|
||||
mod visitor_content;
|
||||
|
||||
#[cfg(feature = "doctests")]
|
||||
mod doctests;
|
||||
|
||||
mod debug;
|
||||
|
||||
const DEFAULT_CRATE_NAME: &'static str = "maybe_async_cfg";
|
||||
const MACRO_MAYBE_NAME: &'static str = "maybe";
|
||||
const MACRO_ONLY_IF_NAME: &'static str = "only_if";
|
||||
const MACRO_REMOVE_IF_NAME: &'static str = "remove_if";
|
||||
const MACRO_NOOP_NAME: &'static str = "noop";
|
||||
const MACRO_REMOVE_NAME: &'static str = "remove";
|
||||
const MACRO_DEFAULT_NAME: &'static str = "default";
|
||||
|
||||
const STANDARD_MACROS: &'static [&'static str] = &[
|
||||
"dbg",
|
||||
"print",
|
||||
"println",
|
||||
"assert",
|
||||
"assert_eq",
|
||||
"assert_ne",
|
||||
];
|
||||
|
||||
/// Marks the code that can be presented in several versions.
|
||||
///
|
||||
/// ### The `maybe` macro has the following parameters:
|
||||
///
|
||||
/// - `disable`
|
||||
///
|
||||
/// The macro with `disable` parameter will do nothing, like `noop`. Use it to write and debug
|
||||
/// initial async code.
|
||||
///
|
||||
/// - `prefix`
|
||||
///
|
||||
/// The name of `maybe-async-cfg` crate. If not set, `"maybe_async_cfg"` will be used.
|
||||
///
|
||||
/// - `sync`, `async`
|
||||
///
|
||||
/// Defines versions of the code: the item to which the attribute `maybe` refers will be
|
||||
/// replaced with multiple copies (one for each version), which will be modified according to
|
||||
/// the version kind and its parameters.
|
||||
///
|
||||
/// For the `sync` version, the item will be converted from async to sync code by deleting
|
||||
/// the `async` and `await` keywords. The types `Future<Output=XXX>` will also be replaced with just
|
||||
/// `XXX`. For the `async` version, the item will be left async.
|
||||
///
|
||||
/// In any case, the item will be converted according to all the parameters described below. For
|
||||
/// functions, structs/enums and traits, the name will be changed as if it is mentioned in the
|
||||
/// `idents` list (if it is not explicitly specified there and if `keep_self` is not present).
|
||||
///
|
||||
/// - All other parameters will be passed to all versions (with merging).
|
||||
///
|
||||
/// Therefore, those parts of the version parameters that match in all versions can be specified
|
||||
/// here. For example, this is the expected behavior for the `idents` list.
|
||||
///
|
||||
/// ### Every version has the following parameters:
|
||||
///
|
||||
/// - `disable`
|
||||
///
|
||||
/// Ignore this version entirely.
|
||||
///
|
||||
/// - `key`
|
||||
///
|
||||
/// Defines unique name of the version to use it in `only_if`/`remove_if` conditions. If
|
||||
/// omitted, `sync`/`async` will be used.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[maybe_async_cfg::maybe(
|
||||
/// sync(key="foo", feature="use_sync"),
|
||||
/// async(key="bar", feature="use_async"),
|
||||
/// )]
|
||||
/// struct Struct {
|
||||
/// f: usize,
|
||||
///
|
||||
/// // This field will only be present in sync version
|
||||
/// #[maybe_async_cfg::only_if(key="foo")]
|
||||
/// sync_only_field: bool,
|
||||
/// }
|
||||
/// ```
|
||||
/// After conversion:
|
||||
/// ```rust
|
||||
/// #[cfg(feature="use_sync")]
|
||||
/// struct StructSync {
|
||||
/// f: usize,
|
||||
/// sync_only_field: bool,
|
||||
/// }
|
||||
/// #[cfg(feature="use_async")]
|
||||
/// struct StructAsync {
|
||||
/// f: usize,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - `cfg`
|
||||
///
|
||||
/// Defines the condition (based on features), under which the current version should appear.
|
||||
///
|
||||
/// Note: conditions like `feature = "..."`, `not(...)`, `all(...)`, `any(...)` will be
|
||||
/// processed correctly, even if the `cfg(...)` was omitted.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[maybe_async_cfg::maybe(
|
||||
/// sync(cfg(feature="use_sync")),
|
||||
/// async(feature="use_async")
|
||||
/// )]
|
||||
/// struct Struct {
|
||||
/// f: Foo,
|
||||
/// }
|
||||
/// ```
|
||||
/// After conversion:
|
||||
/// ```rust
|
||||
/// #[cfg(feature="use_sync")]
|
||||
/// struct StructSync {
|
||||
/// f: Foo,
|
||||
/// }
|
||||
/// #[cfg(feature="use_async")]
|
||||
/// struct StructAsync {
|
||||
/// f: Foo,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - `idents`
|
||||
///
|
||||
/// Defines a list of identifiers that should be renamed depending on the version of the code.
|
||||
///
|
||||
/// Each identifier can have the following clarifying parameters:
|
||||
///
|
||||
/// - `snake`, `fn`, `mod`
|
||||
///
|
||||
/// means that this is the snake-case name of the function or module and it should be
|
||||
/// converted by adding the suffixes `"_sync"`/`"_async"` (otherwise, the suffixes
|
||||
/// `"Sync"`/`"Async"` will be used).
|
||||
///
|
||||
/// - `use`
|
||||
///
|
||||
/// in `use` lists, using this identifier will result in renaming via the `as` expression,
|
||||
/// rather than a simple replacement as is. In other cases, a simple replacement will be used.
|
||||
///
|
||||
/// - `keep`
|
||||
///
|
||||
/// this identifier will not be converted anywhere
|
||||
///
|
||||
/// - `sync`, `async`
|
||||
///
|
||||
/// specifies the name that will be used in the corresponding version of the code. Overrides
|
||||
/// the standard scheme of suffixes used by default. If the parameter value is omitted,
|
||||
/// the identifier will not be renamed in this case.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[maybe_async_cfg::maybe(
|
||||
/// idents(
|
||||
/// Foo,
|
||||
/// Bar,
|
||||
/// baz(fn),
|
||||
/// Qux(use),
|
||||
/// waldo(sync, async="async_waldo"),
|
||||
/// xyzzy(fn, use, sync="xizzy_the_sync_func"),
|
||||
/// ),
|
||||
/// sync(feature="use_sync"),
|
||||
/// async(feature="use_async"),
|
||||
/// )]
|
||||
/// async fn func() {
|
||||
/// struct Foo {}
|
||||
/// use extcrate::{
|
||||
/// Bar,
|
||||
/// baz,
|
||||
/// Qux,
|
||||
/// waldo::{
|
||||
/// plugh,
|
||||
/// xyzzy
|
||||
/// }
|
||||
/// };
|
||||
/// let _ = baz( Foo {}, Bar::new() ).await;
|
||||
/// let _ = xizzy( Qux::flob(b).await );
|
||||
/// }
|
||||
/// ```
|
||||
/// After conversion:
|
||||
/// ```rust
|
||||
/// #[cfg(feature="use_sync")]
|
||||
/// fn func_sync() {
|
||||
/// struct FooSync {}
|
||||
/// use extcrate::{
|
||||
/// BarSync,
|
||||
/// baz_sync,
|
||||
/// Qux as QuxSync,
|
||||
/// waldo::{
|
||||
/// plugh,
|
||||
/// xyzzy as xizzy_the_sync_func
|
||||
/// }
|
||||
/// };
|
||||
/// let _ = baz_sync( FooSync {}, BarSync::new() );
|
||||
/// let _ = xizzy_the_sync_func( QuxSync::flob() );
|
||||
/// }
|
||||
/// #[cfg(feature="use_async")]
|
||||
/// async fn func_async() {
|
||||
/// struct FooAsync {}
|
||||
/// use extcrate::{
|
||||
/// BarAsync,
|
||||
/// baz_async,
|
||||
/// Qux as QuxAsync,
|
||||
/// async_waldo::{
|
||||
/// plugh,
|
||||
/// xyzzy as xyzzy_async
|
||||
/// }
|
||||
/// };
|
||||
/// let _ = baz_async( FooAsync {}, BarAsync::new() ).await;
|
||||
/// let _ = xyzzy_async( QuxAsync::flob().await );
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - `keep_self`
|
||||
///
|
||||
/// Do not change name of item to which attribute `maybe` refers.
|
||||
///
|
||||
/// - `self`
|
||||
///
|
||||
/// Defines the name that will be assigned to the item in this variant.
|
||||
///
|
||||
/// - `send`
|
||||
///
|
||||
/// If `send = "Send"` or `send = "true"` is present, the attribute
|
||||
/// `#[async_trait::async_trait]` will be added before the async code. If `send = "?Send"` or
|
||||
/// `send = "false"` then `#[async_trait::async_trait(?Send)]` will be added.
|
||||
///
|
||||
/// - `drop_attrs`
|
||||
///
|
||||
/// Remove any attributes with specified names.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[maybe_async_cfg::maybe(
|
||||
/// sync(feature="use_sync", drop_attrs(attr)),
|
||||
/// async(feature="use_async"),
|
||||
/// )]
|
||||
/// struct Struct {
|
||||
/// f: usize,
|
||||
///
|
||||
/// // This attribute will be removed in sync version
|
||||
/// #[attr(param)]
|
||||
/// field1: bool,
|
||||
/// }
|
||||
/// ```
|
||||
/// After conversion:
|
||||
/// ```rust
|
||||
/// #[cfg(feature="use_sync")]
|
||||
/// struct StructSync {
|
||||
/// f: usize,
|
||||
/// field1: bool,
|
||||
/// }
|
||||
/// #[cfg(feature="use_async")]
|
||||
/// struct StructAsync {
|
||||
/// f: usize,
|
||||
/// #[attr(param)]
|
||||
/// field1: bool,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - `replace_features`
|
||||
///
|
||||
/// Replace one feature name with another.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[maybe_async_cfg::maybe(
|
||||
/// sync(feature="use_sync", replace_feature("secure", "secure_sync")),
|
||||
/// async(feature="use_async"),
|
||||
/// )]
|
||||
/// struct Struct {
|
||||
/// f: usize,
|
||||
/// // In sync version "secure" feature will be replaced with "secure_sync" feature
|
||||
/// #[cfg(feature="secure")]
|
||||
/// field: bool,
|
||||
/// }
|
||||
/// ```
|
||||
/// After conversion:
|
||||
/// ```rust
|
||||
/// #[cfg(feature="use_sync")]
|
||||
/// struct StructSync {
|
||||
/// f: usize,
|
||||
/// #[cfg(feature="secure_sync")]
|
||||
/// field: bool,
|
||||
/// }
|
||||
/// #[cfg(feature="use_async")]
|
||||
/// struct StructAsync {
|
||||
/// f: usize,
|
||||
/// #[cfg(feature="secure")]
|
||||
/// field: bool,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - `inner`, `outer`
|
||||
///
|
||||
/// Adds some attributes to the generated code. Inner attributes will appear below attribute
|
||||
/// `#[cfg(...)]`, outer attributes will appear above it.
|
||||
///
|
||||
/// Note: if the version parameter is not parsed as a parameter of some other type, it will be
|
||||
/// interpreted as an inner attribute.
|
||||
///
|
||||
/// Useful for testing: just write `test` in version parameters.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[maybe_async_cfg::maybe(
|
||||
/// sync(feature="secure_sync", test, "resource(path = \"/foo/bar\")", outer(xizzy)),
|
||||
/// async(feature="secure_sync", inner(baz(qux), async_attributes::test)),
|
||||
/// )]
|
||||
/// async fn test_func() {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// ```
|
||||
/// After conversion:
|
||||
/// ```rust
|
||||
/// #[xizzy]
|
||||
/// #[cfg(feature="use_sync")]
|
||||
/// #[test]
|
||||
/// #[resource(path = "/foo/bar")]
|
||||
/// fn test_func_sync() {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// #[cfg(feature="use_async")]
|
||||
/// #[baz(qux)]
|
||||
/// #[async_attributes::test]
|
||||
/// async fn test_func_async() {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - In other cases, the following rules apply:
|
||||
///
|
||||
/// - name-value pairs (`xxx = "yyy"`) with a name other than `key`, `prefix`, `send` and
|
||||
/// `feature` will produce an error.
|
||||
///
|
||||
/// - `feature = "..."`, `not(...)`, `all(...)`, `any(...)` will be interpreted as condition for
|
||||
/// current version (as wrapped in `cfg(...)`).
|
||||
///
|
||||
/// - all another parameters will be interpreted as inner attribute for current version (as
|
||||
/// wrapped in `inner(...)`).
|
||||
///
|
||||
/// ### Formal syntax
|
||||
///
|
||||
/// > _ParametersList_ :\
|
||||
/// > _Parameter_ (`,` _Parameter_)<sup>\*</sup>
|
||||
/// >
|
||||
/// > _Parameter_ :\
|
||||
/// > `disable`\
|
||||
/// > | `keep_self`\
|
||||
/// > | `prefix` `=` _STRING_LITERAL_\
|
||||
/// > | (`sync` | `async`) `(` _VersionParametersList_ `)`\
|
||||
/// > | `idents` `(` _IdentsList_ `)`\
|
||||
/// >
|
||||
/// > _VersionParametersList_ :\
|
||||
/// > _VersionParameter_ (`,` _VersionParameter_)<sup>\*</sup>
|
||||
/// >
|
||||
/// > _VersionParameter_ :\
|
||||
/// > `disable`\
|
||||
/// > | `keep_self`\
|
||||
/// > | `key` `=` _STRING_LITERAL_\
|
||||
/// > | `feature` `=` _STRING_LITERAL_\
|
||||
/// > | `self` `=` _STRING_LITERAL_\
|
||||
/// > | `send` `=` (`""` | `"Send"` | `"true"` | `"?Send"` | `"false"`)\
|
||||
/// > | (`cfg` | `any` | `all` | `not`) `(` _ANY_CFG_CONDITION_ `)`\
|
||||
/// > | `idents` `(` _IdentsList_ `)`\
|
||||
/// > | (`outer` | `inner`) `(` _AttributesList_ `)`\
|
||||
/// > | `replace_feature` `(` _STRING_LITERAL_ `,` _STRING_LITERAL_ `)`\
|
||||
/// > | `drop_attrs` `(` _IdentifiersList_ `)`\
|
||||
/// > | _Attribute_
|
||||
/// >
|
||||
/// > _Path_ :\
|
||||
/// > _IDENTIFIER_ (`::` _IDENTIFIER_)<sup>\+</sup>
|
||||
/// >
|
||||
/// > _IdentifiersList_ :\
|
||||
/// > _IDENTIFIER_ (`,` _IDENTIFIER_)<sup>\*</sup>
|
||||
/// >
|
||||
/// > _IdentsList_ :\
|
||||
/// > _Ident_ (`,` _Ident_)<sup>\*</sup>
|
||||
/// >
|
||||
/// > _Ident_ :\
|
||||
/// > _IDENTIFIER_ (`(` _IdentParametersList_ `)`)<sup>\?</sup>
|
||||
/// >
|
||||
/// > _IdentParametersList_ :\
|
||||
/// > _IdentParameter_ (`,` _IdentParameter_)<sup>\*</sup>
|
||||
/// >
|
||||
/// > _IdentParameter_ :\
|
||||
/// > `keep`\
|
||||
/// > | `use`\
|
||||
/// > | (`snake` | `fn` | `mod` )\
|
||||
/// > | `use`\
|
||||
/// > | (`sync` | `async` | _IDENTIFIER_) (`=` _STRING_LITERAL_)<sup>\?</sup>
|
||||
/// >
|
||||
/// > _AttributesList_ :\
|
||||
/// > _Attribute_ (`,` _Attribute_)<sup>\*</sup>
|
||||
/// >
|
||||
/// > _Attribute_ :\
|
||||
/// > (_IDENTIFIER_ | _Path_) (`(` _ANY_VALID_ARGS_ `)`)<sup>\?</sup>\
|
||||
/// > | _STRING_LITERAL_
|
||||
#[proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn maybe(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
macros::maybe(args, input)
|
||||
}
|
||||
|
||||
/// Marks conditional content that should only be used in the specified version of the code.
|
||||
#[proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn only_if(_: TokenStream, body: TokenStream) -> TokenStream {
|
||||
body
|
||||
}
|
||||
|
||||
/// Marks conditional content that should be used in all versions of the code except the specified
|
||||
/// one.
|
||||
#[proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn remove_if(_: TokenStream, body: TokenStream) -> TokenStream {
|
||||
body
|
||||
}
|
||||
|
||||
/// Does nothing (leaves content intact).
|
||||
#[proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn noop(_: TokenStream, body: TokenStream) -> TokenStream {
|
||||
body
|
||||
}
|
||||
|
||||
/// Removes marked content.
|
||||
#[proc_macro_error]
|
||||
#[proc_macro_attribute]
|
||||
pub fn remove(_: TokenStream, _: TokenStream) -> TokenStream {
|
||||
TokenStream::new()
|
||||
}
|
||||
|
||||
/// A wrapper for code with common `maybe` parameters
|
||||
///
|
||||
/// The `content` macro allows you to specify common parameters for many `maybe` macros. Use the
|
||||
/// internal `default` attribute with the required parameters inside the `content` macro.
|
||||
///
|
||||
/// ```rust
|
||||
/// maybe_async_cfg::content!{
|
||||
/// #![maybe_async_cfg::default(
|
||||
/// idents(Foo, Bar),
|
||||
/// )]
|
||||
///
|
||||
/// #[maybe_async_cfg::maybe(sync(feature="use_sync"), async(feature="use_async"))]
|
||||
/// struct Struct {
|
||||
/// f: Foo,
|
||||
/// }
|
||||
///
|
||||
/// #[maybe_async_cfg::maybe(sync(feature="use_sync"), async(feature="use_async"))]
|
||||
/// async fn func(b: Bar) {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// } // content!
|
||||
/// ```
|
||||
/// After conversion:
|
||||
/// ```rust
|
||||
/// #[cfg(feature="use_sync")]
|
||||
/// struct StructSync {
|
||||
/// f: FooSync,
|
||||
/// }
|
||||
/// #[cfg(feature="use_async")]
|
||||
/// struct StructAsync {
|
||||
/// f: FooAsync,
|
||||
/// }
|
||||
///
|
||||
/// #[cfg(feature="use_sync")]
|
||||
/// fn func_sync(b: BarSync) {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// #[cfg(feature="use_async")]
|
||||
/// async fn func_async(b: BarAsync) {
|
||||
/// todo!()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
#[proc_macro_error]
|
||||
#[proc_macro]
|
||||
pub fn content(body: TokenStream) -> TokenStream {
|
||||
macros::content(body)
|
||||
}
|
||||
Vendored
+177
@@ -0,0 +1,177 @@
|
||||
use proc_macro_error::abort;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use std::iter::FromIterator;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use syn::{
|
||||
parse_macro_input, spanned::Spanned, visit_mut::VisitMut,
|
||||
};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
use crate::{
|
||||
MACRO_MAYBE_NAME,
|
||||
params::{ConvertMode, MacroParameters},
|
||||
utils::{unwrap_or_error},
|
||||
visit_ext::Visitor,
|
||||
visitor_async::{
|
||||
AsyncAwaitVisitor, remove_asyncness_on_trait, remove_asyncness_on_impl,
|
||||
remove_asyncness_on_fn},
|
||||
visitor_content::ContentVisitor,
|
||||
debug::*
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub fn maybe(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
dump_maybe!(&args, &input);
|
||||
|
||||
let params = unwrap_or_error!(MacroParameters::from_tokens(args));
|
||||
dump_params!("maybe params", ¶ms);
|
||||
|
||||
if params.disable_get() {
|
||||
return input;
|
||||
}
|
||||
|
||||
if let Some(convert_mode) = params.mode_get() {
|
||||
return convert(params, input, convert_mode)
|
||||
}
|
||||
|
||||
|
||||
let mut tokens = TokenStream::new();
|
||||
|
||||
for version in ¶ms.versions {
|
||||
let mut ts = TokenStream2::new();
|
||||
|
||||
match version.kind {
|
||||
ConvertMode::IntoAsync | ConvertMode::IntoSync => {
|
||||
let _ = unwrap_or_error!(version
|
||||
.params
|
||||
.extend_tokenstream2_with_cfg_outer_attrs(&mut ts));
|
||||
let name = params.make_self_path(MACRO_MAYBE_NAME);
|
||||
let args = version.params.to_tokens(Some(version.kind));
|
||||
ts.extend(quote!(#[#name(#args)]));
|
||||
|
||||
let _ =
|
||||
unwrap_or_error!(version.params.extend_tokenstream2_with_inner_attrs(&mut ts));
|
||||
}
|
||||
}
|
||||
|
||||
let ts: TokenStream = ts.into();
|
||||
tokens.extend(ts);
|
||||
tokens.extend(input.clone());
|
||||
}
|
||||
|
||||
dump_tokens!("maybe after", &tokens);
|
||||
|
||||
tokens
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub fn convert(mut params: MacroParameters, input: TokenStream, convert_mode: ConvertMode) -> TokenStream {
|
||||
dump_tokens!("convert before", &input);
|
||||
|
||||
let mut file = parse_macro_input!(input as syn::File);
|
||||
for item in &mut file.items {
|
||||
match item {
|
||||
syn::Item::Impl(item) => convert_impl(&mut params, item, convert_mode),
|
||||
syn::Item::Struct(item) => convert_struct(&mut params, item, convert_mode),
|
||||
syn::Item::Enum(item) => convert_enum(&mut params, item, convert_mode),
|
||||
syn::Item::Trait(item) => convert_trait(&mut params, item, convert_mode),
|
||||
syn::Item::Fn(item) => convert_fn(&mut params, item, convert_mode),
|
||||
syn::Item::Use(item) => convert_use(&mut params, item, convert_mode),
|
||||
syn::Item::Mod(item) => convert_mod(&mut params, item, convert_mode),
|
||||
_ => {
|
||||
abort!(item.span(), "Allowed impl, struct, enum, trait, fn or use items only");
|
||||
}
|
||||
}
|
||||
}
|
||||
let ts = quote!(#file);
|
||||
|
||||
dump_tokens2!("convert after", &ts);
|
||||
ts.into()
|
||||
}
|
||||
|
||||
fn convert_impl(params: &mut MacroParameters, item: &mut syn::ItemImpl, convert_mode: ConvertMode) {
|
||||
match &mut *item.self_ty {
|
||||
syn::Type::Path(syn::TypePath { path, .. }) => {
|
||||
if let Some(last) = path.segments.last_mut() {
|
||||
params.original_self_name_set(last.ident.to_string(), false);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if !params.recursive_asyncness_removal_get() {
|
||||
remove_asyncness_on_impl( item, convert_mode, params.send_get() );
|
||||
}
|
||||
|
||||
let mut visitor = Visitor::new(AsyncAwaitVisitor::new(params, convert_mode));
|
||||
visitor.visit_item_impl_mut(item)
|
||||
}
|
||||
|
||||
fn convert_struct(params: &mut MacroParameters, item: &mut syn::ItemStruct, convert_mode: ConvertMode) {
|
||||
params.original_self_name_set(item.ident.to_string(), false);
|
||||
|
||||
let mut visitor = Visitor::new(AsyncAwaitVisitor::new(params, convert_mode));
|
||||
visitor.visit_item_struct_mut(item)
|
||||
}
|
||||
|
||||
fn convert_enum(params: &mut MacroParameters, item: &mut syn::ItemEnum, convert_mode: ConvertMode) {
|
||||
params.original_self_name_set(item.ident.to_string(), false);
|
||||
|
||||
let mut visitor = Visitor::new(AsyncAwaitVisitor::new(params, convert_mode));
|
||||
visitor.visit_item_enum_mut(item)
|
||||
}
|
||||
|
||||
fn convert_trait(params: &mut MacroParameters, item: &mut syn::ItemTrait, convert_mode: ConvertMode) {
|
||||
params.original_self_name_set(item.ident.to_string(), false);
|
||||
|
||||
if !params.recursive_asyncness_removal_get() {
|
||||
remove_asyncness_on_trait(item, convert_mode);
|
||||
}
|
||||
|
||||
let mut visitor = Visitor::new(AsyncAwaitVisitor::new(params, convert_mode));
|
||||
visitor.visit_item_trait_mut(item)
|
||||
}
|
||||
|
||||
fn convert_fn(params: &mut MacroParameters, item: &mut syn::ItemFn, convert_mode: ConvertMode) {
|
||||
params.original_self_name_set(item.sig.ident.to_string(), true);
|
||||
|
||||
if !params.recursive_asyncness_removal_get() {
|
||||
remove_asyncness_on_fn(item, convert_mode);
|
||||
}
|
||||
|
||||
let mut visitor = Visitor::new(AsyncAwaitVisitor::new(params, convert_mode));
|
||||
visitor.visit_item_fn_mut(item)
|
||||
}
|
||||
|
||||
fn convert_use(params: &mut MacroParameters, item: &mut syn::ItemUse, convert_mode: ConvertMode) {
|
||||
let mut visitor = Visitor::new(AsyncAwaitVisitor::new(params, convert_mode));
|
||||
visitor.visit_item_use_mut(item)
|
||||
}
|
||||
|
||||
fn convert_mod(params: &mut MacroParameters, item: &mut syn::ItemMod, convert_mode: ConvertMode) {
|
||||
params.original_self_name_set(item.ident.to_string(), true);
|
||||
|
||||
let mut visitor = Visitor::new(AsyncAwaitVisitor::new(params, convert_mode));
|
||||
visitor.visit_item_mod_mut(item)
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub fn content(body: TokenStream) -> TokenStream {
|
||||
dump_tokens!("content before", &body);
|
||||
|
||||
let mut visitor = Visitor::new(ContentVisitor::new());
|
||||
let ts: TokenStream = visitor.process(body.into()).into();
|
||||
|
||||
dump_tokens!("content after", &ts);
|
||||
ts
|
||||
}
|
||||
Vendored
+1001
File diff suppressed because it is too large.
Load diff
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use syn::{
|
||||
parenthesized,
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
token::Comma,
|
||||
Expr,
|
||||
Ident,
|
||||
Meta,
|
||||
NestedMeta,
|
||||
Token,
|
||||
};
|
||||
|
||||
use quote::ToTokens;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
macro_rules! unwrap_or_error {
|
||||
($res:expr) => {
|
||||
match $res {
|
||||
Ok(p) => p,
|
||||
Err(err) => {
|
||||
abort!(err)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use unwrap_or_error;
|
||||
|
||||
// macro_rules! unwrap2_or_error {
|
||||
// ($res:expr) => {
|
||||
// match $res {
|
||||
// Ok(p) => p,
|
||||
// Err(err) => {
|
||||
// return proc_macro2::TokenStream::from(syn::Error::from(err).to_compile_error());
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
// pub(crate) use unwrap2_or_error;
|
||||
|
||||
macro_rules! set_error_and_return {
|
||||
($err:expr) => {{
|
||||
emit_error!($err);
|
||||
return;
|
||||
}};
|
||||
($err:expr, $ret:expr) => {{
|
||||
emit_error!($err);
|
||||
return $ret;
|
||||
}};
|
||||
}
|
||||
pub(crate) use set_error_and_return;
|
||||
|
||||
macro_rules! unwrap_or_set_error_and_return {
|
||||
($res:expr) => {
|
||||
match $res {
|
||||
Ok(p) => p,
|
||||
Err(err) => set_error_and_return!(err),
|
||||
}
|
||||
};
|
||||
($res:expr, $ret:expr) => {
|
||||
match $res {
|
||||
Ok(p) => p,
|
||||
Err(err) => set_error_and_return!(err, $ret),
|
||||
}
|
||||
};
|
||||
}
|
||||
pub(crate) use unwrap_or_set_error_and_return;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub(crate) fn make_path(name: &str) -> syn::Path {
|
||||
let mut segments = Punctuated::<syn::PathSegment, syn::token::Colon2>::new();
|
||||
segments.push_value(syn::PathSegment {
|
||||
ident: Ident::new(name, Span::call_site()),
|
||||
arguments: syn::PathArguments::None,
|
||||
});
|
||||
|
||||
syn::Path {
|
||||
leading_colon: None,
|
||||
segments,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_nestedmeta_namevalue(name: &str, value: &str) -> syn::NestedMeta {
|
||||
NestedMeta::Meta(Meta::NameValue(syn::MetaNameValue {
|
||||
path: make_path(name),
|
||||
eq_token: Token),
|
||||
lit: syn::Lit::Str(syn::LitStr::new(value, Span::call_site())),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn make_nestedmeta_list(
|
||||
name: &str,
|
||||
nested: Punctuated<NestedMeta, syn::token::Comma>,
|
||||
) -> syn::NestedMeta {
|
||||
NestedMeta::Meta(Meta::List(syn::MetaList {
|
||||
path: make_path(name),
|
||||
paren_token: syn::token::Paren(proc_macro2::Span::call_site()),
|
||||
nested,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn make_attr_from_str<S: AsRef<str>>(s: S, span: Span) -> syn::Result<syn::Attribute> {
|
||||
let stream: TokenStream2 = format!("#[{}]", s.as_ref()).parse()?;
|
||||
let mut attrs: VecOfAttrs = syn::parse(stream.into())?;
|
||||
let attr = match attrs.attrs.len() {
|
||||
1 => attrs.attrs.remove(0),
|
||||
_ => return Err(syn::Error::new(span, "Expected attribute")),
|
||||
};
|
||||
|
||||
Ok(attr)
|
||||
}
|
||||
|
||||
pub(crate) fn make_attr_ts_from_str<S: AsRef<str>>(s: S, span: Span) -> syn::Result<TokenStream2> {
|
||||
Ok(make_attr_from_str(s, span)?.to_token_stream())
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct VecOfAttrs {
|
||||
pub attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for VecOfAttrs {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
Ok(VecOfAttrs {
|
||||
attrs: input.call(syn::Attribute::parse_outer)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct AttributeArgsInParens {
|
||||
_paren: syn::token::Paren,
|
||||
pub args: Punctuated<NestedMeta, Token![,]>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for AttributeArgsInParens {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
Ok(Self {
|
||||
_paren: parenthesized!(content in input),
|
||||
args: content.parse_terminated(NestedMeta::parse)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct PunctuatedList {
|
||||
pub list: Punctuated<Expr, Comma>,
|
||||
}
|
||||
|
||||
impl Parse for PunctuatedList {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Ok(PunctuatedList {
|
||||
list: Punctuated::<Expr, Comma>::parse_terminated(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct DebugByDisplay<T: std::fmt::Display>(pub T);
|
||||
|
||||
impl<T: std::fmt::Display> std::fmt::Debug for DebugByDisplay<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
T::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct OptionToTokens<T: ToTokens>(pub Option<T>);
|
||||
|
||||
impl<T: ToTokens> std::fmt::Debug for OptionToTokens<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.as_ref().map(|m| DebugByDisplay(m.to_token_stream())).fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct EqStr {
|
||||
#[allow(dead_code)]
|
||||
pub eq: Token![=],
|
||||
pub str: syn::LitStr,
|
||||
}
|
||||
|
||||
impl Parse for EqStr {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Ok(EqStr {
|
||||
eq: input.parse()?,
|
||||
str: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+354
@@ -0,0 +1,354 @@
|
||||
#[allow(unused_imports)]
|
||||
use std::iter::FromIterator;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use proc_macro_error::{emit_error, abort};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
visit_mut::{self, VisitMut},
|
||||
};
|
||||
|
||||
use crate::utils::unwrap_or_error;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum IdentMode {
|
||||
Use,
|
||||
Other,
|
||||
}
|
||||
|
||||
pub trait VisitMutExt {
|
||||
fn process_attrs(&mut self, _attrs: &mut Vec<syn::Attribute>) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_ident(&mut self, _ident: &mut syn::Ident, _mode: IdentMode) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_attribute(&mut self, _node: &mut syn::Attribute) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_expr(&mut self, _node: &mut syn::Expr) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_file(&mut self, _node: &mut syn::File) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_item(&mut self, _node: &mut syn::Item) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_item_impl(&mut self, _node: &mut syn::ItemImpl) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_item_trait(&mut self, _node: &mut syn::ItemTrait) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_item_fn(&mut self, _node: &mut syn::ItemFn) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_macro(&mut self, _node: &mut syn::Macro) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_path_segment(&mut self, _node: &mut syn::PathSegment) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_type_param(&mut self, _node: &mut syn::TypeParam) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn process_use_tree(&mut self, _node: &mut syn::UseTree) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn after_process_item(&mut self, _node: &mut syn::Item) -> syn::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
pub struct Visitor<T> {
|
||||
pub inner: T,
|
||||
// err: Option<syn::Error>,
|
||||
}
|
||||
|
||||
impl<T> Visitor<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
pub fn process(&mut self, item: TokenStream2) -> TokenStream2
|
||||
where
|
||||
Self: VisitMutExt,
|
||||
{
|
||||
let mut syntax_tree: syn::File = unwrap_or_error!(syn::parse(item.into()));
|
||||
self.visit_file_mut(&mut syntax_tree);
|
||||
let ts = quote!(#syntax_tree);
|
||||
|
||||
ts
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
macro_rules! impl_fn {
|
||||
(@expr $value:expr, value) => {
|
||||
$value
|
||||
};
|
||||
(@expr $value:expr, (_, value)) => {
|
||||
&mut $value.1
|
||||
};
|
||||
(@func $self:expr, $node:ident, $proc:ident(node $(.$path:ident)? as Some($expr:tt) $(, $mode:expr)?) ) => {
|
||||
if let Some(value) = impl_fn!(@arg $node $(.$path)?) {
|
||||
match $self.$proc( impl_fn!(@expr value, $expr) $(, $mode)? ) {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
emit_error!(e)
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
(@func $self:expr, $node:ident, $proc:ident(node $(.$path:ident)? $(, $mode:expr)?) ) => {
|
||||
match $self.$proc( impl_fn!(@arg $node $(.$path)?) $(, $mode)? ) {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
emit_error!(e)
|
||||
}
|
||||
};
|
||||
};
|
||||
(@funcs $self:expr, $node:ident, { $($proc:ident $params:tt ;)+ } ) => {
|
||||
$(
|
||||
impl_fn!(@func $self, $node, $proc $params);
|
||||
)+
|
||||
};
|
||||
(@arg $node:ident $(.$path:ident)+) => {
|
||||
&mut $node $(.$path)+
|
||||
};
|
||||
(@arg $node:ident) => {
|
||||
$node
|
||||
};
|
||||
($name:ident, $ty:ty, ) => {
|
||||
fn $name(&mut self, node: &mut $ty) {
|
||||
visit_mut::$name(self, node);
|
||||
}
|
||||
};
|
||||
($name:ident, $ty:ty, $before:tt) => {
|
||||
fn $name(&mut self, node: &mut $ty) {
|
||||
impl_fn!(@funcs self, node, $before);
|
||||
|
||||
visit_mut::$name(self, node);
|
||||
}
|
||||
};
|
||||
($name:ident, $ty:ty, $before:tt, $after:tt) => {
|
||||
fn $name(&mut self, node: &mut $ty) {
|
||||
impl_fn!(@funcs self, node, $before);
|
||||
|
||||
visit_mut::$name(self, node);
|
||||
|
||||
impl_fn!(@funcs self, node, $after);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl<T> VisitMut for Visitor<T> where Self: VisitMutExt,
|
||||
{
|
||||
impl_fn!(visit_abi_mut, syn::Abi, );
|
||||
impl_fn!(visit_angle_bracketed_generic_arguments_mut,
|
||||
syn::AngleBracketedGenericArguments,
|
||||
);
|
||||
impl_fn!(visit_arm_mut, syn::Arm, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_attr_style_mut, syn::AttrStyle, );
|
||||
impl_fn!(visit_attribute_mut, syn::Attribute, { process_attribute(node); });
|
||||
impl_fn!(visit_bare_fn_arg_mut, syn::BareFnArg, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_bin_op_mut, syn::BinOp, );
|
||||
impl_fn!(visit_binding_mut, syn::Binding, { process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_block_mut, syn::Block, );
|
||||
impl_fn!(visit_bound_lifetimes_mut, syn::BoundLifetimes, );
|
||||
impl_fn!(visit_const_param_mut, syn::ConstParam, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_constraint_mut, syn::Constraint, );
|
||||
impl_fn!(visit_data_mut, syn::Data, );
|
||||
impl_fn!(visit_data_enum_mut, syn::DataEnum, );
|
||||
impl_fn!(visit_data_struct_mut, syn::DataStruct, );
|
||||
impl_fn!(visit_data_union_mut, syn::DataUnion, );
|
||||
impl_fn!(visit_derive_input_mut, syn::DeriveInput, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_mut, syn::Expr, { process_expr(node); });
|
||||
impl_fn!(visit_expr_array_mut, syn::ExprArray, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_assign_mut, syn::ExprAssign, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_assign_op_mut, syn::ExprAssignOp, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_async_mut, syn::ExprAsync, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_await_mut, syn::ExprAwait, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_binary_mut, syn::ExprBinary, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_block_mut, syn::ExprBlock, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_box_mut, syn::ExprBox, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_break_mut, syn::ExprBreak, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_call_mut, syn::ExprCall, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_cast_mut, syn::ExprCast, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_closure_mut, syn::ExprClosure, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_continue_mut, syn::ExprContinue, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_field_mut, syn::ExprField, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_for_loop_mut, syn::ExprForLoop, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_group_mut, syn::ExprGroup, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_if_mut, syn::ExprIf, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_index_mut, syn::ExprIndex, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_let_mut, syn::ExprLet, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_lit_mut, syn::ExprLit, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_loop_mut, syn::ExprLoop, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_macro_mut, syn::ExprMacro, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_match_mut, syn::ExprMatch, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_method_call_mut, syn::ExprMethodCall, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_paren_mut, syn::ExprParen, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_path_mut, syn::ExprPath, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_range_mut, syn::ExprRange, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_reference_mut, syn::ExprReference, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_repeat_mut, syn::ExprRepeat, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_return_mut, syn::ExprReturn, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_struct_mut, syn::ExprStruct, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_try_mut, syn::ExprTry, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_try_block_mut, syn::ExprTryBlock, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_tuple_mut, syn::ExprTuple, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_type_mut, syn::ExprType, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_unary_mut, syn::ExprUnary, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_unsafe_mut, syn::ExprUnsafe, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_while_mut, syn::ExprWhile, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_expr_yield_mut, syn::ExprYield, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_field_mut, syn::Field, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_field_pat_mut, syn::FieldPat, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_field_value_mut, syn::FieldValue, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_fields_mut, syn::Fields, );
|
||||
impl_fn!(visit_fields_named_mut, syn::FieldsNamed, );
|
||||
impl_fn!(visit_fields_unnamed_mut, syn::FieldsUnnamed, );
|
||||
impl_fn!(visit_file_mut, syn::File, { process_attrs(node.attrs); process_file(node); });
|
||||
impl_fn!(visit_fn_arg_mut, syn::FnArg, );
|
||||
impl_fn!(visit_foreign_item_mut, syn::ForeignItem, );
|
||||
impl_fn!(visit_foreign_item_fn_mut, syn::ForeignItemFn, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_foreign_item_macro_mut, syn::ForeignItemMacro, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_foreign_item_static_mut, syn::ForeignItemStatic, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_foreign_item_type_mut, syn::ForeignItemType, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_generic_argument_mut, syn::GenericArgument, );
|
||||
impl_fn!(visit_generic_method_argument_mut,
|
||||
syn::GenericMethodArgument,
|
||||
);
|
||||
impl_fn!(visit_generic_param_mut, syn::GenericParam, );
|
||||
impl_fn!(visit_generics_mut, syn::Generics, );
|
||||
impl_fn!(visit_ident_mut, syn::Ident, );
|
||||
impl_fn!(visit_impl_item_mut, syn::ImplItem, );
|
||||
impl_fn!(visit_impl_item_const_mut, syn::ImplItemConst, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_impl_item_macro_mut, syn::ImplItemMacro, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_impl_item_method_mut, syn::ImplItemMethod, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_impl_item_type_mut, syn::ImplItemType, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_index_mut, syn::Index, );
|
||||
impl_fn!(visit_item_mut, syn::Item, { process_item(node); }, { after_process_item(node); });
|
||||
impl_fn!(visit_item_const_mut, syn::ItemConst, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_item_enum_mut, syn::ItemEnum, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_item_extern_crate_mut, syn::ItemExternCrate, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); process_ident(node.rename as Some((_, value)), IdentMode::Other); });
|
||||
impl_fn!(visit_item_fn_mut, syn::ItemFn, { process_attrs(node.attrs); process_item_fn(node); });
|
||||
impl_fn!(visit_item_foreign_mod_mut, syn::ItemForeignMod, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_item_impl_mut, syn::ItemImpl, { process_attrs(node.attrs); process_item_impl(node); });
|
||||
impl_fn!(visit_item_macro_mut, syn::ItemMacro, { process_attrs(node.attrs); process_ident(node.ident as Some(value), IdentMode::Other); });
|
||||
impl_fn!(visit_item_macro2_mut, syn::ItemMacro2, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_item_mod_mut, syn::ItemMod, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_item_static_mut, syn::ItemStatic, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_item_struct_mut, syn::ItemStruct, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_item_trait_mut, syn::ItemTrait, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); process_item_trait(node); });
|
||||
impl_fn!(visit_item_trait_alias_mut, syn::ItemTraitAlias, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_item_type_mut, syn::ItemType, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_item_union_mut, syn::ItemUnion, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_item_use_mut, syn::ItemUse, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_label_mut, syn::Label, );
|
||||
impl_fn!(visit_lifetime_mut, syn::Lifetime, );
|
||||
impl_fn!(visit_lifetime_def_mut, syn::LifetimeDef, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_lit_mut, syn::Lit, );
|
||||
impl_fn!(visit_lit_bool_mut, syn::LitBool, );
|
||||
impl_fn!(visit_lit_byte_mut, syn::LitByte, );
|
||||
impl_fn!(visit_lit_byte_str_mut, syn::LitByteStr, );
|
||||
impl_fn!(visit_lit_char_mut, syn::LitChar, );
|
||||
impl_fn!(visit_lit_float_mut, syn::LitFloat, );
|
||||
impl_fn!(visit_lit_int_mut, syn::LitInt, );
|
||||
impl_fn!(visit_lit_str_mut, syn::LitStr, );
|
||||
impl_fn!(visit_local_mut, syn::Local, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_macro_mut, syn::Macro, { process_macro(node); });
|
||||
impl_fn!(visit_macro_delimiter_mut, syn::MacroDelimiter, );
|
||||
impl_fn!(visit_member_mut, syn::Member, );
|
||||
impl_fn!(visit_meta_mut, syn::Meta, );
|
||||
impl_fn!(visit_meta_list_mut, syn::MetaList, );
|
||||
impl_fn!(visit_meta_name_value_mut, syn::MetaNameValue, );
|
||||
impl_fn!(visit_method_turbofish_mut, syn::MethodTurbofish, );
|
||||
impl_fn!(visit_nested_meta_mut, syn::NestedMeta, );
|
||||
impl_fn!(visit_parenthesized_generic_arguments_mut,
|
||||
syn::ParenthesizedGenericArguments,
|
||||
);
|
||||
impl_fn!(visit_pat_mut, syn::Pat, );
|
||||
impl_fn!(visit_pat_box_mut, syn::PatBox, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_ident_mut, syn::PatIdent, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_lit_mut, syn::PatLit, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_macro_mut, syn::PatMacro, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_or_mut, syn::PatOr, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_path_mut, syn::PatPath, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_range_mut, syn::PatRange, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_reference_mut, syn::PatReference, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_rest_mut, syn::PatRest, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_slice_mut, syn::PatSlice, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_struct_mut, syn::PatStruct, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_tuple_mut, syn::PatTuple, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_tuple_struct_mut, syn::PatTupleStruct, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_type_mut, syn::PatType, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_pat_wild_mut, syn::PatWild, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_path_mut, syn::Path, );
|
||||
impl_fn!(visit_path_arguments_mut, syn::PathArguments, );
|
||||
impl_fn!(visit_path_segment_mut, syn::PathSegment, { process_path_segment(node); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_predicate_eq_mut, syn::PredicateEq, );
|
||||
impl_fn!(visit_predicate_lifetime_mut, syn::PredicateLifetime, );
|
||||
impl_fn!(visit_predicate_type_mut, syn::PredicateType, );
|
||||
impl_fn!(visit_qself_mut, syn::QSelf, );
|
||||
impl_fn!(visit_range_limits_mut, syn::RangeLimits, );
|
||||
impl_fn!(visit_receiver_mut, syn::Receiver, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_return_type_mut, syn::ReturnType, );
|
||||
impl_fn!(visit_signature_mut, syn::Signature, { process_ident(node.ident, IdentMode::Other); } );
|
||||
impl_fn!(visit_span_mut, Span, );
|
||||
impl_fn!(visit_stmt_mut, syn::Stmt, );
|
||||
impl_fn!(visit_trait_bound_mut, syn::TraitBound, );
|
||||
impl_fn!(visit_trait_bound_modifier_mut,syn::TraitBoundModifier,);
|
||||
impl_fn!(visit_trait_item_mut, syn::TraitItem, );
|
||||
impl_fn!(visit_trait_item_const_mut, syn::TraitItemConst, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_trait_item_macro_mut, syn::TraitItemMacro, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_trait_item_method_mut, syn::TraitItemMethod, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_trait_item_type_mut, syn::TraitItemType, { process_attrs(node.attrs); process_ident(node.ident, IdentMode::Other); });
|
||||
impl_fn!(visit_type_mut, syn::Type, );
|
||||
impl_fn!(visit_type_array_mut, syn::TypeArray, );
|
||||
impl_fn!(visit_type_bare_fn_mut, syn::TypeBareFn, );
|
||||
impl_fn!(visit_type_group_mut, syn::TypeGroup, );
|
||||
impl_fn!(visit_type_impl_trait_mut, syn::TypeImplTrait, );
|
||||
impl_fn!(visit_type_infer_mut, syn::TypeInfer, );
|
||||
impl_fn!(visit_type_macro_mut, syn::TypeMacro, );
|
||||
impl_fn!(visit_type_never_mut, syn::TypeNever, );
|
||||
impl_fn!(visit_type_param_mut, syn::TypeParam, { process_attrs(node.attrs); process_type_param(node); });
|
||||
impl_fn!(visit_type_param_bound_mut, syn::TypeParamBound, );
|
||||
impl_fn!(visit_type_paren_mut, syn::TypeParen, );
|
||||
impl_fn!(visit_type_path_mut, syn::TypePath, );
|
||||
impl_fn!(visit_type_ptr_mut, syn::TypePtr, );
|
||||
impl_fn!(visit_type_reference_mut, syn::TypeReference, );
|
||||
impl_fn!(visit_type_slice_mut, syn::TypeSlice, );
|
||||
impl_fn!(visit_type_trait_object_mut, syn::TypeTraitObject, );
|
||||
impl_fn!(visit_type_tuple_mut, syn::TypeTuple, );
|
||||
impl_fn!(visit_un_op_mut, syn::UnOp, );
|
||||
impl_fn!(visit_use_glob_mut, syn::UseGlob, );
|
||||
impl_fn!(visit_use_group_mut, syn::UseGroup, );
|
||||
impl_fn!(visit_use_name_mut, syn::UseName, { process_ident(node.ident, IdentMode::Use); });
|
||||
impl_fn!(visit_use_path_mut, syn::UsePath, { process_ident(node.ident, IdentMode::Use); });
|
||||
impl_fn!(visit_use_rename_mut, syn::UseRename, { process_ident(node.ident, IdentMode::Use); process_ident(node.rename, IdentMode::Use); });
|
||||
impl_fn!(visit_use_tree_mut, syn::UseTree, { process_use_tree(node); });
|
||||
impl_fn!(visit_variadic_mut, syn::Variadic, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_variant_mut, syn::Variant, { process_attrs(node.attrs); });
|
||||
impl_fn!(visit_vis_crate_mut, syn::VisCrate, );
|
||||
impl_fn!(visit_vis_public_mut, syn::VisPublic, );
|
||||
impl_fn!(visit_vis_restricted_mut, syn::VisRestricted, );
|
||||
impl_fn!(visit_visibility_mut, syn::Visibility, );
|
||||
impl_fn!(visit_where_clause_mut, syn::WhereClause, );
|
||||
impl_fn!(visit_where_predicate_mut, syn::WherePredicate, );
|
||||
|
||||
}
|
||||
+657
@@ -0,0 +1,657 @@
|
||||
#[allow(unused_imports)]
|
||||
use std::{collections::HashMap, iter::FromIterator};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use proc_macro::{TokenStream};
|
||||
#[cfg(feature = "doctests")]
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{visit_mut::VisitMut, spanned::Spanned};
|
||||
|
||||
use crate::{
|
||||
MACRO_NOOP_NAME, MACRO_REMOVE_NAME, MACRO_ONLY_IF_NAME, MACRO_REMOVE_IF_NAME,
|
||||
params::{ConvertMode, MacroParameters},
|
||||
utils::{AttributeArgsInParens, PunctuatedList, make_attr_from_str},
|
||||
visit_ext::{IdentMode, VisitMutExt, Visitor},
|
||||
};
|
||||
#[cfg(feature = "doctests")]
|
||||
use crate::{
|
||||
doctests::process_doctests,
|
||||
utils::{EqStr, make_path},
|
||||
};
|
||||
|
||||
pub struct AsyncAwaitVisitor<'p> {
|
||||
convert_mode: ConvertMode,
|
||||
params: &'p mut MacroParameters,
|
||||
generics: Vec<HashMap<String, syn::PathSegment>>,
|
||||
}
|
||||
|
||||
impl<'p> AsyncAwaitVisitor<'p> {
|
||||
pub fn new(params: &'p mut MacroParameters, convert_mode: ConvertMode) -> Self {
|
||||
Self {
|
||||
convert_mode,
|
||||
params,
|
||||
generics: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn generics_get<S: AsRef<str>>(&self, key: S) -> Option<&syn::PathSegment> {
|
||||
for gens in &self.generics {
|
||||
if let Some(ps) = gens.get(key.as_ref()) {
|
||||
return Some(ps);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn search_future_trait_bound(bound: &syn::TypeParamBound) -> Option<syn::PathSegment> {
|
||||
if let syn::TypeParamBound::Trait(trait_bound) = bound {
|
||||
let segment = &trait_bound.path.segments[trait_bound.path.segments.len() - 1];
|
||||
let name = segment.ident.to_string();
|
||||
if name.eq("Future") {
|
||||
// match Future<Output=Type>
|
||||
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
|
||||
// binding: Output=Type
|
||||
if let syn::GenericArgument::Binding(binding) = &args.args[0] {
|
||||
if let syn::Type::Path(p) = &binding.ty {
|
||||
return Some(p.path.segments[0].clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn remove_asyncness_on_trait(item: &mut syn::ItemTrait, convert_mode: ConvertMode) {
|
||||
match convert_mode {
|
||||
ConvertMode::IntoSync => {
|
||||
for inner in &mut item.items {
|
||||
if let syn::TraitItem::Method(ref mut method) = inner {
|
||||
if method.sig.asyncness.is_some() {
|
||||
method.sig.asyncness = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ConvertMode::IntoAsync => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_asyncness_on_impl(item: &mut syn::ItemImpl, convert_mode: ConvertMode, send: Option<bool>) {
|
||||
match convert_mode {
|
||||
ConvertMode::IntoSync => {
|
||||
for inner in &mut item.items {
|
||||
if let syn::ImplItem::Method(ref mut method) = inner {
|
||||
if method.sig.asyncness.is_some() {
|
||||
method.sig.asyncness = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ConvertMode::IntoAsync => {
|
||||
if let Some(send) = send {
|
||||
let attr_str = if send {
|
||||
"async_trait::async_trait"
|
||||
} else {
|
||||
"async_trait::async_trait(?Send)"
|
||||
};
|
||||
let attr = make_attr_from_str(attr_str, item.span()).unwrap();
|
||||
item.attrs.push(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_asyncness_on_fn(item: &mut syn::ItemFn, convert_mode: ConvertMode) {
|
||||
match convert_mode {
|
||||
ConvertMode::IntoSync => {
|
||||
if item.sig.asyncness.is_some() {
|
||||
item.sig.asyncness = None;
|
||||
}
|
||||
}
|
||||
ConvertMode::IntoAsync => {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p> AsyncAwaitVisitor<'p> {
|
||||
fn process_replace_features_meta(&self, meta: &mut syn::Meta) -> syn::Result<bool> {
|
||||
let mut changed = false;
|
||||
|
||||
match meta {
|
||||
syn::Meta::NameValue(syn::MetaNameValue {
|
||||
path,
|
||||
lit: syn::Lit::Str(s),
|
||||
..
|
||||
}) => {
|
||||
if let Some(ident) = path.get_ident() {
|
||||
if ident.to_string() == "feature" {
|
||||
let prev = s.value();
|
||||
if let Some(new) = self.params.replace_features_get(&prev) {
|
||||
*s = syn::LitStr::new(new, s.span());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Meta::List(list) => {
|
||||
for nm in &mut list.nested {
|
||||
if let syn::NestedMeta::Meta(m) = nm {
|
||||
changed |= self.process_replace_features_meta(m)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(changed)
|
||||
}
|
||||
|
||||
fn process_attribute_if(&mut self, attr: &mut syn::Attribute, not: bool) -> syn::Result<()> {
|
||||
let args =
|
||||
syn::parse_macro_input::parse::<AttributeArgsInParens>(attr.tokens.clone().into())?;
|
||||
|
||||
let arg = match &args.args.len() {
|
||||
0 => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
attr.to_token_stream(),
|
||||
"Expected ident",
|
||||
))
|
||||
}
|
||||
1 => &args.args[0],
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
args.args[1].to_token_stream(),
|
||||
"Too many arguments",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let key = match arg {
|
||||
syn::NestedMeta::Lit(syn::Lit::Str(s)) => s.value(),
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(ref p)) => {
|
||||
if let Some(s) = p.get_ident() {
|
||||
s.to_string()
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
arg.to_token_stream(),
|
||||
"Wrong ident",
|
||||
));
|
||||
}
|
||||
}
|
||||
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
|
||||
path,
|
||||
lit: syn::Lit::Str(value),
|
||||
..
|
||||
})) if path.is_ident("key") => value.value(),
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
arg.to_token_stream(),
|
||||
"Wrong ident",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let success = if let Some(current_key) = self.params.key_get() {
|
||||
(current_key == &key) ^ not
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let new_name = if success { MACRO_NOOP_NAME } else { MACRO_REMOVE_NAME };
|
||||
attr.path = self.params.make_self_path(new_name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "doctests")]
|
||||
fn process_doc_attrs(&mut self, attrs: &mut Vec<syn::Attribute>) -> syn::Result<()> {
|
||||
|
||||
let mut acc: Vec<syn::Attribute> = vec![];
|
||||
let mut acc_temp: Vec<syn::Attribute> = vec![];
|
||||
let mut lines: Vec<String> = vec![];
|
||||
let mut inside_doc = false;
|
||||
|
||||
fn process_docs(acc: &mut Vec<syn::Attribute>, acc_temp: &mut Vec<syn::Attribute>, lines: &mut Vec<String>, params: &MacroParameters) {
|
||||
assert!(!lines.is_empty());
|
||||
let mut first = true;
|
||||
let doc: String = lines.iter().map(|s| {
|
||||
if first {
|
||||
first = false;
|
||||
s.clone()
|
||||
} else {
|
||||
let mut ss = String::from("\n");
|
||||
ss.push_str( s.as_str() );
|
||||
ss
|
||||
}
|
||||
}).collect();
|
||||
|
||||
let processor = |key: &str, code: &str| -> Option<Option<String>> {
|
||||
if let Some(param_key) = params.key_get() {
|
||||
if param_key == key {
|
||||
Some(Some(code.to_string()))
|
||||
} else {
|
||||
Some(None)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(doc) = process_doctests(doc.as_str(), processor) {
|
||||
let mut acc_temp_drain = acc_temp.drain(..);
|
||||
for line in doc.lines() {
|
||||
let tokens = quote!(= #line);
|
||||
let attr = if let Some(mut attr) = acc_temp_drain.next() {
|
||||
attr.tokens = tokens;
|
||||
attr
|
||||
} else {
|
||||
let sp = Span::call_site();
|
||||
syn::Attribute {
|
||||
pound_token: syn::Token,
|
||||
style: syn::AttrStyle::Outer,
|
||||
bracket_token: syn::token::Bracket(sp),
|
||||
path: make_path("doc"),
|
||||
tokens,
|
||||
}
|
||||
};
|
||||
acc.push(attr);
|
||||
}
|
||||
} else {
|
||||
for attr in acc_temp.drain(..) {
|
||||
acc.push(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for attr in attrs.drain(..) {
|
||||
match (inside_doc, attr.path.is_ident("doc")) {
|
||||
(false, false) => {
|
||||
acc.push(attr);
|
||||
},
|
||||
(false, true) => {
|
||||
let es = syn::parse2::<EqStr>(attr.tokens.clone())?;
|
||||
let doc = es.str.value();
|
||||
|
||||
lines.push(doc);
|
||||
acc_temp.push(attr);
|
||||
inside_doc = true;
|
||||
},
|
||||
(true, false) => {
|
||||
process_docs(&mut acc, &mut acc_temp, &mut lines, &self.params);
|
||||
|
||||
acc_temp.clear();
|
||||
lines.clear();
|
||||
inside_doc = false;
|
||||
|
||||
acc.push(attr);
|
||||
},
|
||||
(true, true) => {
|
||||
let es = syn::parse2::<EqStr>(attr.tokens.clone())?;
|
||||
let doc = es.str.value();
|
||||
|
||||
lines.push(doc);
|
||||
acc_temp.push(attr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if inside_doc {
|
||||
process_docs(&mut acc, &mut acc_temp, &mut lines, &self.params);
|
||||
}
|
||||
|
||||
let _ = std::mem::replace(attrs, acc);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_attrs(&mut self, attrs: &mut Vec<syn::Attribute>) -> syn::Result<()> {
|
||||
#[cfg(feature = "doctests")]
|
||||
self.process_doc_attrs(attrs)?;
|
||||
|
||||
for attr in attrs.iter_mut() {
|
||||
if let Some(name) = self.params.is_our_attr(attr) {
|
||||
match name.as_str() {
|
||||
MACRO_ONLY_IF_NAME => self.process_attribute_if(attr, false)?,
|
||||
MACRO_REMOVE_IF_NAME => self.process_attribute_if(attr, true)?,
|
||||
_ => {
|
||||
// Attribute stays unchanged. Unknown attributes will be
|
||||
// rejected by compiter later.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !self.params.drop_attrs_is_empty() {
|
||||
attrs.retain(|attr| {
|
||||
if let Some(ident) = attr.path.get_ident() {
|
||||
let ident = ident.to_string();
|
||||
!self.params.drop_attrs_contains(&ident)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if !self.params.replace_features_is_empty() {
|
||||
for attr in attrs {
|
||||
if let Some(ident) = attr.path.get_ident() {
|
||||
if ident.to_string() == "cfg" {
|
||||
if let Ok(mut meta) = attr.parse_meta() {
|
||||
if self.process_replace_features_meta(&mut meta)? {
|
||||
if let syn::Meta::List(syn::MetaList { nested, .. }) = meta {
|
||||
attr.tokens = quote!((#nested));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_expr(&mut self, node: &mut syn::Expr) -> syn::Result<()> {
|
||||
match self.convert_mode {
|
||||
ConvertMode::IntoSync => {
|
||||
// async -> sync, remove async_impl blocks
|
||||
match node {
|
||||
syn::Expr::Await(expr) => {
|
||||
*node = (*expr.base).clone()
|
||||
}
|
||||
|
||||
syn::Expr::Async(expr) => {
|
||||
let inner = &expr.block;
|
||||
let sync_expr = if inner.stmts.len() == 1 {
|
||||
// remove useless braces when there is only one statement
|
||||
let stmt = &inner.stmts.get(0).unwrap();
|
||||
// convert statement to Expr
|
||||
syn::parse_quote!(#stmt)
|
||||
} else {
|
||||
syn::Expr::Block(syn::ExprBlock {
|
||||
attrs: expr.attrs.clone(),
|
||||
block: inner.clone(),
|
||||
label: None,
|
||||
})
|
||||
};
|
||||
*node = sync_expr;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ConvertMode::IntoAsync => {
|
||||
// stay async, just remove sync_impl blocks
|
||||
match node {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_item(&mut self, node: &mut syn::Item) -> syn::Result<()> {
|
||||
match self.convert_mode {
|
||||
ConvertMode::IntoSync => {
|
||||
// find generic parameter of Future and replace it with its Output type
|
||||
if let syn::Item::Fn(item_fn) = node {
|
||||
let mut gens: HashMap<String, syn::PathSegment> = HashMap::new();
|
||||
|
||||
// generic params: <T:Future<Output=()>, F>
|
||||
for param in &item_fn.sig.generics.params {
|
||||
// generic param: T:Future<Output=()>
|
||||
if let syn::GenericParam::Type(type_param) = param {
|
||||
let generic_type_name = &type_param.ident;
|
||||
|
||||
// bound: Future<Output=()>
|
||||
for bound in &type_param.bounds {
|
||||
if let Some(ps) = search_future_trait_bound(bound) {
|
||||
gens.insert(generic_type_name.to_string(), ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(where_clause) = &item_fn.sig.generics.where_clause {
|
||||
for predicate in &where_clause.predicates {
|
||||
if let syn::WherePredicate::Type(predicate_type) = predicate {
|
||||
let generic_type_name =
|
||||
if let syn::Type::Path(p) = &predicate_type.bounded_ty {
|
||||
&p.path.segments[0].ident
|
||||
} else {
|
||||
panic!("Please submit an issue");
|
||||
};
|
||||
|
||||
for bound in &predicate_type.bounds {
|
||||
if let Some(ps) = search_future_trait_bound(bound) {
|
||||
gens.insert(generic_type_name.to_string(), ps);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.generics.push(gens);
|
||||
}
|
||||
|
||||
if let syn::Item::Fn(item_fn) = node {
|
||||
// remove generic type from generics <T, F>
|
||||
let args = item_fn
|
||||
.sig
|
||||
.generics
|
||||
.params
|
||||
.iter()
|
||||
.filter_map(|param| {
|
||||
if let syn::GenericParam::Type(type_param) = ¶m {
|
||||
if let Some(_) = self.generics_get(type_param.ident.to_string()) {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(param)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
item_fn.sig.generics.params = syn::punctuated::Punctuated::from_iter(
|
||||
args.into_iter().map(|p| p.clone()).collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
// remove generic type from where clause
|
||||
if let Some(where_clause) = &mut item_fn.sig.generics.where_clause {
|
||||
let new_where_clause = where_clause
|
||||
.predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let syn::WherePredicate::Type(predicate_type) = predicate {
|
||||
if let syn::Type::Path(p) = &predicate_type.bounded_ty {
|
||||
if let Some(_) =
|
||||
self.generics_get(p.path.segments[0].ident.to_string())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
Some(predicate)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
where_clause.predicates = syn::punctuated::Punctuated::from_iter(
|
||||
new_where_clause
|
||||
.into_iter()
|
||||
.map(|c| c.clone())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
ConvertMode::IntoAsync => {}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn after_process_item(&mut self, node: &mut syn::Item) -> syn::Result<()> {
|
||||
match self.convert_mode {
|
||||
ConvertMode::IntoSync => {
|
||||
// find generic parameter of Future and replace it with its Output type
|
||||
if let syn::Item::Fn(_item_fn) = node {
|
||||
self.generics.pop();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_item_impl(&mut self, node: &mut syn::ItemImpl) -> syn::Result<()> {
|
||||
if self.params.recursive_asyncness_removal_get() {
|
||||
remove_asyncness_on_impl( node, self.convert_mode, self.params.send_get() );
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_item_trait(&mut self, node: &mut syn::ItemTrait) -> syn::Result<()> {
|
||||
if self.params.recursive_asyncness_removal_get() {
|
||||
remove_asyncness_on_trait( node, self.convert_mode );
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_item_fn(&mut self, node: &mut syn::ItemFn) -> syn::Result<()> {
|
||||
if self.params.recursive_asyncness_removal_get() {
|
||||
remove_asyncness_on_fn( node, self.convert_mode );
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_path_segment(&mut self, node: &mut syn::PathSegment) -> syn::Result<()> {
|
||||
let ident = &mut node.ident;
|
||||
let ident_s = ident.to_string();
|
||||
|
||||
// replace generic type with target type
|
||||
if let Some(ps) = self.generics_get(&ident_s) {
|
||||
*node = ps.clone();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_ident(&mut self, ident: &mut syn::Ident, mode: IdentMode) -> syn::Result<()> {
|
||||
if mode == IdentMode::Use {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
if let Some(ir) = self.params.idents_get(ident.to_string()) {
|
||||
*ident = ir.ident_add_suffix(ident, self.convert_mode, self.params.key_get());
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_type_param(&mut self, node: &mut syn::TypeParam) -> syn::Result<()> {
|
||||
let ident = &mut node.ident;
|
||||
|
||||
if let Some(ir) = self.params.idents_get(&ident.to_string()) {
|
||||
*ident = ir.ident_add_suffix(ident, self.convert_mode, self.params.key_get());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_use_tree(&mut self, node: &mut syn::UseTree) -> syn::Result<()> {
|
||||
match node {
|
||||
syn::UseTree::Path(syn::UsePath { ident, .. }) => {
|
||||
if let Some(ir) = self.params.idents_get(&ident.to_string()) {
|
||||
if !ir.use_mode {
|
||||
*ident = ir.ident_add_suffix(ident, self.convert_mode, self.params.key_get());
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::UseTree::Name(name) => {
|
||||
let ident = &mut name.ident;
|
||||
|
||||
if let Some(ir) = self.params.idents_get(&ident.to_string()) {
|
||||
if ir.use_mode {
|
||||
*node = syn::UseTree::Rename(syn::UseRename {
|
||||
ident: ident.clone(),
|
||||
as_token: syn::Token),
|
||||
rename: ir.ident_add_suffix(ident, self.convert_mode, self.params.key_get()),
|
||||
});
|
||||
} else {
|
||||
*ident = ir.ident_add_suffix(ident, self.convert_mode, self.params.key_get());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p> VisitMutExt for Visitor<AsyncAwaitVisitor<'p>> {
|
||||
fn process_attrs(&mut self, attrs: &mut Vec<syn::Attribute>) -> syn::Result<()> {
|
||||
self.inner.process_attrs(attrs)
|
||||
}
|
||||
fn process_ident(&mut self, ident: &mut syn::Ident, mode: IdentMode) -> syn::Result<()> {
|
||||
self.inner.process_ident(ident, mode)
|
||||
}
|
||||
fn process_expr(&mut self, node: &mut syn::Expr) -> syn::Result<()> {
|
||||
self.inner.process_expr(node)
|
||||
}
|
||||
fn process_item(&mut self, node: &mut syn::Item) -> syn::Result<()> {
|
||||
self.inner.process_item(node)
|
||||
}
|
||||
fn process_item_impl(&mut self, node: &mut syn::ItemImpl) -> syn::Result<()> {
|
||||
self.inner.process_item_impl(node)
|
||||
}
|
||||
fn process_item_trait(&mut self, node: &mut syn::ItemTrait) -> syn::Result<()> {
|
||||
self.inner.process_item_trait(node)
|
||||
}
|
||||
fn process_item_fn(&mut self, node: &mut syn::ItemFn) -> syn::Result<()> {
|
||||
self.inner.process_item_fn(node)
|
||||
}
|
||||
fn after_process_item(&mut self, node: &mut syn::Item) -> syn::Result<()> {
|
||||
self.inner.after_process_item(node)
|
||||
}
|
||||
|
||||
fn process_macro(&mut self, node: &mut syn::Macro) -> syn::Result<()> {
|
||||
if let Some(ident) = node.path.get_ident() {
|
||||
if self
|
||||
.inner
|
||||
.params
|
||||
.standard_macros()
|
||||
.contains(&ident.to_string().as_str())
|
||||
{
|
||||
let mut args = syn::parse2::<PunctuatedList>(node.tokens.clone())?;
|
||||
|
||||
for arg in &mut args.list {
|
||||
self.visit_expr_mut(arg);
|
||||
}
|
||||
|
||||
node.tokens = args.list.into_token_stream();
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
fn process_path_segment(&mut self, node: &mut syn::PathSegment) -> syn::Result<()> {
|
||||
self.inner.process_path_segment(node)
|
||||
}
|
||||
fn process_type_param(&mut self, node: &mut syn::TypeParam) -> syn::Result<()> {
|
||||
self.inner.process_type_param(node)
|
||||
}
|
||||
fn process_use_tree(&mut self, node: &mut syn::UseTree) -> syn::Result<()> {
|
||||
self.inner.process_use_tree(node)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p> AsyncAwaitVisitor<'p> {}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
#[allow(unused_imports)]
|
||||
use std::iter::FromIterator;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{Delimiter, Group, TokenStream as TokenStream2, TokenTree};
|
||||
use proc_macro_error::emit_error;
|
||||
use quote::quote;
|
||||
|
||||
use crate::{
|
||||
params::MacroParameters,
|
||||
utils::{set_error_and_return, unwrap_or_set_error_and_return},
|
||||
visit_ext::{VisitMutExt, Visitor},
|
||||
DEFAULT_CRATE_NAME, MACRO_MAYBE_NAME, MACRO_DEFAULT_NAME,
|
||||
};
|
||||
|
||||
pub struct ContentVisitor {
|
||||
pub params: MacroParameters,
|
||||
}
|
||||
|
||||
impl ContentVisitor {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
params: MacroParameters::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn process_attribute_maybe(&mut self, node: &mut syn::Attribute) -> syn::Result<()> {
|
||||
let mut params = MacroParameters::from_tokens_in_parens(node.tokens.clone().into())?;
|
||||
|
||||
MacroParameters::apply_parent(&mut params, &self.params)?;
|
||||
|
||||
let tokens = params.to_tokens(None);
|
||||
node.tokens = quote!((#tokens));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_file(&mut self, node: &mut syn::File) -> syn::Result<()> {
|
||||
node.attrs.retain(|attr| {
|
||||
if let Some(prefix) = is_default_attr(attr) {
|
||||
self.params = unwrap_or_set_error_and_return!(
|
||||
MacroParameters::from_tokens_in_parens(attr.tokens.clone().into()),
|
||||
false
|
||||
);
|
||||
self.params.prefix_set(prefix);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_attribute(&mut self, node: &mut syn::Attribute) -> syn::Result<()> {
|
||||
if let Some(name) = self.params.is_our_attr(node) {
|
||||
match name.as_str() {
|
||||
MACRO_MAYBE_NAME => self.process_attribute_maybe(node)?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn extend_with_group(parent: &mut TokenStream2, child: TokenStream2, delim: Delimiter) {
|
||||
let g = TokenTree::Group(Group::new(delim, child));
|
||||
parent.extend(vec![g]);
|
||||
}
|
||||
|
||||
fn search_maybe_in_tokenstream(&self, ts: TokenStream2, initial_state: u8) -> TokenStream2 {
|
||||
let mut state: u8 = initial_state;
|
||||
let mut result = TokenStream2::new();
|
||||
|
||||
if state == 7 {
|
||||
let mut params = match MacroParameters::from_tokens(ts.clone().into()) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return ts,
|
||||
};
|
||||
|
||||
match MacroParameters::apply_parent(&mut params, &self.params) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return ts,
|
||||
};
|
||||
|
||||
return params.to_tokens(None).into();
|
||||
}
|
||||
|
||||
let mut iter = ts.into_iter();
|
||||
while let Some(tt) = iter.next() {
|
||||
match state {
|
||||
0 => {
|
||||
match &tt {
|
||||
TokenTree::Punct(p) if p.as_char() == '#' => {
|
||||
result.extend(vec![tt]);
|
||||
state = 1;
|
||||
continue;
|
||||
}
|
||||
TokenTree::Group(g) => {
|
||||
Self::extend_with_group(
|
||||
&mut result,
|
||||
self.search_maybe_in_tokenstream(g.stream(), 0),
|
||||
g.delimiter(),
|
||||
);
|
||||
state = 0;
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
1 => {
|
||||
if let TokenTree::Group(g) = &tt {
|
||||
if g.delimiter() == Delimiter::Bracket {
|
||||
Self::extend_with_group(
|
||||
&mut result,
|
||||
self.search_maybe_in_tokenstream(g.stream(), 2),
|
||||
g.delimiter(),
|
||||
);
|
||||
state = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if let TokenTree::Ident(ident) = &tt {
|
||||
if ident.to_string() == DEFAULT_CRATE_NAME {
|
||||
result.extend(vec![tt]);
|
||||
state = 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
if let TokenTree::Punct(p) = &tt {
|
||||
if p.as_char() == ':' {
|
||||
result.extend(vec![tt]);
|
||||
state = 4;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
4 => {
|
||||
if let TokenTree::Punct(p) = &tt {
|
||||
if p.as_char() == ':' {
|
||||
result.extend(vec![tt]);
|
||||
state = 5;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
5 => {
|
||||
if let TokenTree::Ident(ident) = &tt {
|
||||
if ident.to_string() == MACRO_MAYBE_NAME {
|
||||
result.extend(vec![tt]);
|
||||
state = 6;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
6 => {
|
||||
if let TokenTree::Group(g) = &tt {
|
||||
if g.delimiter() == Delimiter::Parenthesis {
|
||||
Self::extend_with_group(
|
||||
&mut result,
|
||||
self.search_maybe_in_tokenstream(g.stream(), 7),
|
||||
g.delimiter(),
|
||||
);
|
||||
|
||||
state = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
result.extend(vec![tt]);
|
||||
state = 0;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl VisitMutExt for Visitor<ContentVisitor> {
|
||||
fn process_attribute(&mut self, node: &mut syn::Attribute) -> syn::Result<()> {
|
||||
self.inner.process_attribute(node)
|
||||
}
|
||||
|
||||
fn process_file(&mut self, node: &mut syn::File) -> syn::Result<()> {
|
||||
self.inner.process_file(node)
|
||||
}
|
||||
|
||||
fn process_macro(&mut self, node: &mut syn::Macro) -> syn::Result<()> {
|
||||
if node.path.is_ident("macro_rules") {
|
||||
node.tokens = self
|
||||
.inner
|
||||
.search_maybe_in_tokenstream(node.tokens.clone(), 0);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn is_default_attr(attr: &syn::Attribute) -> Option<String> {
|
||||
if let syn::AttrStyle::Inner(_) = attr.style {
|
||||
if attr.path.leading_colon.is_none() && attr.path.segments.len() == 2 {
|
||||
let first_segment = &attr.path.segments[0];
|
||||
let last_segment = &attr.path.segments[1];
|
||||
if first_segment.arguments == syn::PathArguments::None
|
||||
&& last_segment.arguments == syn::PathArguments::None
|
||||
{
|
||||
let first = first_segment.ident.to_string();
|
||||
let last = last_segment.ident.to_string();
|
||||
|
||||
if last == MACRO_DEFAULT_NAME {
|
||||
return Some(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
None
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
#[allow(dead_code)]
|
||||
trait T {
|
||||
/// This is a doctest
|
||||
fn f() {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
#[test]
|
||||
fn ui() {
|
||||
let t = trybuild::TestCases::new();
|
||||
t.pass("tests/ui/01-maybe-async.rs");
|
||||
t.pass("tests/ui/02-must-be-async.rs");
|
||||
t.pass("tests/ui/03-must-be-sync.rs");
|
||||
t.pass("tests/ui/04-unit-test-util.rs");
|
||||
t.pass("tests/ui/05-replace-future-generic-type-with-output.rs");
|
||||
t.pass("tests/ui/06-sync_impl_async_impl.rs");
|
||||
|
||||
t.compile_fail("tests/ui/test_fail/01-empty-test.rs");
|
||||
t.compile_fail("tests/ui/test_fail/02-unknown-path.rs");
|
||||
t.compile_fail("tests/ui/test_fail/03-async-gt2.rs");
|
||||
t.compile_fail("tests/ui/test_fail/04-bad-sync-cond.rs");
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[maybe_async_cfg::maybe(send="Send", sync(feature = "is_sync"), async(not(feature = "is_sync"), async_trait::async_trait))]
|
||||
trait Trait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(send="?Send", sync(feature = "is_sync"), async(not(feature = "is_sync"), async_trait::async_trait))]
|
||||
pub trait PubTrait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
async fn async_fn() {}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
pub async fn pub_async_fn() {
|
||||
}
|
||||
|
||||
struct Struct;
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, idents(Trait), sync(feature = "is_sync"), async(not(feature = "is_sync"), async_trait::async_trait))]
|
||||
impl Trait for Struct {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self) {}
|
||||
|
||||
async fn async_fn(&self) {
|
||||
async { self.declare_async().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "is_sync")]
|
||||
fn main() -> std::result::Result<(), ()> {
|
||||
let s = Struct;
|
||||
s.declare_async();
|
||||
s.async_fn();
|
||||
async_fn();
|
||||
pub_async_fn();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
let s = Struct;
|
||||
s.declare_async().await;
|
||||
s.async_fn().await;
|
||||
async_fn().await;
|
||||
pub_async_fn().await;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[maybe_async_cfg::maybe(send="Send", async(not(feature = "is_sync"), async_trait::async_trait))]
|
||||
trait Trait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(send="?Send", async(not(feature = "is_sync"), async_trait::async_trait))]
|
||||
trait NotSendTrait {
|
||||
async fn declare_async_not_send(&self);
|
||||
|
||||
async fn async_fn_not_send(&self) {
|
||||
self.declare_async_not_send().await
|
||||
}
|
||||
}
|
||||
#[maybe_async_cfg::maybe(send="Send", async(not(feature = "is_sync"), async_trait::async_trait))]
|
||||
pub trait PubTrait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, async(not(feature = "is_sync")))]
|
||||
async fn async_fn() {}
|
||||
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, async(not(feature = "is_sync")))]
|
||||
pub async fn pub_async_fn() {}
|
||||
|
||||
struct Struct;
|
||||
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, idents(Trait), async(not(feature = "is_sync"), async_trait::async_trait))]
|
||||
impl Trait for Struct {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self) {}
|
||||
|
||||
async fn async_fn(&self) {
|
||||
async { self.declare_async().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, idents(NotSendTrait), send="?Send", async(not(feature = "is_sync"), async_trait::async_trait))]
|
||||
impl NotSendTrait for Struct {
|
||||
async fn declare_async_not_send(&self) {}
|
||||
|
||||
async fn async_fn_not_send(&self) {
|
||||
async { self.declare_async_not_send().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "is_sync")]
|
||||
fn main() {}
|
||||
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[async_std::main]
|
||||
async fn main() {
|
||||
let s = Struct;
|
||||
s.declare_async().await;
|
||||
s.async_fn().await;
|
||||
async_fn().await;
|
||||
pub_async_fn().await;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"))]
|
||||
trait Trait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"))]
|
||||
pub trait PubTrait {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self);
|
||||
|
||||
async fn async_fn(&self) {
|
||||
self.declare_async().await
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, sync(feature = "is_sync"))]
|
||||
async fn async_fn() {}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, sync(feature = "is_sync"))]
|
||||
pub async fn pub_async_fn() {}
|
||||
|
||||
struct Struct;
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, idents(Trait), sync(feature = "is_sync"))]
|
||||
impl Trait for Struct {
|
||||
fn sync_fn() {}
|
||||
|
||||
async fn declare_async(&self) {}
|
||||
|
||||
async fn async_fn(&self) {
|
||||
async { self.declare_async().await }.await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "is_sync")]
|
||||
fn main() -> std::result::Result<(), ()> {
|
||||
let s = Struct;
|
||||
s.declare_async();
|
||||
s.async_fn();
|
||||
async_fn();
|
||||
pub_async_fn();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "is_sync"))]
|
||||
#[async_std::main]
|
||||
async fn main() {}
|
||||
@@ -0,0 +1,37 @@
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
async fn async_fn() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(
|
||||
idents(async_fn),
|
||||
sync(feature = "is_sync", test),
|
||||
async(all(not(feature="is_sync"), feature = "async_std"), async_std::test),
|
||||
async(all(not(feature="is_sync"), feature = "tokio"), tokio::test)
|
||||
)]
|
||||
async fn test_async_fn() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync", test), async(not(feature = "is_sync"), async_std::test))]
|
||||
async fn test_async_fn2() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync", test), async(not(feature = "is_sync"), async_std::test))]
|
||||
async fn test_async_fn3() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync", test), async(not(feature = "is_sync"), async_std::test))]
|
||||
async fn test_async_fn4() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
#![allow(unused_imports)]
|
||||
use std::future::Future;
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
pub async fn with_fn<T, F: Sync + std::future::Future<Output = Result<(), ()>>>(
|
||||
test: T,
|
||||
) -> Result<(), ()>
|
||||
where
|
||||
T: FnOnce() -> F,
|
||||
{
|
||||
test().await
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
pub async fn with_fn_where<T, F>(test: T) -> Result<(), ()>
|
||||
where
|
||||
T: FnOnce() -> F,
|
||||
F: Sync + Future<Output = Result<(), ()>>,
|
||||
{
|
||||
test().await
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, sync(feature = "is_sync"))]
|
||||
fn main() {
|
||||
with_fn(|| Ok(())).unwrap();
|
||||
with_fn_where(|| Ok(())).unwrap();
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, async(not(feature = "is_sync")))]
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
with_fn(|| async { Ok(()) }).await.unwrap();
|
||||
with_fn_where(|| async { Ok(()) }).await.unwrap();
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#![allow(dead_code, unused_variables)]
|
||||
|
||||
/// InnerClient differ a lot between sync and async.
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(not(feature = "is_sync"), async_trait::async_trait))]
|
||||
trait Trait {
|
||||
async fn maybe_async_fn();
|
||||
}
|
||||
|
||||
/// The higher level API for end user.
|
||||
pub struct Struct;
|
||||
|
||||
/// Synchronous implementation, only compiles when `is_sync` feature is off.
|
||||
/// Else the compiler will complain that *request is defined multiple times* and
|
||||
/// blabla.
|
||||
#[maybe_async_cfg::maybe(keep_self, idents(Trait), sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
#[maybe_async::only_if(key="sync")]
|
||||
impl Trait for Struct {
|
||||
fn maybe_async_fn() { }
|
||||
}
|
||||
|
||||
/// Asynchronous implementation, only compiles when `is_sync` feature is off.
|
||||
#[maybe_async_cfg::maybe(keep_self, idents(Trait), sync(feature = "is_sync"), async(not(feature = "is_sync"), async_trait::async_trait))]
|
||||
#[maybe_async::only_if(key="async")]
|
||||
impl Trait for Struct {
|
||||
async fn maybe_async_fn() { }
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, idents(Trait, another_maybe_async_fn(fn)), sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
impl Struct {
|
||||
async fn another_maybe_async_fn() {
|
||||
<Self as Trait>::maybe_async_fn().await
|
||||
// When `is_sync` is toggle on, this block will compiles to:
|
||||
// Self::maybe_async_fn()
|
||||
}
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, idents(another_maybe_async_fn(fn)), sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
#[maybe_async::only_if(key="sync")]
|
||||
fn main() {
|
||||
let _ = Struct::another_maybe_async_fn();
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(keep_self, idents(another_maybe_async_fn(fn)), sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
#[maybe_async::only_if(key="async")]
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let _ = Struct::another_maybe_async_fn().await;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#[maybe_async_cfg::maybe(
|
||||
sync(feature = "is_sync"),
|
||||
)]
|
||||
async fn async_fn() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(
|
||||
idents(async_fn(fn)),
|
||||
sync(feature = "is_sync"),
|
||||
async(not(feature = "is_sync")),
|
||||
)]
|
||||
async fn test_async_fn() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error[E0425]: cannot find function `async_fn_async` in this scope
|
||||
--> tests/ui/test_fail/01-empty-test.rs:14:15
|
||||
|
|
||||
14 | let res = async_fn().await;
|
||||
| ^^^^^^^^ not found in this scope
|
||||
@@ -0,0 +1,21 @@
|
||||
#[maybe_async_cfg::maybe(
|
||||
sync(feature = "is_sync"),
|
||||
unknown(not(feature = "is_sync")),
|
||||
)]
|
||||
async fn async_fn() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(
|
||||
idents(async_fn(fn)),
|
||||
sync(feature = "is_sync"),
|
||||
async(not(feature = "is_sync")),
|
||||
)]
|
||||
async fn test_async_fn() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error[E0425]: cannot find function `async_fn_async` in this scope
|
||||
--> tests/ui/test_fail/02-unknown-path.rs:15:15
|
||||
|
|
||||
15 | let res = async_fn().await;
|
||||
| ^^^^^^^^ not found in this scope
|
||||
@@ -0,0 +1,23 @@
|
||||
#[maybe_async_cfg::maybe(
|
||||
sync(feature = "is_sync"),
|
||||
async(not(feature = "is_sync")),
|
||||
)]
|
||||
async fn async_fn() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
//#[maybe_async::test(feature="is_sync", async(feature="async", async_std::test, added))]
|
||||
#[maybe_async_cfg::maybe(
|
||||
idents(async_fn(fn)),
|
||||
sync(feature = "is_sync"),
|
||||
async(not(feature = "is_sync")),
|
||||
async(not(feature = "is_sync")),
|
||||
)]
|
||||
async fn test_async_fn() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
error[E0428]: the name `test_async_fn_async` is defined multiple times
|
||||
--> tests/ui/test_fail/03-async-gt2.rs:16:1
|
||||
|
|
||||
16 | async fn test_async_fn() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| `test_async_fn_async` redefined here
|
||||
| previous definition of the value `test_async_fn_async` here
|
||||
|
|
||||
= note: `test_async_fn_async` must be defined only once in the value namespace of this module
|
||||
@@ -0,0 +1,23 @@
|
||||
#[maybe_async_cfg::maybe(
|
||||
sync(feature = "is_sync"),
|
||||
async(not(feature = "is_sync")),
|
||||
)]
|
||||
async fn async_fn() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// bad sync condition
|
||||
//#[maybe_async::test(unknown(feature="async", async_std::test))]
|
||||
#[maybe_async_cfg::maybe(
|
||||
idents(async_fn(fn)),
|
||||
sync(unknown = "is_sync"),
|
||||
async(not(feature = "is_sync")),
|
||||
)]
|
||||
async fn test_async_fn() {
|
||||
let res = async_fn().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
error: Wrong name for name-value pair: unknown
|
||||
--> tests/ui/test_fail/04-bad-sync-cond.rs:13:10
|
||||
|
|
||||
13 | sync(unknown = "is_sync"),
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#[maybe_async_cfg::maybe(sync(feature = "is_sync"), async(not(feature = "is_sync")))]
|
||||
async fn some_function() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(
|
||||
idents(some_function(fn)),
|
||||
sync(feature = "is_sync", test),
|
||||
async(not(feature = "is_sync"), async_std::test)
|
||||
)]
|
||||
async fn test_async_fn() {
|
||||
let res = some_function().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(
|
||||
idents(some_function(fn)),
|
||||
sync(feature = "is_sync", test),
|
||||
async(not(feature = "is_sync"), tokio::test)
|
||||
)]
|
||||
async fn test_async_fn2() {
|
||||
let res = some_function().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(idents(some_function(fn)), sync(feature = "is_sync", test))]
|
||||
async fn test_async_fn3() {
|
||||
let res = some_function().await;
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
|
||||
#[maybe_async_cfg::maybe(idents(some_function(fn)), sync(feature = "is_sync", test))]
|
||||
async fn test_sync_fn() {
|
||||
let res = some_function();
|
||||
assert_eq!(res, true);
|
||||
}
|
||||
Reference in new issue
Block a user