delegate-0.13.5/.cargo_vcs_info.json0000644000000001360000000000100126740ustar { "git": { "sha1": "b129d4cbf4cc499eff7743365dcb0fe5d8d2e820" }, "path_in_vcs": "" }delegate-0.13.5/Cargo.lock0000644000000217430000000000100106560ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "async-trait" version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "delegate" version = "0.13.5" dependencies = [ "async-trait", "futures", "macrotest", "proc-macro2", "quote", "syn", "tokio", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "futures" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-sink" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "glob" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "indexmap" version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "macrotest" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bf02346400dec0d7e4af0aa787c28acf174ce54a9c77f6507a1ee62e2aa2ca2" dependencies = [ "diff", "fastrand", "glob", "prettyplease", "serde", "serde_derive", "serde_json", "syn", "toml", ] [[package]] name = "memchr" version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "prettyplease" version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", ] [[package]] name = "proc-macro2" version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", "serde_core", ] [[package]] name = "serde_spanned" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" dependencies = [ "serde_core", ] [[package]] name = "slab" version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "syn" version = "2.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tokio" version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ "pin-project-lite", ] [[package]] name = "toml" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ "indexmap", "serde_core", "serde_spanned", "toml_datetime", "toml_parser", "toml_writer", "winnow", ] [[package]] name = "toml_datetime" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ "winnow", ] [[package]] name = "toml_writer" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] name = "winnow" version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" delegate-0.13.5/Cargo.toml0000644000000025640000000000100107010ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "delegate" version = "0.13.5" authors = [ "Godfrey Chan ", "Jakub Beránek ", ] build = false include = [ "src/*.rs", "Cargo.toml", "README.md", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Method delegation with less boilerplate" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/kobzol/rust-delegate" [lib] name = "delegate" path = "src/lib.rs" proc-macro = true [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "2" features = [ "full", "visit-mut", ] [dev-dependencies.async-trait] version = "0.1.50" [dev-dependencies.futures] version = "0.3.16" [dev-dependencies.macrotest] version = "1.0.12" [dev-dependencies.tokio] version = "1.16.1" features = ["sync"] delegate-0.13.5/Cargo.toml.orig000064400000000000000000000012041046102023000143500ustar 00000000000000[package] name = "delegate" description = "Method delegation with less boilerplate" version = "0.13.5" authors = ["Godfrey Chan ", "Jakub Beránek "] repository = "https://github.com/kobzol/rust-delegate" readme = "README.md" license = "MIT OR Apache-2.0" edition = "2018" include = [ "src/*.rs", "Cargo.toml", "README.md" ] [dependencies] syn = { version = "2", features = ["full", "visit-mut"] } quote = "1" proc-macro2 = "1" [lib] proc-macro = true [dev-dependencies] async-trait = "0.1.50" futures = "0.3.16" tokio = { version = "1.16.1", features = ["sync"] } macrotest = "1.0.12" delegate-0.13.5/README.md000064400000000000000000000311571046102023000127520ustar 00000000000000# Method delegation with less boilerplate [![Build Status](https://github.com/kobzol/rust-delegate/workflows/Tests/badge.svg)](https://github.com/kobzol/rust-delegate/actions) [![Crates.io](https://img.shields.io/crates/v/delegate.svg)](https://crates.io/crates/delegate) This crate removes some boilerplate for structs that simply delegate some of their methods to one or more of their fields. It gives you the `delegate!` macro, which delegates method calls to selected expressions (usually inner fields). ## Example: A Stack data structure implemented using an inner Vec via delegation. ```rust use delegate::delegate; #[derive(Clone, Debug)] struct Stack { inner: Vec, } impl Stack { pub fn new() -> Self { Self { inner: vec![] } } delegate! { to self.inner { pub fn is_empty(&self) -> bool; pub fn push(&mut self, value: T); pub fn pop(&mut self) -> Option; pub fn clear(&mut self); #[call(len)] pub fn size(&self) -> usize; #[call(last)] pub fn peek(&self) -> Option<&T>; } } } ``` ## Features ### Delegate to a method with a different name ```rust struct Stack { inner: Vec } impl Stack { delegate! { to self.inner { #[call(push)] pub fn add(&mut self, value: u32); } } } ``` ### Use an arbitrary inner field expression ```rust struct Wrapper { inner: Rc>> } impl Wrapper { delegate! { to self.inner.deref().borrow_mut() { pub fn push(&mut self, val: u32); } } } ``` ### Delegate to enum variants ```rust use delegate::delegate; enum Enum { A(A), B(B), C { v: C }, } struct A { val: usize, } impl A { fn dbg_inner(&self) -> usize { dbg!(self.val); 1 } } struct B { val_a: String, } impl B { fn dbg_inner(&self) -> usize { dbg!(self.val_a.clone()); 2 } } struct C { val_c: f64, } impl C { fn dbg_inner(&self) -> usize { dbg!(self.val_c); 3 } } impl Enum { delegate! { // transformed to // // ```rust // match self { // Enum::A(a) => a.dbg_inner(), // Enum::B(b) => { println!("i am b"); b }.dbg_inner(), // Enum::C { v: c } => { c }.dbg_inner(), // } // ``` to match self { Enum::A(a) => a, Enum::B(b) => { println!("i am b"); b }, Enum::C { v: c } => { c }, } { fn dbg_inner(&self) -> usize; } } } ``` ### Use modifiers that alter the generated method body ```rust use delegate::delegate; struct Inner; impl Inner { pub fn method(&self, num: u32) -> u32 { num } pub fn method_res(&self, num: u32) -> Result { Ok(num) } } struct Wrapper { inner: Inner } impl Wrapper { delegate! { to self.inner { // calls method, converts result to u64 using `From` #[into] pub fn method(&self, num: u32) -> u64; // calls method, returns () #[call(method)] pub fn method_noreturn(&self, num: u32); // calls method, converts result to i6 using `TryFrom` #[try_into] #[call(method)] pub fn method2(&self, num: u32) -> Result; // calls method_res, unwraps the result #[unwrap] pub fn method_res(&self, num: u32) -> u32; // calls method_res, unwraps the result, then calls into #[unwrap] #[into] #[call(method_res)] pub fn method_res_into(&self, num: u32) -> u64; // specify explicit type for into #[into(u64)] #[call(method)] pub fn method_into_explicit(&self, num: u32) -> u64; } } } ``` ### Custom called expression The `#[expr()]` attribute can be used to modify the delegated call. You can use the `$` sigil as a placeholder for what delegate would normally expand to, and wrap that expression with custom code. _Note:_ the `$` placeholder isn't required and can be present multiple times if you want. ```rust struct A(Vec); impl A { delegate! { to self.0 { #[expr(*$.unwrap())] /// Here `$` == `self.0.get(idx)` /// Will expand to `*self.0.get(idx).unwrap()` fn get(&self, idx: usize) -> u8; #[call(get)] #[expr($?.checked_pow(2))] /// Here `$` == `self.0.get(idx)` /// Will expand to `self.0.get(idx)?.checked_pow(2)` fn get_checked_pow_2(&self, idx: usize) -> Option; } } } ``` ### Add additional arguments to method ```rust struct Inner(u32); impl Inner { pub fn new(m: u32) -> Self { // some "very complex" constructing work Self(m) } pub fn method(&self, n: u32) -> u32 { self.0 + n } } struct Wrapper { inner: OnceCell, } impl Wrapper { pub fn new() -> Self { Self { inner: OnceCell::new(), } } fn content(&self, val: u32) -> &Inner { self.inner.get_or_init(|| Inner(val)) } delegate! { to |k: u32| self.content(k) { // `wrapper.method(k, num)` will call `self.content(k).method(num)` pub fn method(&self, num: u32) -> u32; } } } ``` ### Call `await` on async functions ```rust struct Inner; impl Inner { pub async fn method(&self, num: u32) -> u32 { num } } struct Wrapper { inner: Inner } impl Wrapper { delegate! { to self.inner { // calls method(num).await, returns impl Future pub async fn method(&self, num: u32) -> u32; // calls method(num).await.into(), returns impl Future #[into] #[call(method)] pub async fn method_into(&self, num: u32) -> u64; } } } ``` You can use the `#[await(true/false)]` attribute on delegated methods to specify if `.await` should be generated after the delegated expression. It will be generated by default if the delegated method is `async`. ### Delegate to multiple fields ```rust struct MultiStack { left: Vec, right: Vec, } impl MultiStack { delegate! { to self.left { /// Push an item to the top of the left stack #[call(push)] pub fn push_left(&mut self, value: u32); } to self.right { /// Push an item to the top of the right stack #[call(push)] pub fn push_right(&mut self, value: u32); } } } ``` ### Inline attributes `rust-delegate` inserts `#[inline(always)]` automatically. You can override that decision by specifying `#[inline]` manually on the delegated method. ### Segment attributes You can use an attribute on a whole delegation segment to automatically apply it to all methods in that segment: ```rust struct Wrapper { inner: Inner } impl Wrapper { delegate! { #[unwrap] to self.inner { fn foo(&self) -> u32; // calls self.inner.foo().unwrap() fn bar(&self) -> u32; // calls self.inner.bar().unwrap() } } } ``` ### Adding additional arguments You can specify expressions in the signature that will be used as delegated arguments: ```rust use delegate::delegate; struct Inner; impl Inner { pub fn polynomial(&self, a: i32, x: i32, b: i32, y: i32, c: i32) -> i32 { a + x * x + b * y + c } } struct Wrapper { inner: Inner, a: i32, b: i32, c: i32 } impl Wrapper { delegate! { to self.inner { // Calls `polynomial` on `inner` with `self.a`, `self.b` and // `self.c` passed as arguments `a`, `b`, and `c`, effectively // calling `polynomial(self.a, x, self.b, y, self.c)`. pub fn polynomial(&self, [ self.a ], x: i32, [ self.b ], y: i32, [ self.c ]) -> i32 ; // Calls `polynomial` on `inner` with `0`s passed for arguments // `a` and `x`, and `self.b` and `self.c` for `b` and `c`, // effectively calling `polynomial(0, 0, self.b, y, self.c)`. #[call(polynomial)] pub fn linear(&self, [ 0 ], [ 0 ], [ self.b ], y: i32, [ self.c ]) -> i32 ; } } } ``` ### Parameter modifiers You can modify how will an input parameter be passed to the delegated method with parameter attribute modifiers. Currently, the following modifiers are supported: - `#[into]`: Calls `.into()` on the parameter passed to the delegated method. - `#[as_ref]`: Calls `.as_ref()` on the parameter passed to the delegated method. - `#[newtype]`: Accesses the first tuple element (`.0`) of the parameter passed to the delegated method. > Note that these modifiers might be removed in the future, try to use the more general `#[expr]` mechanism to achieve this functionality. ```rust use delegate::delegate; struct InnerType {} impl InnerType { fn foo(&self, other: Self) {} } impl From for InnerType { fn from(wrapper: Wrapper) -> Self { wrapper.0 } } struct Wrapper(InnerType); impl Wrapper { delegate! { to self.0 { // Calls `self.0.foo(other.into());` pub fn foo(&self, #[into] other: Self); // Calls `self.0.bar(other.0);` pub fn bar(&self, #[newtype] other: Self); } } } ``` ### Delegate associated functions ```rust use delegate::delegate; struct A {} impl A { fn foo(a: u32) -> u32 { a + 1 } } struct B; impl B { delegate! { to A { fn foo(a: u32) -> u32; } } } assert_eq!(B::foo(1), 2); ``` ### Delegate associated constants ```rust use delegate::delegate; trait WithConst { const TOTO: u8; } struct A; impl WithConst for A { const TOTO: u8 = 1; } struct B; impl WithConst for B { const TOTO: u8 = 2; } struct C; impl WithConst for C { const TOTO: u8 = 2; } enum Enum { A(A), B(B), C(C), } impl Enum { delegate! { to match self { Self::A(a) => a, Self::B(b) => b, Self::C(c) => { println!("hello from c"); c }, } { #[const(WithConst::TOTO)] fn get_toto(&self) -> u8; } } } assert_eq!(Enum::A(A).get_toto(), ::TOTO); ``` ### Delegate to fields ```rust use delegate::delegate; struct Datum { value: u32, error: u32, } struct DatumWrapper(Datum); impl DatumWrapper { delegate! { to self.0 { /// Get the value of a nested field with the same name #[field] fn value(&self) -> u32; /// Get the value of a nested field with a different name #[field(value)] fn renamed_value(&self) -> u32; /// Get shared reference to a nested field #[field(&value)] fn value_ref(&self) -> &u32; /// Get mutable reference to a nested field #[field(&mut value)] fn value_ref_mut(&mut self) -> &mut u32; /// Get mutable reference to a nested field with the same name #[field(&)] fn error(&self) -> &u32; } } } ``` ## Development This project uses a standard test suite for quality control, as well as a set of "expansion" tests that utilize the `macrotest` crate to ensure the macro expands as expected. PRs implementing new features should add both standard and expansion tests where appropriate. To add an expansion test, place a Rust source file in the `tests/expand/` directory with methods demonstrating the new feature. Next, run `cargo test` to run the test suite and generate a `*.expanded.rs` file in the same directory. Next, carefully inspect the contents of the generated file to confirm that all methods expanded as expected. Finally, commit both files to the git repository. Future test suite runs will now include expanding the source file and comparing it to the expanded file. ## License Licensed under either of - Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. ## Conduct Please follow the [Rust Code of Conduct]. For escalation or moderation issues please contact the crate author(s) listed in [`Cargo.toml`](./Cargo.toml). [Rust Code of Conduct]: https://www.rust-lang.org/conduct.html delegate-0.13.5/src/attributes.rs000064400000000000000000000453501046102023000150160ustar 00000000000000use proc_macro2::{Delimiter, TokenStream, TokenTree}; use quote::ToTokens; use std::collections::VecDeque; use std::ops::Not; use syn::parse::ParseStream; use syn::{Attribute, Error, Meta, Path, PathSegment, Token, TypePath}; pub struct CallMethodAttribute { name: syn::Ident, } impl syn::parse::Parse for CallMethodAttribute { fn parse(input: ParseStream) -> Result { Ok(CallMethodAttribute { name: input.parse()?, }) } } #[derive(Default, Clone)] pub struct GetFieldAttribute { reference: Option<(Token![&], Option)>, member: Option, } impl GetFieldAttribute { pub fn reference_tokens(&self) -> Option { let (ref_, mut_) = self.reference.as_ref()?; let mut tokens = ref_.to_token_stream(); mut_.to_tokens(&mut tokens); Some(tokens) } } impl syn::parse::Parse for GetFieldAttribute { fn parse(input: ParseStream) -> Result { let mut reference = None; if let Ok(ref_) = input.parse::() { reference = Some((ref_, None)); } if let Some((_, mut_)) = &mut reference { *mut_ = input.parse::().ok(); } let member = input.is_empty().not().then(|| input.parse()).transpose()?; Ok(GetFieldAttribute { reference, member }) } } struct GenerateAwaitAttribute { literal: syn::LitBool, } impl syn::parse::Parse for GenerateAwaitAttribute { fn parse(input: ParseStream) -> Result { Ok(GenerateAwaitAttribute { literal: input.parse()?, }) } } struct IntoAttribute { type_path: Option, } impl syn::parse::Parse for IntoAttribute { fn parse(input: ParseStream) -> Result { let type_path: TypePath = input.parse().map_err(|error| { Error::new( input.span(), format!("{error}\nExpected type name, e.g. #[into(u32)]"), ) })?; Ok(IntoAttribute { type_path: Some(type_path), }) } } pub struct AssociatedConstant { pub const_name: PathSegment, pub trait_path: Path, } impl syn::parse::Parse for AssociatedConstant { fn parse(input: ParseStream) -> Result { let mut path = input.parse::().map_err(|error| { Error::new( input.span(), format!( "{error}\nExpected const path, e.g. #[const(path::to::MyTrait::CONST_NAME)]" ), ) })?; let const_name = path.segments.pop().ok_or_else(|| { Error::new_spanned( &path, "Expected a path. e.g. #[const(path::to::MyTrait::CONST_NAME)]", ) })?; // poping a segment leads to trailing `::` path.segments.pop_punct().ok_or_else(|| { Error::new_spanned( &path, "Expected a multipart path. e.g. #[const(path::to::MyTrait::CONST_NAME)]", ) })?; Ok(Self { const_name: const_name.into_value(), trait_path: path, }) } } #[derive(Clone)] /// Represent the placeholder `$` found inside an expr attribute's template pub struct ExprPlaceHolder; impl syn::parse::Parse for ExprPlaceHolder { fn parse(input: ParseStream) -> syn::Result { input.parse::()?; Ok(Self) } } /// Kind of allowed placeholders in an `expr` attribute template #[derive(Clone)] enum Placeholder { ExprPlaceholder(ExprPlaceHolder), } #[derive(Clone)] /// Tokens found in the expr attribute's template /// Token are either /// - a replacable pattern (placeholder) /// - a normal token /// - a group containing a recursive representation of template tokens enum TemplateToken { Normal(TokenTree), Placeholder(Placeholder), Group(Delimiter, TemplateExpr), } impl TemplateToken { /// Replace relevant placeholder tokens with the provided tokens fn replace(&self, replacement: &TokenStream) -> TokenStream { match self { Self::Group(del, template) => { let replaced_tokens = template .tokens .iter() .map(|token| token.replace(replacement)); proc_macro2::Group::new(*del, quote::quote! { #(#replaced_tokens)* }) .to_token_stream() } Self::Normal(token_tree) => token_tree.to_token_stream(), Self::Placeholder(_) => replacement.clone(), } } } #[derive(Clone)] /// An expr attribute's template pub struct TemplateExpr { tokens: Vec, } impl syn::parse::Parse for TemplateExpr { /// Parsing a template means storing the raw template while differenciating /// placeholders and "normal" tokens fn parse(input: ParseStream) -> syn::Result { let mut tokens = Vec::new(); while !input.is_empty() { if input.fork().parse::().is_ok() { let placeholder = input.parse()?; tokens.push(TemplateToken::Placeholder(Placeholder::ExprPlaceholder( placeholder, ))); continue; } match input.parse()? { TokenTree::Group(group) => { let inner_stream = group.stream(); let inner_expr = syn::parse2(inner_stream)?; tokens.push(TemplateToken::Group(group.delimiter(), inner_expr)); } other => { tokens.push(TemplateToken::Normal(other)); } } } Ok(TemplateExpr { tokens }) } } impl TemplateExpr { /// returns the template after expanding the relevant placeholders pub fn expand_template(&self, replacement: &TokenStream) -> TokenStream { self.tokens.iter().fold(TokenStream::new(), |mut ts, tok| { ts.extend(tok.replace(replacement)); ts }) } } pub struct TraitTarget { type_path: TypePath, } impl syn::parse::Parse for TraitTarget { fn parse(input: ParseStream) -> Result { let type_path: TypePath = input.parse().map_err(|error| { Error::new( input.span(), format!("{error}\nExpected trait path, e.g. #[through(foo::MyTrait)]"), ) })?; Ok(TraitTarget { type_path }) } } #[derive(Clone)] pub enum ReturnExpression { Into(Option), TryInto, Unwrap, } pub enum TargetSpecifier { Field(GetFieldAttribute), Method(CallMethodAttribute), } impl TargetSpecifier { pub fn get_member(&self, default: &syn::Ident) -> syn::Member { match self { Self::Field(GetFieldAttribute { member: Some(member), .. }) => member.clone(), Self::Field(_) => default.clone().into(), Self::Method(method) => method.name.clone().into(), } } } enum ParsedAttribute { ReturnExpression(ReturnExpression), Await(bool), TargetSpecifier(TargetSpecifier), ThroughTrait(TraitTarget), ConstantAccess(AssociatedConstant), Expr(TemplateExpr), } fn parse_attributes( attrs: &[Attribute], ) -> ( impl Iterator + '_, impl Iterator, ) { let (parsed, other): (Vec<_>, Vec<_>) = attrs .iter() .map(|attribute| { let parsed = if let syn::AttrStyle::Outer = attribute.style { let name = attribute .path() .get_ident() .map(|i| i.to_string()) .unwrap_or_default(); match name.as_str() { "call" => { let target = attribute .parse_args::() .expect("Cannot parse `call` attribute"); let spec = TargetSpecifier::Method(target); Some(ParsedAttribute::TargetSpecifier(spec)) } "field" => { let target = if let syn::Meta::Path(_) = &attribute.meta { GetFieldAttribute::default() } else { attribute .parse_args::() .expect("Cannot parse `field` attribute") }; let spec = TargetSpecifier::Field(target); Some(ParsedAttribute::TargetSpecifier(spec)) } "into" => { let into = match &attribute.meta { Meta::NameValue(_) => { panic!("Cannot parse `into` attribute: expected parentheses") } Meta::Path(_) => IntoAttribute { type_path: None }, Meta::List(meta) => meta .parse_args::() .expect("Cannot parse `into` attribute"), }; Some(ParsedAttribute::ReturnExpression(ReturnExpression::Into( into.type_path, ))) } "try_into" => { if let Meta::List(meta) = &attribute.meta { meta.parse_nested_meta(|meta| { if meta.path.is_ident("unwrap") { panic!( "Replace #[try_into(unwrap)] with\n#[try_into]\n#[unwrap]", ); } Ok(()) }) .expect("Invalid `try_into` arguments"); } Some(ParsedAttribute::ReturnExpression(ReturnExpression::TryInto)) } "unwrap" => Some(ParsedAttribute::ReturnExpression(ReturnExpression::Unwrap)), "await" => { let generate = attribute .parse_args::() .expect("Cannot parse `await` attribute"); Some(ParsedAttribute::Await(generate.literal.value)) } "through" => Some(ParsedAttribute::ThroughTrait( attribute .parse_args::() .expect("Cannot parse `through` attribute"), )), "const" => Some(ParsedAttribute::ConstantAccess( attribute .parse_args::() .expect("Cannot parse `const` attribute"), )), "expr" => Some(ParsedAttribute::Expr( attribute .parse_args::() .expect("Cannot parse `expr` attribute"), )), _ => None, } } else { None }; (parsed, attribute) }) .partition(|(parsed, _)| parsed.is_some()); ( parsed.into_iter().map(|(parsed, _)| parsed.unwrap()), other.into_iter().map(|(_, attr)| attr), ) } pub struct MethodAttributes<'a> { pub attributes: Vec<&'a Attribute>, pub target_specifier: Option, pub expressions: VecDeque, pub generate_await: Option, pub target_trait: Option, pub associated_constant: Option, pub expr_attr: Option, } /// Iterates through the attributes of a method and filters special attributes. /// - call => sets the name of the target method to call /// - into => generates a `into()` call after the delegated expression /// - try_into => generates a `try_into()` call after the delegated expression /// - await => generates an `.await` expression after the delegated expression /// - unwrap => generates a `unwrap()` call after the delegated expression /// - through => generates a UFCS call (`Target::method(&, ...)`) around the delegated expression /// - const => generates a getter to a trait associated constant pub fn parse_method_attributes<'a>( attrs: &'a [Attribute], method: &syn::TraitItemFn, ) -> MethodAttributes<'a> { let mut target_spec: Option = None; let mut expressions: Vec = vec![]; let mut generate_await: Option = None; let mut target_trait: Option = None; let mut associated_constant: Option = None; let mut expr_attr: Option = None; let (parsed, other) = parse_attributes(attrs); for attr in parsed { match attr { ParsedAttribute::ReturnExpression(expr) => expressions.push(expr), ParsedAttribute::Await(value) => { if generate_await.is_some() { panic!( "Multiple `await` attributes specified for {}", method.sig.ident ) } generate_await = Some(value); } ParsedAttribute::TargetSpecifier(spec) => { if target_spec.is_some() { panic!( "Multiple field/call attributes specified for {}", method.sig.ident ) } target_spec = Some(spec); } ParsedAttribute::ThroughTrait(target) => { if target_trait.is_some() { panic!( "Multiple through attributes specified for {}", method.sig.ident ) } target_trait = Some(target); } ParsedAttribute::ConstantAccess(const_attr) => { if associated_constant.is_some() { panic!( "Multiple const attributes specified for {}", method.sig.ident ) } associated_constant = Some(const_attr); } ParsedAttribute::Expr(token_tree) => { if expr_attr.is_some() { panic!( "Multiple expr attributes specified for {}", method.sig.ident ) } expr_attr = Some(token_tree); } } } if associated_constant.is_some() && target_spec.is_some() { panic!("Cannot use both `call`/`field` and `const` attributes."); } MethodAttributes { attributes: other.into_iter().collect(), target_specifier: target_spec, generate_await, expressions: expressions.into(), target_trait: target_trait.map(|t| t.type_path), associated_constant, expr_attr, } } pub struct SegmentAttributes { pub expressions: Vec, pub generate_await: Option, pub target_trait: Option, pub other_attrs: Vec, pub expr_attr: Option, } pub fn parse_segment_attributes(attrs: &[Attribute]) -> SegmentAttributes { let mut expressions: Vec = vec![]; let mut generate_await: Option = None; let mut target_trait: Option = None; let mut expr_attr: Option = None; let (parsed, other) = parse_attributes(attrs); for attribute in parsed { match attribute { ParsedAttribute::ReturnExpression(expr) => expressions.push(expr), ParsedAttribute::Await(value) => { if generate_await.is_some() { panic!("Multiple `await` attributes specified for segment"); } generate_await = Some(value); } ParsedAttribute::ThroughTrait(target) => { if target_trait.is_some() { panic!("Multiple `through` attributes specified for segment"); } target_trait = Some(target); } ParsedAttribute::TargetSpecifier(_) => { panic!("Field/call attribute cannot be specified on a `to ` segment."); } ParsedAttribute::ConstantAccess(_) => { panic!("Const attribute cannot be specified on a `to ` segment."); } ParsedAttribute::Expr(token_tree) => { if expr_attr.is_some() { panic!("Multiple `expr` attributes specified for segment"); } expr_attr = Some(token_tree); } } } SegmentAttributes { expressions, generate_await, target_trait: target_trait.map(|t| t.type_path), other_attrs: other.cloned().collect::>(), expr_attr, } } /// Applies default values from the segment and adds them to the method attributes. pub fn combine_attributes<'a>( mut method_attrs: MethodAttributes<'a>, segment_attrs: &'a SegmentAttributes, ) -> MethodAttributes<'a> { let SegmentAttributes { expressions, generate_await, target_trait, other_attrs, expr_attr, } = segment_attrs; if method_attrs.generate_await.is_none() { method_attrs.generate_await = *generate_await; } if method_attrs.target_trait.is_none() { method_attrs.target_trait.clone_from(target_trait); } if method_attrs.expr_attr.is_none() { method_attrs.expr_attr.clone_from(expr_attr); } for expr in expressions { match expr { ReturnExpression::Into(path) => { if !method_attrs .expressions .iter() .any(|expr| matches!(expr, ReturnExpression::Into(_))) { method_attrs .expressions .push_front(ReturnExpression::Into(path.clone())); } } _ => method_attrs.expressions.push_front(expr.clone()), } } for other_attr in other_attrs { if !method_attrs .attributes .iter() .any(|attr| attr.path().get_ident() == other_attr.path().get_ident()) { method_attrs.attributes.push(other_attr); } } method_attrs } delegate-0.13.5/src/lib.rs000064400000000000000000001202231046102023000133670ustar 00000000000000//! This crate removes some boilerplate for structs that simply delegate //! some of their methods to one or more of their fields. //! //! It gives you the `delegate!` macro, which delegates method calls to selected expressions (usually inner fields). //! //! ## Features: //! - Delegate to a method with a different name //! ```rust //! use delegate::delegate; //! //! struct Stack { inner: Vec } //! impl Stack { //! delegate! { //! to self.inner { //! #[call(push)] //! pub fn add(&mut self, value: u32); //! } //! } //! } //! ``` //! - Use an arbitrary inner field expression //! ```rust //! use delegate::delegate; //! //! use std::rc::Rc; //! use std::cell::RefCell; //! use std::ops::Deref; //! //! struct Wrapper { inner: Rc>> } //! impl Wrapper { //! delegate! { //! to self.inner.deref().borrow_mut() { //! pub fn push(&mut self, val: u32); //! } //! } //! } //! ``` //! //! - Delegate to enum variants //! //! ```rust //! use delegate::delegate; //! //! enum Enum { //! A(A), //! B(B), //! C { v: C }, //! } //! //! struct A { //! val: usize, //! } //! //! impl A { //! fn dbg_inner(&self) -> usize { //! dbg!(self.val); //! 1 //! } //! } //! struct B { //! val_a: String, //! } //! //! impl B { //! fn dbg_inner(&self) -> usize { //! dbg!(self.val_a.clone()); //! 2 //! } //! } //! //! struct C { //! val_c: f64, //! } //! //! impl C { //! fn dbg_inner(&self) -> usize { //! dbg!(self.val_c); //! 3 //! } //! } //! //! impl Enum { //! delegate! { //! // transformed to //! // //! // ```rust //! // match self { //! // Enum::A(a) => a.dbg_inner(), //! // Enum::B(b) => { println!("i am b"); b }.dbg_inner(), //! // Enum::C { v: c } => { c }.dbg_inner(), //! // } //! // ``` //! to match self { //! Enum::A(a) => a, //! Enum::B(b) => { println!("i am b"); b }, //! Enum::C { v: c } => { c }, //! } { //! fn dbg_inner(&self) -> usize; //! } //! } //! } //! ``` //! //! - Use modifiers that alter the generated method body //! ```rust //! use delegate::delegate; //! struct Inner; //! impl Inner { //! pub fn method(&self, num: u32) -> u32 { num } //! pub fn method_res(&self, num: u32) -> Result { Ok(num) } //! } //! struct Wrapper { inner: Inner } //! impl Wrapper { //! delegate! { //! to self.inner { //! // calls method, converts result to u64 using `From` //! #[into] //! pub fn method(&self, num: u32) -> u64; //! //! // calls method, returns () //! #[call(method)] //! pub fn method_noreturn(&self, num: u32); //! //! // calls method, converts result to i6 using `TryFrom` //! #[try_into] //! #[call(method)] //! pub fn method2(&self, num: u32) -> Result; //! //! // calls method_res, unwraps the result //! #[unwrap] //! pub fn method_res(&self, num: u32) -> u32; //! //! // calls method_res, unwraps the result, then calls into //! #[unwrap] //! #[into] //! #[call(method_res)] //! pub fn method_res_into(&self, num: u32) -> u64; //! //! // specify explicit type for into //! #[into(u64)] //! #[call(method)] //! pub fn method_into_explicit(&self, num: u32) -> u64; //! } //! } //! } //! ``` //! //! - Custom called expression //! //! The `#[expr()]` attribute can be used to modify the delegated call. You can use the `$` sigil as a placeholder for what delegate would normally expand to, and wrap that expression with custom code. //! //! _Note:_ the `$` placeholder isn't required and can be present multiple times if you want. //! //! ```rs //! struct A(Vec); //! //! impl A { //! delegate! { //! to self.0 { //! #[expr(*$.unwrap())] //! /// Here `$` == `self.0.get(idx)` //! /// Will expand to `*self.0.get(idx).unwrap()` //! fn get(&self, idx: usize) -> u8; //! //! #[call(get)] //! #[expr($?.checked_pow(2))] //! /// Here `$` == `self.0.get(idx)` //! /// Will expand to `self.0.get(idx)?.checked_pow(2)` //! fn get_checked_pow_2(&self, idx: usize) -> Option; //! } //! } //! } //! ``` //! //! - Call `await` on async functions //! ```rust //! use delegate::delegate; //! //! struct Inner; //! impl Inner { //! pub async fn method(&self, num: u32) -> u32 { num } //! } //! struct Wrapper { inner: Inner } //! impl Wrapper { //! delegate! { //! to self.inner { //! // calls method(num).await, returns impl Future //! pub async fn method(&self, num: u32) -> u32; //! //! // calls method(num).await.into(), returns impl Future //! #[into] //! #[call(method)] //! pub async fn method_into(&self, num: u32) -> u64; //! } //! } //! } //! ``` //! You can use the `#[await(true/false)]` attribute on delegated methods to specify if `.await` should //! be generated after the delegated expression. It will be generated by default if the delegated //! method is `async`. //! - Delegate to multiple fields //! ```rust //! use delegate::delegate; //! //! struct MultiStack { //! left: Vec, //! right: Vec, //! } //! impl MultiStack { //! delegate! { //! to self.left { //! // Push an item to the top of the left stack //! #[call(push)] //! pub fn push_left(&mut self, value: u32); //! } //! to self.right { //! // Push an item to the top of the right stack //! #[call(push)] //! pub fn push_right(&mut self, value: u32); //! } //! } //! } //! ``` //! - Inserts `#[inline(always)]` automatically (unless you specify `#[inline]` manually on the method) //! - You can use an attribute on a whole segment to automatically apply it to all methods in that //! segment: //! ```rust //! use delegate::delegate; //! //! struct Inner; //! //! impl Inner { //! fn foo(&self) -> Result { Ok(0) } //! fn bar(&self) -> Result { Ok(1) } //! } //! //! struct Wrapper { inner: Inner } //! //! impl Wrapper { //! delegate! { //! #[unwrap] //! to self.inner { //! fn foo(&self) -> u32; // calls self.inner.foo().unwrap() //! fn bar(&self) -> u32; // calls self.inner.bar().unwrap() //! } //! } //! } //! ``` //! - Specify expressions in the signature that will be used as delegated arguments //! ```rust //! use delegate::delegate; //! struct Inner; //! impl Inner { //! pub fn polynomial(&self, a: i32, x: i32, b: i32, y: i32, c: i32) -> i32 { //! a + x * x + b * y + c //! } //! } //! struct Wrapper { inner: Inner, a: i32, b: i32, c: i32 } //! impl Wrapper { //! delegate! { //! to self.inner { //! // Calls `polynomial` on `inner` with `self.a`, `self.b` and //! // `self.c` passed as arguments `a`, `b`, and `c`, effectively //! // calling `polynomial(self.a, x, self.b, y, self.c)`. //! pub fn polynomial(&self, [ self.a ], x: i32, [ self.b ], y: i32, [ self.c ]) -> i32 ; //! // Calls `polynomial` on `inner` with `0`s passed for arguments //! // `a` and `x`, and `self.b` and `self.c` for `b` and `c`, //! // effectively calling `polynomial(0, 0, self.b, y, self.c)`. //! #[call(polynomial)] //! pub fn linear(&self, [ 0 ], [ 0 ], [ self.b ], y: i32, [ self.c ]) -> i32 ; //! } //! } //! } //! ``` //! - Modify how will an input parameter be passed to the delegated method with parameter attribute modifiers. //! Currently, the following modifiers are supported: //! - `#[into]`: Calls `.into()` on the parameter passed to the delegated method. //! - `#[as_ref]`: Calls `.as_ref()` on the parameter passed to the delegated method. //! - `#[newtype]`: Calls `.0` on the parameter passed to the delegated method. //! ```rust //! use delegate::delegate; //! //! struct InnerType {} //! impl InnerType { //! fn foo(&self, other: Self) {} //! } //! //! impl From for InnerType { //! fn from(wrapper: Wrapper) -> Self { //! wrapper.0 //! } //! } //! //! struct Wrapper(InnerType); //! impl Wrapper { //! delegate! { //! to self.0 { //! // Calls `self.0.foo(other.into());` //! pub fn foo(&self, #[into] other: Self); //! } //! } //! } //! ``` //! - Specify a trait through which will the delegated method be called //! (using [UFCS](https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls). //! ```rust //! use delegate::delegate; //! //! struct InnerType {} //! impl InnerType { //! //! } //! //! trait MyTrait { //! fn foo(&self); //! } //! impl MyTrait for InnerType { //! fn foo(&self) {} //! } //! //! struct Wrapper(InnerType); //! impl Wrapper { //! delegate! { //! to &self.0 { //! // Calls `MyTrait::foo(&self.0)` //! #[through(MyTrait)] //! pub fn foo(&self); //! } //! } //! } //! ``` //! //! - Add additional arguments to method //! //! ```rust //! use delegate::delegate; //! use std::cell::OnceCell; //! struct Inner(u32); //! impl Inner { //! pub fn new(m: u32) -> Self { //! // some "very complex" constructing work //! Self(m) //! } //! pub fn method(&self, n: u32) -> u32 { //! self.0 + n //! } //! } //! //! struct Wrapper { //! inner: OnceCell, //! } //! //! impl Wrapper { //! pub fn new() -> Self { //! Self { //! inner: OnceCell::new(), //! } //! } //! fn content(&self, val: u32) -> &Inner { //! self.inner.get_or_init(|| Inner(val)) //! } //! delegate! { //! to |k: u32| self.content(k) { //! // `wrapper.method(k, num)` will call `self.content(k).method(num)` //! pub fn method(&self, num: u32) -> u32; //! } //! } //! } //! ``` //! - Delegate associated functions //! ```rust //! use delegate::delegate; //! //! struct A {} //! impl A { //! fn foo(a: u32) -> u32 { //! a + 1 //! } //! } //! //! struct B; //! //! impl B { //! delegate! { //! to A { //! fn foo(a: u32) -> u32; //! } //! } //! } //! //! assert_eq!(B::foo(1), 2); //! ``` //! - Delegate associated constants //! //! ```rust //! use delegate::delegate; //! //! trait WithConst { //! const TOTO: u8; //! } //! //! struct A; //! impl WithConst for A { //! const TOTO: u8 = 1; //! } //! //! struct B; //! impl WithConst for B { //! const TOTO: u8 = 2; //! } //! struct C; //! impl WithConst for C { //! const TOTO: u8 = 2; //! } //! //! enum Enum { //! A(A), //! B(B), //! C(C), //! } //! //! impl Enum { //! delegate! { //! to match self { //! Self::A(a) => a, //! Self::B(b) => b, //! Self::C(c) => { println!("hello from c"); c }, //! } { //! #[const(WithConst::TOTO)] //! fn get_toto(&self) -> u8; //! } //! } //! } //! //! assert_eq!(Enum::A(A).get_toto(), ::TOTO); //! ``` //! //! - Delegate to fields //! ```rust //! use delegate::delegate; //! //! struct Datum { //! value: u32, //! error: u32, //! } //! //! struct DatumWrapper(Datum); //! //! impl DatumWrapper { //! delegate! { //! to self.0 { //! /// Get the value of a nested field with the same name //! #[field] //! fn value(&self) -> u32; //! //! /// Get the value of a nested field with a different name //! #[field(value)] //! fn renamed_value(&self) -> u32; //! //! /// Get shared reference to a nested field //! #[field(&value)] //! fn value_ref(&self) -> &u32; //! //! /// Get mutable reference to a nested field //! #[field(&mut value)] //! fn value_ref_mut(&mut self) -> &mut u32; //! //! /// Get mutable reference to a nested field with the same name //! #[field(&)] //! fn error(&self) -> &u32; //! } //! } //! } //! ``` extern crate proc_macro; use std::mem; use attributes::AssociatedConstant; use proc_macro::TokenStream; use proc_macro2::Ident; use quote::{quote, ToTokens}; use syn::parse::{Parse, ParseStream}; use syn::spanned::Spanned; use syn::visit_mut::VisitMut; use syn::{parse_quote, Error, Expr, ExprField, ExprMethodCall, FnArg, GenericParam, Meta}; use crate::attributes::{ combine_attributes, parse_method_attributes, parse_segment_attributes, ReturnExpression, SegmentAttributes, TargetSpecifier, }; mod attributes; mod kw { syn::custom_keyword!(to); syn::custom_keyword!(target); } #[derive(Clone)] enum ArgumentModifier { Into, AsRef, Newtype, } #[derive(Clone)] enum DelegatedInput { Input { parameter: syn::FnArg, modifier: Option, }, Argument(syn::Expr), } fn get_argument_modifier(attribute: syn::Attribute) -> Result { if let Meta::Path(mut path) = attribute.meta { if path.segments.len() == 1 { let segment = path.segments.pop().unwrap(); if segment.value().arguments.is_empty() { let ident = segment.value().ident.to_string(); let ident = ident.as_str(); match ident { "into" => return Ok(ArgumentModifier::Into), "as_ref" => return Ok(ArgumentModifier::AsRef), "newtype" => return Ok(ArgumentModifier::Newtype), _ => (), } } } }; panic!("The attribute argument has to be `into` or `as_ref`, like this: `#[into] a: u32`.") } impl syn::parse::Parse for DelegatedInput { fn parse(input: ParseStream) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(syn::token::Bracket) { let content; let _bracket_token = syn::bracketed!(content in input); let expression: syn::Expr = content.parse()?; Ok(Self::Argument(expression)) } else { let (input, modifier) = if lookahead.peek(syn::token::Pound) { let mut attributes = input.call(tolerant_outer_attributes)?; if attributes.len() > 1 { panic!("You can specify at most a single attribute for each parameter in a delegated method"); } let modifier = get_argument_modifier(attributes.pop().unwrap()) .expect("Could not parse argument modifier attribute"); let input: syn::FnArg = input.parse()?; (input, Some(modifier)) } else { (input.parse()?, None) }; Ok(Self::Input { parameter: input, modifier, }) } } } struct DelegatedMethod { method: syn::TraitItemFn, attributes: Vec, visibility: syn::Visibility, arguments: syn::punctuated::Punctuated, } // Given an input parameter from a function signature, create a function // argument used to call the delegate function: omit receiver, extract an // identifier from a typed input parameter (and wrap it in an `Expr`). fn parse_input_into_argument_expression( function_name: &Ident, input: &syn::FnArg, ) -> Option { match input { // Parse inputs of the form `x: T` to retrieve their identifiers. syn::FnArg::Typed(typed) => { match &*typed.pat { // This should not happen, I think. If it does, // it will be ignored as if it were the // receiver. syn::Pat::Ident(ident) if ident.ident == "self" => None, // Expression in the form `x: T`. Extract the // identifier, wrap it in Expr for type compatibility with bracketed expressions, // and append it // to the argument list. syn::Pat::Ident(ident) => { let path_segment = syn::PathSegment { ident: ident.ident.clone(), arguments: syn::PathArguments::None, }; let mut segments = syn::punctuated::Punctuated::new(); segments.push(path_segment); let path = syn::Path { leading_colon: None, segments, }; let ident_as_expr = syn::Expr::from(syn::ExprPath { attrs: Vec::new(), qself: None, path, }); Some(ident_as_expr) } // Other more complex argument expressions are not covered. _ => panic!( "You have to use simple identifiers for delegated method parameters ({})", function_name // The signature is not constructed yet. We make due. ), } } // Skip any `self`/`&self`/`&mut self` argument, since // it does not appear in the argument list and it's // already added to the parameter list. syn::FnArg::Receiver(_receiver) => None, } } impl syn::parse::Parse for DelegatedMethod { fn parse(input: ParseStream) -> Result { let attributes = input.call(tolerant_outer_attributes)?; let visibility = input.call(syn::Visibility::parse)?; // Unchanged from Parse from TraitItemMethod let constness: Option = input.parse()?; let asyncness: Option = input.parse()?; let unsafety: Option = input.parse()?; let abi: Option = input.parse()?; let fn_token: syn::Token![fn] = input.parse()?; let ident: Ident = input.parse()?; let generics: syn::Generics = input.parse()?; let content; let paren_token = syn::parenthesized!(content in input); // Parse inputs (method parameters) and arguments. The parameters // constitute the parameter list of the signature of the delegating // method so it must include all inputs, except bracketed expressions. // The argument list constitutes the list of arguments used to call the // delegated function. It must include all inputs, excluding the // receiver (self-type) input. The arguments must all be parsed to // retrieve the expressions inside of the brackets as well as variable // identifiers of ordinary inputs. The arguments must preserve the order // of the inputs. let delegated_inputs = content.parse_terminated(DelegatedInput::parse, syn::Token![,])?; let mut inputs: syn::punctuated::Punctuated = syn::punctuated::Punctuated::new(); let mut arguments: syn::punctuated::Punctuated = syn::punctuated::Punctuated::new(); // First, combine the cases for pairs with cases for end, to remove // redundancy below. delegated_inputs .into_pairs() .map(|punctuated_pair| match punctuated_pair { syn::punctuated::Pair::Punctuated(item, comma) => (item, Some(comma)), syn::punctuated::Pair::End(item) => (item, None), }) .for_each(|pair| match pair { // This input is a bracketed argument (eg. `[ self.x ]`). It // is omitted in the signature of the delegator, but the // expression inside the brackets is used in the body of the // delegator, as an arugnment to the delegated function (eg. // `self.x`). The argument needs to be generated in the // appropriate position with respect other arguments and non- // argument inputs. As long as inputs are added to the // `arguments` vector in order of occurance, this is trivial. (DelegatedInput::Argument(argument), maybe_comma) => { arguments.push_value(argument); if let Some(comma) = maybe_comma { arguments.push_punct(comma) } } // The input is a standard function parameter with a name and // a type (eg. `x: T`). This input needs to be reflected in // the delegator signature as is (eg. `x: T`). The identifier // also needs to be included in the argument list in part // (eg. `x`). The argument list needs to preserve the order of // the inputs with relation to arguments (see above), so the // parsing is best done here (previously it was done at // generation). ( DelegatedInput::Input { parameter, modifier, }, maybe_comma, ) => { inputs.push_value(parameter.clone()); if let Some(comma) = maybe_comma { inputs.push_punct(comma); } let maybe_argument = parse_input_into_argument_expression(&ident, ¶meter); if let Some(mut argument) = maybe_argument { let span = argument.span(); if let Some(modifier) = modifier { let method_call = |name: &str| { syn::Expr::from(ExprMethodCall { attrs: vec![], receiver: Box::new(argument.clone()), dot_token: Default::default(), method: Ident::new(name, span), turbofish: None, paren_token, args: Default::default(), }) }; let field_call = || { syn::Expr::from(ExprField { attrs: vec![], base: Box::new(argument.clone()), dot_token: Default::default(), member: syn::Member::Unnamed(0.into()), }) }; match modifier { ArgumentModifier::Into => { argument = method_call("into"); } ArgumentModifier::AsRef => { argument = method_call("as_ref"); } ArgumentModifier::Newtype => argument = field_call(), } } arguments.push(argument); if let Some(comma) = maybe_comma { arguments.push_punct(comma); } } } }); // Unchanged from Parse from TraitItemMethod let output: syn::ReturnType = input.parse()?; let where_clause: Option = input.parse()?; // This needs to be generated manually, because inputs need to be // separated into actual inputs that go in the signature (the // parameters) and the additional expressions in square brackets which // go into the arguments vector (artguments of the call on the method // on the inner object). let signature = syn::Signature { constness, asyncness, unsafety, abi, fn_token, ident, paren_token, inputs, output, variadic: None, generics: syn::Generics { where_clause, ..generics }, }; // Check if the input contains a semicolon or a brace. If it contains // a semicolon, we parse it (to retain token location information) and // continue. However, if it contains a brace, this indicates that // there is a default definition of the method. This is not supported, // so in that case we error out. let lookahead = input.lookahead1(); let semi_token: Option = if lookahead.peek(syn::Token![;]) { Some(input.parse()?) } else { panic!( "Do not include implementation of delegated functions ({})", signature.ident ); }; // This needs to be populated from scratch because of the signature above. let method = syn::TraitItemFn { // All attributes are attached to `DelegatedMethod`, since they // presumably pertain to the process of delegation, not the // signature of the delegator. attrs: Vec::new(), sig: signature, default: None, semi_token, }; Ok(DelegatedMethod { method, attributes, visibility, arguments, }) } } struct DelegatedSegment { delegator: syn::Expr, methods: Vec, segment_attrs: SegmentAttributes, } impl syn::parse::Parse for DelegatedSegment { fn parse(input: ParseStream) -> Result { let attributes = input.call(tolerant_outer_attributes)?; let segment_attrs = parse_segment_attributes(&attributes); if let Ok(keyword) = input.parse::() { return Err(Error::new(keyword.span(), "You are using the old `target` expression, which is deprecated. Please replace `target` with `to`.")); } else { input.parse::()?; } syn::Expr::parse_without_eager_brace(input).and_then(|delegator| { let content; syn::braced!(content in input); let mut methods = vec![]; while !content.is_empty() { methods.push( content .parse::() .expect("Cannot parse delegated method"), ); } Ok(DelegatedSegment { delegator, methods, segment_attrs, }) }) } } struct DelegationBlock { segments: Vec, } impl syn::parse::Parse for DelegationBlock { fn parse(input: ParseStream) -> Result { let mut segments = vec![]; while !input.is_empty() { segments.push(input.parse()?); } Ok(DelegationBlock { segments }) } } /// Returns true if there are any `inline` attributes in the input. fn has_inline_attribute(attrs: &[&syn::Attribute]) -> bool { attrs.iter().any(|attr| { if let syn::AttrStyle::Outer = attr.style { attr.path().is_ident("inline") } else { false } }) } struct MatchVisitor(F); impl proc_macro2::TokenStream> VisitMut for MatchVisitor { fn visit_arm_mut(&mut self, arm: &mut syn::Arm) { let transformed = self.0(&arm.body); arm.body = parse_quote!(#transformed); } } #[proc_macro] pub fn delegate(tokens: TokenStream) -> TokenStream { let block: DelegationBlock = syn::parse_macro_input!(tokens); let sections = block.segments.iter().map(|delegator| { let delegated_expr = &delegator.delegator; let functions = delegator.methods.iter().map(|method| { let input = &method.method; let mut signature = input.sig.clone(); if let Expr::Closure(closure) = delegated_expr { let additional_inputs: Vec = closure .inputs .iter() .map(|input| { if let syn::Pat::Type(pat_type) = input { syn::parse_quote!(#pat_type) } else { panic!( "Use a type pattern (`a: u32`) for delegation closure arguments" ); } }) .collect(); let mut origin_inputs = mem::take(&mut signature.inputs).into_iter(); // When delegating methods, `first_input` should be self or similar receivers // Then we need to move it to first // When delegating associated methods, it may be a trivial argument or does not even exist // We just keep the origin order. let first_input = origin_inputs.next(); match first_input { Some(FnArg::Receiver(receiver)) => { signature.inputs.push(FnArg::Receiver(receiver)); signature.inputs.extend(additional_inputs); } Some(first_input) => { signature.inputs.extend(additional_inputs); signature.inputs.push(first_input); } _ => { signature.inputs.extend(additional_inputs); } } signature.inputs.extend(origin_inputs); } let attributes = parse_method_attributes(&method.attributes, input); let attributes = combine_attributes(attributes, &delegator.segment_attrs); if input.default.is_some() { panic!( "Do not include implementation of delegated functions ({})", signature.ident ); } // Generate an argument vector from Punctuated list. let args: Vec = method.arguments.clone().into_iter().collect(); // Get name (or index) of the target method or field let name = match &attributes.target_specifier { Some(target) => target.get_member(&input.sig.ident), None => input.sig.ident.clone().into(), }; let inline = if has_inline_attribute(&attributes.attributes) { quote!() } else { quote! { #[inline] } }; let visibility = &method.visibility; let is_method = method.method.sig.receiver().is_some(); let associated_const = &attributes.associated_constant; let expr_attr = &attributes.expr_attr; // Use the body of a closure (like `|k: u32| `) as the delegation expression let delegated_body = if let Expr::Closure(closure) = delegated_expr { &closure.body } else { delegated_expr }; let span = input.span(); let generate_await = attributes .generate_await .unwrap_or_else(|| method.method.sig.asyncness.is_some()); // fn method<'a, A, B> -> method:: let generic_params = &method.method.sig.generics.params; let generics = if generic_params.is_empty() { quote::quote! {} } else { let span = generic_params.span(); let mut params: syn::punctuated::Punctuated< proc_macro2::TokenStream, syn::Token![,], > = syn::punctuated::Punctuated::new(); for param in generic_params.iter() { let token = match param { GenericParam::Lifetime(_) => { // Do not pass lifetimes to generic arguments explicitly to avoid // things like https://doc.rust-lang.org/error_codes/E0794.html // See https://github.com/Kobzol/rust-delegate/issues/85. continue; } GenericParam::Type(t) => { let token = &t.ident; let span = t.span(); quote::quote_spanned! {span=> #token } } GenericParam::Const(c) => { let token = &c.ident; let span = c.span(); quote::quote_spanned! {span=> #token } } }; params.push(token); } quote::quote_spanned! {span=> ::<#params> } }; let modify_expr = |expr: &Expr| { let body = if let Some(target_trait) = &attributes.target_trait { quote::quote! { #target_trait::#name#generics(#expr, #(#args),*) } } else if let Some(AssociatedConstant { const_name, trait_path, }) = associated_const { let return_type = &signature.output; quote::quote! {{ const fn get_const(t: &T) #return_type { ::#const_name } get_const(#expr) }} } else if is_method { match &attributes.target_specifier { None | Some(TargetSpecifier::Method(_)) => { quote::quote! { #expr.#name#generics(#(#args),*) } } Some(TargetSpecifier::Field(target)) => { let reference = target.reference_tokens(); quote::quote! { #reference#expr.#name } } } } else { quote::quote! { #expr::#name#generics(#(#args),*) } }; let mut body = if generate_await { quote::quote! { #body.await } } else { body }; for expression in &attributes.expressions { match expression { ReturnExpression::Into(type_name) => { body = match type_name { Some(name) => { quote::quote! { ::core::convert::Into::<#name>::into(#body) } } None => quote::quote! { ::core::convert::Into::into(#body) }, }; } ReturnExpression::TryInto => { body = quote::quote! { ::core::convert::TryInto::try_into(#body) }; } ReturnExpression::Unwrap => { body = quote::quote! { #body.unwrap() }; } } } body }; let mut body = if let Expr::Match(expr_match) = delegated_body { let mut expr_match = expr_match.clone(); MatchVisitor(modify_expr).visit_expr_match_mut(&mut expr_match); expr_match.into_token_stream() } else { modify_expr(delegated_body) }; if let syn::ReturnType::Default = &signature.output { body = quote::quote! { #body; }; }; if let Some(expr_template) = expr_attr { body = expr_template.expand_template(&body); } let attrs = &attributes.attributes; quote::quote_spanned! {span=> #(#attrs)* #inline #visibility #signature { #body } } }); quote! { #(#functions)* } }); let result = quote! { #(#sections)* }; result.into() } // we cannot use `Attributes::parse_outer` directly, because it does not allow keywords to appear // in meta path positions, i.e., it does not accept `#[await(true)]`. // related issue: https://github.com/dtolnay/syn/issues/1458 fn tolerant_outer_attributes(input: ParseStream) -> syn::Result> { use proc_macro2::{Delimiter, TokenTree}; use syn::{ bracketed, ext::IdentExt, parse::discouraged::Speculative, token::{Brace, Bracket, Paren}, AttrStyle, Attribute, ExprLit, Lit, MacroDelimiter, MetaList, MetaNameValue, Path, Result, Token, }; fn tolerant_attr(input: ParseStream) -> Result { let content; Ok(Attribute { pound_token: input.parse()?, style: AttrStyle::Outer, bracket_token: bracketed!(content in input), meta: content.call(tolerant_meta)?, }) } // adapted from `impl Parse for Meta` fn tolerant_meta(input: ParseStream) -> Result { // Try to parse as Meta if let Ok(meta) = input.call(Meta::parse) { Ok(meta) } else { // If it's not possible, try to parse it as any identifier, to support #[await] let path = Path::from(input.call(Ident::parse_any)?); if input.peek(Paren) || input.peek(Bracket) || input.peek(Brace) { // adapted from the private `syn::attr::parse_meta_after_path` input.step(|cursor| { if let Some((TokenTree::Group(g), rest)) = cursor.token_tree() { let span = g.delim_span(); let delimiter = match g.delimiter() { Delimiter::Parenthesis => MacroDelimiter::Paren(Paren(span)), Delimiter::Brace => MacroDelimiter::Brace(Brace(span)), Delimiter::Bracket => MacroDelimiter::Bracket(Bracket(span)), Delimiter::None => { return Err(cursor.error("expected delimiter")); } }; Ok(( Meta::List(MetaList { path, delimiter, tokens: g.stream(), }), rest, )) } else { Err(cursor.error("expected delimiter")) } }) } else if input.peek(Token![=]) { // adapted from the private `syn::attr::parse_meta_name_value_after_path` let eq_token = input.parse()?; let ahead = input.fork(); let value = match ahead.parse::>()? { // this branch is probably for speeding up the parsing for doc comments etc. Some(lit) if ahead.is_empty() => { input.advance_to(&ahead); Expr::Lit(ExprLit { attrs: Vec::new(), lit, }) } _ => input.parse()?, }; Ok(Meta::NameValue(MetaNameValue { path, eq_token, value, })) } else { Ok(Meta::Path(path)) } } } let mut attrs = Vec::new(); while input.peek(Token![#]) { attrs.push(input.call(tolerant_attr)?); } Ok(attrs) }