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
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
|
||||
}
|
||||
Reference in new issue
Block a user