virtue-0.0.18/.cargo_vcs_info.json0000644000000001360000000000100124400ustar { "git": { "sha1": "1ecc01325a038c4f28aa8de25caae7c6eb91c66f" }, "path_in_vcs": "" }virtue-0.0.18/.github/dependabot.yml000064400000000000000000000004601046102023000154200ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: / schedule: interval: daily open-pull-requests-limit: 10 - package-ecosystem: "github-actions" directory: "/" schedule: # Check for updates to GitHub Actions every weekday interval: "daily" virtue-0.0.18/.github/stale.yml000064400000000000000000000013071046102023000144240ustar 00000000000000# Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - bug - security - not-stale # Label to use when marking an issue as stale staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false virtue-0.0.18/.github/workflows/rust.yml000064400000000000000000000130551046102023000163510ustar 00000000000000{ "name": "CI", "on": { "push": { "branches": ["trunk", "v*", "ci/*"] }, "pull_request": { "branches": ["trunk", "v*"] }, }, "jobs": { "check": { "name": "Check", "runs-on": "ubuntu-latest", "strategy": { "fail-fast": false, "matrix": { "rust": [ "stable", "beta", "nightly", # "1.55.0" TODO: Pick latest stable version when we release 2.0 ] } }, "steps": [ { "uses": "actions/checkout@v4", "name": "Checkout" }, { "uses": "actions-rs/toolchain@v1", "with": { "profile": "minimal", "toolchain": "${{ matrix.rust }}", "override": true, }, "name": "Install Rust ${{ matrix.rust }}", }, { "uses": "actions-rs/cargo@v1", "with": { "command": "check", "args": "--all-features" }, "name": "Run `cargo check`", }, { "uses": "actions-rs/cargo@v1", "with": { "command": "check", "args": "--examples" }, "name": "Check examples", }, ], }, "test": { "name": "Test", "runs-on": "ubuntu-latest", "strategy": { "matrix": { "rust": [ "stable", # "1.55.0" TODO: Pick latest stable version when we release 2.0 ] } }, "steps": [ { "uses": "actions/checkout@v4", "name": "Checkout" }, { "uses": "actions-rs/toolchain@v1", "with": { "profile": "minimal", "toolchain": "${{ matrix.rust }}", "override": true, }, "name": "Install Rust ${{ matrix.rust }}", }, { "run": "cargo test --all --features proc-macro2", "name": "Run `cargo test`", "env": { "RUSTFLAGS": "-D warnings" }, }, ], }, "test_project": { "name": "Test Project", "runs-on": "ubuntu-latest", "strategy": { "matrix": { "rust": [ "stable", # "1.55.0" TODO: Pick latest stable version when we release 0.1 ] } }, "steps": [ { "uses": "actions/checkout@v4", "name": "Checkout" }, { "uses": "actions-rs/toolchain@v1", "with": { "profile": "minimal", "toolchain": "${{ matrix.rust }}", "override": true, }, "name": "Install Rust ${{ matrix.rust }}", }, { "run": "cd test && cargo run", "name": "Run the test project", "env": { "RUSTFLAGS": "-D warnings" }, }, ], }, "lints": { "name": "Lints", "runs-on": "ubuntu-latest", "steps": [ { "uses": "actions/checkout@v4", "name": "Checkout" }, { "uses": "actions-rs/toolchain@v1", "with": { "profile": "minimal", "toolchain": "stable", "override": true, "components": "rustfmt, clippy", }, "name": "Install Rust stable", }, { "uses": "actions-rs/cargo@v1", "with": { "command": "fmt", "args": "--all -- --check" }, "name": "Run `cargo fmt`", }, { "uses": "actions-rs/cargo@v1", "with": { "command": "clippy", "args": "--all-features -- -D warnings", }, "name": "Run `cargo clippy`", }, ], }, "coverage": { "name": "Code Coverage", "runs-on": "ubuntu-latest", "steps": [ { "uses": "actions/checkout@v4", "name": "Checkout" }, { "uses": "actions-rs/toolchain@v1", "with": { "profile": "minimal", "toolchain": "nightly", "override": true, }, "name": "Install Rust nightly", }, { "name": "Run cargo-tarpaulin", "uses": "actions-rs/tarpaulin@v0.1", "with": { "version": "0.18.2", "args": "--all" }, }, { "name": "Upload to codecov.io", "uses": "codecov/codecov-action@v3", }, { "name": "Archive code coverage results", "uses": "actions/upload-artifact@v3", "with": { "name": "code-coverage-report", "path": "cobertura.xml" }, }, ], }, }, } virtue-0.0.18/.github/workflows/security.yml000064400000000000000000000007021046102023000172160ustar 00000000000000{ "name": "Security audit", "on": { "schedule": [{ "cron": "0 0 * * *" }] }, "jobs": { "audit": { "runs-on": "ubuntu-latest", "steps": [ { "uses": "actions/checkout@v4" }, { "uses": "actions-rs/audit-check@v1", "with": { "token": "${{ secrets.GITHUB_TOKEN }}" }, }, ], }, }, } virtue-0.0.18/.gitignore000064400000000000000000000000251046102023000132150ustar 00000000000000/target Cargo.lock virtue-0.0.18/Cargo.toml0000644000000017030000000000100104370ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "virtue" version = "0.0.18" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A sinless derive macro helper" documentation = "https://docs.rs/virtue" readme = "README.md" license = "MIT" repository = "https://github.com/bincode-org/virtue" [lib] name = "virtue" path = "src/lib.rs" [dependencies.proc-macro2] version = "1.0" optional = true [dev-dependencies.proc-macro2] version = "1.0" virtue-0.0.18/Cargo.toml.orig000064400000000000000000000006331046102023000141210ustar 00000000000000[package] name = "virtue" version = "0.0.18" edition = "2021" description = "A sinless derive macro helper" license = "MIT" documentation = "https://docs.rs/virtue" repository = "https://github.com/bincode-org/virtue" readme = "README.md" [dev-dependencies] proc-macro2 = "1.0" [dependencies] proc-macro2 = { version = "1.0", optional = true } [workspace] members = ["test", "test/derive"] virtue-0.0.18/LICENSE.md000064400000000000000000000021171046102023000126350ustar 00000000000000The MIT License (MIT) Copyright (c) 2021 Victor Koenders Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. virtue-0.0.18/README.md000064400000000000000000000041461046102023000125140ustar 00000000000000# Virtue, a sinless derive macro helper ## Goals - Zero dependencies, so fast compile times - No other dependencies needed - Declarative code generation - As much typesystem checking as possible - Build for modern rust: 1.57 and up - Build with popular crates in mind: - [bincode](https://docs.rs/bincode) - Will always respect semver. Minor releases will never have: - Breaking API changes - MSRV changes ## Example ```rust use virtue::prelude::*; #[proc_macro_derive(YourDerive, attributes(some, attributes, go, here))] pub fn derive_your_derive(input: TokenStream) -> TokenStream { derive_your_derive_inner(input) .unwrap_or_else(|error| error.into_token_stream()) } fn derive_your_derive_inner(input: TokenStream) -> Result { // Parse the struct or enum you want to implement a derive for let parse = Parse::new(input)?; // Get a reference to the generator let (mut generator, body) = parse.into_generator(); match body { Body::Struct(body) => { // Implement your struct body here // See `Generator` for more information generator.impl_for("YourTrait")? .generate_fn("your_fn") .with_self_arg(FnSelfArg::RefSelf) .body(|fn_body| { fn_body.push_parsed("println!(\"Hello world\");"); })?; }, Body::Enum(body) => { // Implement your enum body here // See `Generator` for more information generator.impl_for("YourTrait")? .generate_fn("your_fn") .with_self_arg(FnSelfArg::RefSelf) .body(|fn_body| { fn_body.push_parsed("println!(\"Hello world\");"); })?; }, } generator.finish() } ``` Will generate ```ignore impl YourTrait for { fn your_fn(&self) { // .generate_fn("your_fn").with_self_arg(FnSelfArg::RefSelf) println!("Hello world"); // fn_body.push_parsed(...) } } ``` virtue-0.0.18/justfile000064400000000000000000000002001046102023000127700ustar 00000000000000default: just --list ci: cargo fmt --all -- --check cargo clippy --all-features -- -D warnings cargo testvirtue-0.0.18/src/error.rs000064400000000000000000000132131046102023000135160ustar 00000000000000use crate::{ generate::{PushParseError, StreamBuilder}, prelude::*, }; use std::fmt; /// Errors that can occur while parsing or generator your derive macro. #[derive(Debug)] pub enum Error { /// The data type at `Span` is unknown. This will be called when [`Parse::new`] is called on anything that is not a `struct` or `enum`. /// /// [`Parse::new`]: enum.Parse.html#method.new UnknownDataType(Span), /// The rust syntax is invalid. This can be returned while parsing the Enum or Struct. /// /// This error is assumed to not appear as rustc will do syntax checking before virtue gets access to the [`TokenStream`]. /// However this error could still be returned. InvalidRustSyntax { /// The span at which the invalid syntax is found span: Span, /// The expected rust syntax when this parsing occured expected: String, }, /// Expected an ident at the given span. ExpectedIdent(Span), /// Failed to parse the code passed to [`StreamBuilder::push_parsed`]. /// /// [`StreamBuilder::push_parsed`]: struct.StreamBuilder.html#method.push_parsed PushParse { /// An optional span. Normally this is `None`, unless `.with_span` is called. span: Option, /// The internal parse error error: PushParseError, }, /// A custom error thrown by the developer Custom { /// The error message error: String, /// Optionally the position that the error occurred at span: Option, }, } impl From for Error { fn from(e: PushParseError) -> Self { Self::PushParse { span: None, error: e, } } } impl Error { /// Throw a custom error pub fn custom(s: impl Into) -> Self { Self::Custom { error: s.into(), span: None, } } /// Throw a custom error at a given location pub fn custom_at(s: impl Into, span: Span) -> Self { Self::Custom { error: s.into(), span: Some(span), } } /// Throw a custom error at a given token pub fn custom_at_token(s: impl Into, token: TokenTree) -> Self { Self::Custom { error: s.into(), span: Some(token.span()), } } /// Throw a custom error at a given `Option` pub fn custom_at_opt_token(s: impl Into, token: Option) -> Self { Self::Custom { error: s.into(), span: token.map(|t| t.span()), } } pub(crate) fn wrong_token(token: Option<&TokenTree>, expected: &str) -> Result { Err(Self::InvalidRustSyntax { span: token.map(|t| t.span()).unwrap_or_else(Span::call_site), expected: format!("{}, got {:?}", expected, token), }) } /// Return a new error that is located at the given span pub fn with_span(mut self, new_span: Span) -> Self { match &mut self { Error::UnknownDataType(span) => *span = new_span, Error::InvalidRustSyntax { span, .. } => *span = new_span, Error::ExpectedIdent(span) => *span = new_span, Error::PushParse { span, .. } => { *span = Some(new_span); } Error::Custom { span, .. } => *span = Some(new_span), } self } } // helper functions for the unit tests #[cfg(test)] impl Error { pub fn is_unknown_data_type(&self) -> bool { matches!(self, Error::UnknownDataType(_)) } pub fn is_invalid_rust_syntax(&self) -> bool { matches!(self, Error::InvalidRustSyntax { .. }) } } impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self { Self::UnknownDataType(_) => { write!(fmt, "Unknown data type, only enum and struct are supported") } Self::InvalidRustSyntax { expected, .. } => { write!(fmt, "Invalid rust syntax, expected {}", expected) } Self::ExpectedIdent(_) => write!(fmt, "Expected ident"), Self::PushParse { error, .. } => write!( fmt, "Invalid code passed to `StreamBuilder::push_parsed`: {:?}", error ), Self::Custom { error, .. } => write!(fmt, "{}", error), } } } impl Error { /// Turn this error into a [`TokenStream`] so it shows up as a [`compile_error`] for the user. pub fn into_token_stream(self) -> TokenStream { let maybe_span = match &self { Self::UnknownDataType(span) | Self::ExpectedIdent(span) | Self::InvalidRustSyntax { span, .. } => Some(*span), Self::Custom { span, .. } | Self::PushParse { span, .. } => *span, }; self.throw_with_span(maybe_span.unwrap_or_else(Span::call_site)) } /// Turn this error into a [`TokenStream`] so it shows up as a [`compile_error`] for the user. The error will be shown at the given `span`. pub fn throw_with_span(self, span: Span) -> TokenStream { // compile_error!($message) let mut builder = StreamBuilder::new(); builder.ident_str("compile_error"); builder.punct('!'); builder .group(Delimiter::Brace, |b| { b.lit_str(self.to_string()); Ok(()) }) .unwrap(); builder.set_span_on_all_tokens(span); builder.stream } } virtue-0.0.18/src/generate/gen_enum.rs000064400000000000000000000431141046102023000157570ustar 00000000000000use super::{ AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, Path, StreamBuilder, StringOrIdent, }; use crate::parse::{Generic, Generics, Visibility}; use crate::prelude::{Delimiter, Ident, Span, TokenStream}; use crate::Result; /// Builder to generate an `enum { { ... }, ... }` /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Fooz"); /// { /// let mut enumgen = generator.generate_enum("Foo"); /// enumgen /// .add_value("ZST") /// .make_zst(); /// enumgen /// .add_value("Named") /// .add_field("bar", "u16") /// .add_field("baz", "String"); /// enumgen /// .add_value("Unnamed") /// .make_tuple() /// .add_field("", "u16") /// .add_field("baz", "String"); /// } /// # generator.assert_eq("enum Foo { ZST , Named { bar : u16 , baz : String , } , Unnamed (u16 , String ,) , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ``` /// enum Foo { /// ZST, /// Named { /// bar: u16, /// baz: String, /// }, /// Unnamed(u16, String), /// }; /// ``` pub struct GenEnum<'a, P: Parent> { parent: &'a mut P, name: Ident, visibility: Visibility, generics: Option, values: Vec, derives: Vec, attributes: Vec, additional: Vec, } impl<'a, P: Parent> GenEnum<'a, P> { pub(crate) fn new(parent: &'a mut P, name: impl Into) -> Self { Self { parent, name: Ident::new(name.into().as_str(), Span::call_site()), visibility: Visibility::Default, generics: None, values: Vec::new(), derives: Vec::new(), attributes: Vec::new(), additional: Vec::new(), } } /// Make the enum `pub`. By default the struct will have no visibility modifier and will only be visible in the current scope. pub fn make_pub(&mut self) -> &mut Self { self.visibility = Visibility::Pub; self } /// Add a derive macro to the enum. /// /// ``` /// # use virtue::prelude::Generator; /// # use virtue::generate::Path; /// # let mut generator = Generator::with_name("Bar"); /// generator /// .generate_enum("Foo") /// .with_derive("Clone") /// .with_derive("Default") /// .with_derive(Path::from_iter(vec!["serde", "Deserialize"])); /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] enum Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// #[derive(Clone, Default, serde::Deserialize)] /// enum Foo { } pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { AttributeContainer::with_derive(self, derive) } /// Add derive macros to the enum. /// /// ``` /// # use virtue::prelude::Generator; /// # use virtue::generate::Path; /// # let mut generator = Generator::with_name("Bar"); /// generator /// .generate_enum("Foo") /// .with_derives([ /// "Clone".into(), /// "Default".into(), /// Path::from_iter(vec!["serde", "Deserialize"]), /// ]); /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] enum Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// #[derive(Clone, Default, serde::Deserialize)] /// enum Foo { } pub fn with_derives>( &mut self, derives: impl IntoIterator, ) -> &mut Self { AttributeContainer::with_derives(self, derives) } /// Add an attribute to the enum. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) /// instead. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// generator /// .generate_enum("Foo") /// .with_attribute("serde", |b| { /// b.push_parsed("(untagged)")?; /// Ok(()) /// })?; /// # generator.assert_eq("# [serde (untagged)] enum Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// #[serde(untagged)] /// enum Foo { } /// ``` pub fn with_attribute( &mut self, name: impl AsRef, value: impl FnOnce(&mut StreamBuilder) -> Result, ) -> Result<&mut Self> { AttributeContainer::with_attribute(self, name, value) } /// Add a parsed attribute to the enum. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) /// instead. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// /// generator /// .generate_enum("Foo") /// .with_parsed_attribute("serde(untagged)")?; /// # generator.assert_eq("# [serde (untagged)] enum Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// #[serde(untagged)] /// enum Foo { } /// ``` pub fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> Result<&mut Self> { AttributeContainer::with_parsed_attribute(self, attribute) } /// Add a token stream as an attribute to the enum. For `#[derive(...)]`, use /// [`with_derive`](Self::with_derive) instead. /// /// ``` /// # use virtue::prelude::{Generator, TokenStream}; /// # let mut generator = Generator::with_name("Bar"); /// /// let attribute = "serde(untagged)".parse::().unwrap(); /// generator /// .generate_enum("Foo") /// .with_attribute_stream(attribute); /// # generator.assert_eq("# [serde (untagged)] enum Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// #[serde(untagged)] /// enum Foo { } /// ``` pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { AttributeContainer::with_attribute_stream(self, attribute) } /// Inherit the generic parameters of the parent type. /// /// ``` /// # use virtue::prelude::Generator; /// # use virtue::parse::{Generic, Lifetime}; /// # use proc_macro2::{Ident, Span}; /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); /// // given a derive on enum Bar<'a> /// generator /// .generate_enum("Foo") /// .inherit_generics() /// .add_value("Bar") /// .make_tuple() /// .add_field("bar", "&'a str"); /// # generator.assert_eq("enum Foo < 'a > { Bar (&'a str ,) , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// // given a derive on enum Bar<'a> /// enum Foo<'a> { /// Bar(&'a str) /// } /// ``` pub fn inherit_generics(&mut self) -> &mut Self { self.generics = self.parent.generics().cloned(); self } /// Append generic parameters to the type. /// /// ``` /// # use virtue::prelude::Generator; /// # use virtue::parse::{Generic, Lifetime}; /// # use proc_macro2::{Ident, Span}; /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); /// generator /// .generate_enum("Foo") /// .with_generics([Lifetime { ident: Ident::new("a", Span::call_site()), constraint: vec![] }.into()]) /// .add_value("Bar") /// .make_tuple() /// .add_field("bar", "&'a str"); /// # generator.assert_eq("enum Foo < 'a > { Bar (&'a str ,) , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// enum Foo<'a> { /// Bar(&'a str) /// } /// ``` pub fn with_generics(&mut self, generics: impl IntoIterator) -> &mut Self { self.generics .get_or_insert_with(|| Generics(Vec::new())) .extend(generics); self } /// Add a generic parameter to the type. /// /// ``` /// # use virtue::prelude::Generator; /// # use virtue::parse::{Generic, Lifetime}; /// # use proc_macro2::{Ident, Span}; /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); /// generator /// .generate_enum("Foo") /// .with_generic(Lifetime { ident: Ident::new("a", Span::call_site()), constraint: vec![] }.into()) /// .add_value("Bar") /// .make_tuple() /// .add_field("bar", "&'a str"); /// # generator.assert_eq("enum Foo < 'a > { Bar (&'a str ,) , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// enum Foo<'a> { /// Bar(&'a str) /// } /// ``` pub fn with_generic(&mut self, generic: Generic) -> &mut Self { self.generics .get_or_insert_with(|| Generics(Vec::new())) .push(generic); self } /// Add an enum value /// /// Returns a builder for the value that's similar to GenStruct pub fn add_value(&mut self, name: impl Into) -> &mut EnumValue { self.values.push(EnumValue::new(name)); self.values.last_mut().unwrap() } /// Add an `impl for ` pub fn impl_for(&mut self, name: impl Into) -> ImplFor { ImplFor::new(self, name.into(), None) } /// Generate an `impl ` implementation. See [`Impl`] for more information. pub fn r#impl(&mut self) -> Impl { Impl::with_parent_name(self) } /// Generate an `impl ` implementation. See [`Impl`] for more information. /// /// Alias for [`impl`] which doesn't need a `r#` prefix. /// /// [`impl`]: #method.impl pub fn generate_impl(&mut self) -> Impl { Impl::with_parent_name(self) } } impl AttributeContainer for GenEnum<'_, P> { fn derives(&mut self) -> &mut Vec { &mut self.derives } fn attributes(&mut self) -> &mut Vec { &mut self.attributes } } impl<'a, P: Parent> Parent for GenEnum<'a, P> { fn append(&mut self, builder: StreamBuilder) { self.additional.push(builder); } fn name(&self) -> &Ident { &self.name } fn generics(&self) -> Option<&Generics> { self.generics.as_ref() } fn generic_constraints(&self) -> Option<&crate::parse::GenericConstraints> { None } } impl<'a, P: Parent> Drop for GenEnum<'a, P> { fn drop(&mut self) { let mut builder = StreamBuilder::new(); self.build_derives(&mut builder) .build_attributes(&mut builder); if self.visibility == Visibility::Pub { builder.ident_str("pub"); } builder .ident_str("enum") .ident(self.name.clone()) .append( self.generics() .map(Generics::impl_generics) .unwrap_or_default(), ) .group(Delimiter::Brace, |b| { for value in self.values.iter_mut() { build_value(b, value)?; } Ok(()) }) .expect("Could not build enum"); for additional in std::mem::take(&mut self.additional) { builder.append(additional); } self.parent.append(builder); } } fn build_value(builder: &mut StreamBuilder, value: &mut EnumValue) -> Result { value.build_attributes(builder); builder.ident(value.name.clone()); match value.value_type { ValueType::Named => builder.group(Delimiter::Brace, |b| { for field in value.fields.iter_mut() { field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } b.ident_str(&field.name) .punct(':') .push_parsed(&field.ty)? .punct(','); } Ok(()) })?, ValueType::Unnamed => builder.group(Delimiter::Parenthesis, |b| { for field in value.fields.iter_mut() { field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } b.push_parsed(&field.ty)?.punct(','); } Ok(()) })?, ValueType::Zst => builder, }; builder.punct(','); Ok(()) } pub struct EnumValue { name: Ident, fields: Vec, value_type: ValueType, attributes: Vec, } impl EnumValue { fn new(name: impl Into) -> Self { Self { name: Ident::new(name.into().as_str(), Span::call_site()), fields: Vec::new(), value_type: ValueType::Named, attributes: Vec::new(), } } /// Make the struct a zero-sized type (no fields) /// /// Any fields will be ignored pub fn make_zst(&mut self) -> &mut Self { self.value_type = ValueType::Zst; self } /// Make the struct fields unnamed /// /// The names of any field will be ignored pub fn make_tuple(&mut self) -> &mut Self { self.value_type = ValueType::Unnamed; self } /// Add an attribute to the variant. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// generator /// .generate_enum("Foo") /// .add_value("Bar") /// .with_attribute("serde", |b| { /// b.push_parsed("(rename_all = \"camelCase\")")?; /// Ok(()) /// })?; /// # generator.assert_eq("enum Foo { # [serde (rename_all = \"camelCase\")] Bar { } , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// enum Foo { /// #[serde(rename_all = "camelCase")] /// Bar { } /// } /// ``` pub fn with_attribute( &mut self, name: impl AsRef, value: impl FnOnce(&mut StreamBuilder) -> Result, ) -> Result<&mut Self> { AttributeContainer::with_attribute(self, name, value) } /// Add a parsed attribute to the variant. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// generator /// .generate_enum("Foo") /// .add_value("Bar") /// .with_parsed_attribute("serde(rename_all = \"camelCase\")")?; /// # generator.assert_eq("enum Foo { # [serde (rename_all = \"camelCase\")] Bar { } , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// enum Foo { /// #[serde(rename_all = "camelCase")] /// Bar { } /// } /// ``` pub fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> Result<&mut Self> { AttributeContainer::with_parsed_attribute(self, attribute) } /// Add a token stream as an attribute to the variant. /// /// ``` /// # use virtue::prelude::{Generator, TokenStream}; /// # let mut generator = Generator::with_name("Bar"); /// let attribute = "serde(rename_all = \"camelCase\")".parse::().unwrap(); /// generator /// .generate_enum("Foo") /// .add_value("Bar") /// .with_attribute_stream(attribute); /// # generator.assert_eq("enum Foo { # [serde (rename_all = \"camelCase\")] Bar { } , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// enum Foo { /// #[serde(rename_all = "camelCase")] /// Bar { } /// } /// ``` pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { AttributeContainer::with_attribute_stream(self, attribute) } /// Add a field to the enum value. /// /// Names are ignored when the enum value's fields are unnamed /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Fooz"); /// generator /// .generate_enum("Foo") /// .add_value("Bar") /// .add_field("bar", "u16") /// .add_field("baz", "String"); /// # generator.assert_eq("enum Foo { Bar { bar : u16 , baz : String , } , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ``` /// enum Foo { /// Bar { /// bar: u16, /// baz: String /// } /// }; /// ``` pub fn add_field( &mut self, name: impl Into, ty: impl Into, ) -> FieldBuilder { let mut fields = FieldBuilder::from(&mut self.fields); fields.add_field(name, ty); fields } } impl AttributeContainer for EnumValue { fn derives(&mut self) -> &mut Vec { unreachable!("enum variants cannot have derives") } fn attributes(&mut self) -> &mut Vec { &mut self.attributes } } enum ValueType { Named, Unnamed, Zst, } virtue-0.0.18/src/generate/gen_struct.rs000064400000000000000000000340211046102023000163340ustar 00000000000000use super::{ AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, Path, StreamBuilder, StringOrIdent, }; use crate::parse::{Generic, Generics, Visibility}; use crate::prelude::{Delimiter, Ident, Span, TokenStream}; use crate::Result; /// Builder to generate a struct. /// Defaults to a struct with named fields `struct { : , ... }` pub struct GenStruct<'a, P: Parent> { parent: &'a mut P, name: Ident, visibility: Visibility, generics: Option, fields: Vec, derives: Vec, attributes: Vec, additional: Vec, struct_type: StructType, } impl<'a, P: Parent> GenStruct<'a, P> { pub(crate) fn new(parent: &'a mut P, name: impl Into) -> Self { Self { parent, name: Ident::new(name.into().as_str(), Span::call_site()), visibility: Visibility::Default, generics: None, fields: Vec::new(), derives: Vec::new(), attributes: Vec::new(), additional: Vec::new(), struct_type: StructType::Named, } } /// Make the struct a zero-sized type (no fields) /// /// Any fields will be ignored /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Fooz"); /// generator /// .generate_struct("Foo") /// .make_zst() /// .add_field("bar", "u16") /// .add_field("baz", "String"); /// # generator.assert_eq("struct Foo ;"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ``` /// struct Foo; /// ``` pub fn make_zst(&mut self) -> &mut Self { self.struct_type = StructType::Zst; self } /// Make the struct fields unnamed /// /// The names of any field will be ignored /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Fooz"); /// generator /// .generate_struct("Foo") /// .make_tuple() /// .add_field("bar", "u16") /// .add_field("baz", "String"); /// # generator.assert_eq("struct Foo (u16 , String ,) ;"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ``` /// struct Foo(u16, String); /// ``` pub fn make_tuple(&mut self) -> &mut Self { self.struct_type = StructType::Unnamed; self } /// Make the struct `pub`. By default the struct will have no visibility modifier and will only be visible in the current scope. pub fn make_pub(&mut self) -> &mut Self { self.visibility = Visibility::Pub; self } /// Inherit the generic parameters of the parent type. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); /// // given a derive on struct Bar<'a> /// generator /// .generate_struct("Foo") /// .inherit_generics() /// .add_field("bar", "&'a str"); /// # generator.assert_eq("struct Foo < 'a > { bar : &'a str , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// // given a derive on struct Bar<'a> /// struct Foo<'a> { /// bar: &'a str /// } /// ``` pub fn inherit_generics(&mut self) -> &mut Self { self.generics = self.parent.generics().cloned(); self } /// Append generic parameters to the type. /// /// ``` /// # use virtue::prelude::Generator; /// # use virtue::parse::{Generic, Lifetime}; /// # use proc_macro2::{Ident, Span}; /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); /// generator /// .generate_struct("Foo") /// .with_generics([Lifetime { ident: Ident::new("a", Span::call_site()), constraint: vec![] }.into()]) /// .add_field("bar", "&'a str"); /// # generator.assert_eq("struct Foo < 'a > { bar : &'a str , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// struct Foo<'a> { /// bar: &'a str /// } /// ``` pub fn with_generics(&mut self, generics: impl IntoIterator) -> &mut Self { self.generics .get_or_insert_with(|| Generics(Vec::new())) .extend(generics); self } /// Add a generic parameter to the type. /// /// ``` /// # use virtue::prelude::Generator; /// # use virtue::parse::{Generic, Lifetime}; /// # use proc_macro2::{Ident, Span}; /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); /// generator /// .generate_struct("Foo") /// .with_generic(Lifetime { ident: Ident::new("a", Span::call_site()), constraint: vec![] }.into()) /// .add_field("bar", "&'a str"); /// # generator.assert_eq("struct Foo < 'a > { bar : &'a str , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// struct Foo<'a> { /// bar: &'a str /// } /// ``` pub fn with_generic(&mut self, generic: Generic) -> &mut Self { self.generics .get_or_insert_with(|| Generics(Vec::new())) .push(generic); self } /// Add a derive macro to the struct. /// /// ``` /// # use virtue::prelude::Generator; /// # use virtue::generate::Path; /// # let mut generator = Generator::with_name("Bar"); /// generator /// .generate_struct("Foo") /// .with_derive("Clone") /// .with_derive("Default") /// .with_derive(Path::from_iter(vec!["serde", "Deserialize"])); /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] struct Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// #[derive(Clone, Default, serde::Deserialize)] /// struct Foo { } /// ``` pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { AttributeContainer::with_derive(self, derive) } /// Add derive macros to the struct. /// /// ``` /// # use virtue::prelude::Generator; /// # use virtue::generate::Path; /// # let mut generator = Generator::with_name("Bar"); /// generator /// .generate_struct("Foo") /// .with_derives([ /// "Clone".into(), /// "Default".into(), /// Path::from_iter(vec!["serde", "Deserialize"]), /// ]); /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] struct Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// #[derive(Clone, Default, serde::Deserialize)] /// struct Foo { } /// ``` pub fn with_derives>( &mut self, derives: impl IntoIterator, ) -> &mut Self { AttributeContainer::with_derives(self, derives) } /// Add an attribute to the struct. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) /// instead. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// generator /// .generate_struct("Foo") /// .with_attribute("serde", |b| { /// b.push_parsed("(rename_all = \"camelCase\")")?; /// Ok(()) /// })?; /// # generator.assert_eq("# [serde (rename_all = \"camelCase\")] struct Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// #[serde(rename_all = "camelCase")] /// struct Foo { } /// ``` pub fn with_attribute( &mut self, name: impl AsRef, value: impl FnOnce(&mut StreamBuilder) -> Result, ) -> Result<&mut Self> { AttributeContainer::with_attribute(self, name, value) } /// Add a parsed attribute to the struct. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) /// instead. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// generator /// .generate_struct("Foo") /// .with_parsed_attribute("serde(rename_all = \"camelCase\")")?; /// # generator.assert_eq("# [serde (rename_all = \"camelCase\")] struct Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// #[serde(rename_all = "camelCase")] /// struct Foo { } /// ``` pub fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> Result<&mut Self> { AttributeContainer::with_parsed_attribute(self, attribute) } /// Add a token stream as an attribute to the struct. For `#[derive(...)]`, use /// [`with_derive`](Self::with_derive) instead. /// /// ``` /// # use virtue::prelude::{Generator, TokenStream}; /// # use std::str::FromStr; /// # let mut generator = Generator::with_name("Bar"); /// /// let attribute = "serde(rename_all = \"camelCase\")".parse::().unwrap(); /// generator /// .generate_struct("Foo") /// .with_attribute_stream(attribute); /// # generator.assert_eq("# [serde (rename_all = \"camelCase\")] struct Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// #[serde(rename_all = "camelCase")] /// struct Foo { } /// ``` pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { AttributeContainer::with_attribute_stream(self, attribute) } /// Add a field to the struct. /// /// Names are ignored when the Struct's fields are unnamed /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Fooz"); /// generator /// .generate_struct("Foo") /// .add_field("bar", "u16") /// .add_field("baz", "String"); /// # generator.assert_eq("struct Foo { bar : u16 , baz : String , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ``` /// struct Foo { /// bar: u16, /// baz: String, /// }; /// ``` pub fn add_field( &mut self, name: impl Into, ty: impl Into, ) -> FieldBuilder { let mut fields = FieldBuilder::from(&mut self.fields); fields.add_field(name, ty); fields } /// Add an `impl for ` pub fn impl_for(&mut self, name: impl Into) -> ImplFor { ImplFor::new(self, name.into(), None) } /// Generate an `impl ` implementation. See [`Impl`] for more information. pub fn r#impl(&mut self) -> Impl { Impl::with_parent_name(self) } /// Generate an `impl ` implementation. See [`Impl`] for more information. /// /// Alias for [`impl`] which doesn't need a `r#` prefix. /// /// [`impl`]: #method.impl pub fn generate_impl(&mut self) -> Impl { Impl::with_parent_name(self) } } impl AttributeContainer for GenStruct<'_, P> { fn derives(&mut self) -> &mut Vec { &mut self.derives } fn attributes(&mut self) -> &mut Vec { &mut self.attributes } } impl<'a, P: Parent> Parent for GenStruct<'a, P> { fn append(&mut self, builder: StreamBuilder) { self.additional.push(builder); } fn name(&self) -> &Ident { &self.name } fn generics(&self) -> Option<&Generics> { self.generics.as_ref() } fn generic_constraints(&self) -> Option<&crate::parse::GenericConstraints> { None } } impl<'a, P: Parent> Drop for GenStruct<'a, P> { fn drop(&mut self) { use std::mem::take; let mut builder = StreamBuilder::new(); self.build_derives(&mut builder) .build_attributes(&mut builder); if self.visibility == Visibility::Pub { builder.ident_str("pub"); } builder.ident_str("struct").ident(self.name.clone()).append( self.generics() .map(Generics::impl_generics) .unwrap_or_default(), ); match self.struct_type { StructType::Named => builder .group(Delimiter::Brace, |b| { for field in self.fields.iter_mut() { field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } b.ident_str(&field.name) .punct(':') .push_parsed(&field.ty)? .punct(','); } Ok(()) }) .expect("Could not build struct"), StructType::Unnamed => builder .group(Delimiter::Parenthesis, |b| { for field in self.fields.iter_mut() { field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } b.push_parsed(&field.ty)?.punct(','); } Ok(()) }) .expect("Could not build struct") .punct(';'), StructType::Zst => builder.punct(';'), }; for additional in take(&mut self.additional) { builder.append(additional); } self.parent.append(builder); } } enum StructType { Named, Unnamed, Zst, } virtue-0.0.18/src/generate/generate_item.rs000064400000000000000000000350661046102023000170010ustar 00000000000000use super::StreamBuilder; use crate::{ parse::Visibility, prelude::{Delimiter, Result}, }; /// A builder for constants. pub struct GenConst<'a> { consts: &'a mut Vec, attrs: Vec, name: String, ty: String, vis: Visibility, } impl<'a> GenConst<'a> { pub(crate) fn new( consts: &'a mut Vec, name: impl Into, ty: impl Into, ) -> Self { Self { consts, attrs: Vec::new(), name: name.into(), ty: ty.into(), vis: Visibility::Default, } } /// Make the const `pub`. By default the const will have no visibility modifier and will only be visible in the current scope. #[must_use] pub fn make_pub(mut self) -> Self { self.vis = Visibility::Pub; self } /// Add an outer attribute #[must_use] pub fn with_attr(mut self, attr: impl Into) -> Self { self.attrs.push(attr.into()); self } /// Complete the constant definition. This function takes a callback that will form the value of the constant. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// generator.impl_for("Foo") /// .generate_const("BAR", "u8") /// .with_value(|b| { /// b.push_parsed("5")?; /// Ok(()) /// })?; /// # generator.assert_eq("impl Foo for Bar { const BAR : u8 = 5 ; }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// impl Foo for { /// const BAR: u8 = 5; /// } /// ``` pub fn with_value(self, f: F) -> Result where F: FnOnce(&mut StreamBuilder) -> Result, { let mut builder = StreamBuilder::new(); for attr in self.attrs { builder .punct('#') .punct('!') .group(Delimiter::Bracket, |builder| { builder.push_parsed(attr)?; Ok(()) })?; } if self.vis == Visibility::Pub { builder.ident_str("pub"); } builder .ident_str("const") .push_parsed(self.name)? .punct(':') .push_parsed(self.ty)? .punct('='); f(&mut builder)?; builder.punct(';'); self.consts.push(builder); Ok(()) } } /// A builder for functions. pub struct FnBuilder<'a, P> { parent: &'a mut P, name: String, attrs: Vec, is_async: bool, lifetimes: Vec<(String, Vec)>, generics: Vec<(String, Vec)>, self_arg: FnSelfArg, args: Vec<(String, String)>, return_type: Option, vis: Visibility, } impl<'a, P: FnParent> FnBuilder<'a, P> { pub(super) fn new(parent: &'a mut P, name: impl Into) -> Self { Self { parent, name: name.into(), attrs: Vec::new(), is_async: false, lifetimes: Vec::new(), generics: Vec::new(), self_arg: FnSelfArg::None, args: Vec::new(), return_type: None, vis: Visibility::Default, } } /// Add an outer attribute #[must_use] pub fn with_attr(mut self, attr: impl Into) -> Self { self.attrs.push(attr.into()); self } /// Add a lifetime parameter. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Foo"); /// generator /// .r#impl() /// .generate_fn("foo") // fn foo() /// .with_lifetime("a") // fn foo<'a>() /// # .body(|_| Ok(())).unwrap(); /// # generator.assert_eq("impl Foo { fn foo < 'a > () { } }"); /// ``` #[must_use] pub fn with_lifetime(mut self, name: impl Into) -> Self { self.lifetimes.push((name.into(), Vec::new())); self } /// Make the function async /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Foo"); /// generator /// .r#impl() /// .generate_fn("foo") // fn foo() /// .as_async() // async fn foo() /// # .body(|_| Ok(())).unwrap(); /// # generator.assert_eq("impl Foo { async fn foo () { } }"); /// ``` #[must_use] pub fn as_async(mut self) -> Self { self.is_async = true; self } /// Add a lifetime parameter. /// /// `dependencies` are the lifetime dependencies of the given lifetime. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Foo"); /// generator /// .r#impl() /// .generate_fn("foo") // fn foo() /// .with_lifetime("a") // fn foo<'a>() /// .with_lifetime_deps("b", ["a"]) // fn foo<'b: 'a>() /// # .body(|_| Ok(())).unwrap(); /// # generator.assert_eq("impl Foo { fn foo < 'a , 'b : 'a > () { } }"); /// ``` #[must_use] pub fn with_lifetime_deps( mut self, name: impl Into, dependencies: ITER, ) -> Self where ITER: IntoIterator, I: Into, { self.lifetimes.push(( name.into(), dependencies.into_iter().map(Into::into).collect(), )); self } /// Add a generic parameter. Keep in mind that will *not* work for lifetimes. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Foo"); /// generator /// .r#impl() /// .generate_fn("foo") // fn foo() /// .with_generic("D") // fn foo() /// # .body(|_| Ok(())).unwrap(); /// # generator.assert_eq("impl Foo { fn foo < D > () { } }"); /// ``` #[must_use] pub fn with_generic(mut self, name: impl Into) -> Self { self.generics.push((name.into(), Vec::new())); self } /// Add a generic parameter. Keep in mind that will *not* work for lifetimes. /// /// `dependencies` are the dependencies of the parameter. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Foo"); /// generator /// .r#impl() /// .generate_fn("foo") // fn foo() /// .with_generic("D") // fn foo() /// .with_generic_deps("E", ["Encodable"]) // fn foo(); /// # .body(|_| Ok(())).unwrap(); /// # generator.assert_eq("impl Foo { fn foo < D , E : Encodable > () { } }"); /// ``` #[must_use] pub fn with_generic_deps(mut self, name: impl Into, dependencies: DEP) -> Self where DEP: IntoIterator, I: Into, { self.generics.push(( name.into(), dependencies.into_iter().map(Into::into).collect(), )); self } /// Set the value for `self`. See [FnSelfArg] for more information. /// /// ``` /// # use virtue::prelude::{Generator, FnSelfArg}; /// # let mut generator = Generator::with_name("Foo"); /// generator /// .r#impl() /// .generate_fn("foo") // fn foo() /// .with_self_arg(FnSelfArg::RefSelf) // fn foo(&self) /// # .body(|_| Ok(())).unwrap(); /// # generator.assert_eq("impl Foo { fn foo (& self ,) { } }"); /// ``` #[must_use] pub fn with_self_arg(mut self, self_arg: FnSelfArg) -> Self { self.self_arg = self_arg; self } /// Add an argument with a `name` and a `ty`. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Foo"); /// generator /// .r#impl() /// .generate_fn("foo") // fn foo() /// .with_arg("a", "u32") // fn foo(a: u32) /// .with_arg("b", "u32") // fn foo(a: u32, b: u32) /// # .body(|_| Ok(())).unwrap(); /// # generator.assert_eq("impl Foo { fn foo (a : u32 , b : u32) { } }"); /// ``` #[must_use] pub fn with_arg(mut self, name: impl Into, ty: impl Into) -> Self { self.args.push((name.into(), ty.into())); self } /// Set the return type for the function. By default the function will have no return type. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Foo"); /// generator /// .r#impl() /// .generate_fn("foo") // fn foo() /// .with_return_type("u32") // fn foo() -> u32 /// # .body(|_| Ok(())).unwrap(); /// # generator.assert_eq("impl Foo { fn foo () ->u32 { } }"); /// ``` #[must_use] pub fn with_return_type(mut self, ret_type: impl Into) -> Self { self.return_type = Some(ret_type.into()); self } /// Make the function `pub`. If this is not called, the function will have no visibility modifier. #[must_use] pub fn make_pub(mut self) -> Self { self.vis = Visibility::Pub; self } /// Complete the function definition. This function takes a callback that will form the body of the function. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Foo"); /// generator /// .r#impl() /// .generate_fn("foo") // fn foo() /// .body(|b| { /// b.push_parsed("println!(\"hello world\");")?; /// Ok(()) /// }) /// .unwrap(); /// // fn foo() { /// // println!("Hello world"); /// // } /// # generator.assert_eq("impl Foo { fn foo () { println ! (\"hello world\") ; } }"); /// ``` pub fn body( self, body_builder: impl FnOnce(&mut StreamBuilder) -> crate::Result, ) -> crate::Result { let FnBuilder { parent, name, attrs, is_async, lifetimes, generics, self_arg, args, return_type, vis, } = self; let mut builder = StreamBuilder::new(); // attrs for attr in attrs { builder.punct('#').group(Delimiter::Bracket, |builder| { builder.push_parsed(attr)?; Ok(()) })?; } // function name; `fn name` if vis == Visibility::Pub { builder.ident_str("pub"); } if is_async { builder.ident_str("async"); } builder.ident_str("fn"); builder.ident_str(name); // lifetimes; `<'a: 'b, D: Display>` if !lifetimes.is_empty() || !generics.is_empty() { builder.punct('<'); let mut is_first = true; for (lifetime, dependencies) in lifetimes { if is_first { is_first = false; } else { builder.punct(','); } builder.lifetime_str(lifetime.as_ref()); if !dependencies.is_empty() { for (idx, dependency) in dependencies.into_iter().enumerate() { builder.punct(if idx == 0 { ':' } else { '+' }); builder.lifetime_str(dependency.as_ref()); } } } for (generic, dependencies) in generics { if is_first { is_first = false; } else { builder.punct(','); } builder.ident_str(&generic); if !dependencies.is_empty() { for (idx, dependency) in dependencies.into_iter().enumerate() { builder.punct(if idx == 0 { ':' } else { '+' }); builder.push_parsed(&dependency)?; } } } builder.punct('>'); } // Arguments; `(&self, foo: &Bar)` builder.group(Delimiter::Parenthesis, |arg_stream| { if let Some(self_arg) = self_arg.into_token_tree() { arg_stream.append(self_arg); arg_stream.punct(','); } for (idx, (arg_name, arg_ty)) in args.into_iter().enumerate() { if idx != 0 { arg_stream.punct(','); } arg_stream.push_parsed(&arg_name)?; arg_stream.punct(':'); arg_stream.push_parsed(&arg_ty)?; } Ok(()) })?; // Return type: `-> ResultType` if let Some(return_type) = return_type { builder.puncts("->"); builder.push_parsed(&return_type)?; } let mut body_stream = StreamBuilder::new(); body_builder(&mut body_stream)?; parent.append(builder, body_stream) } } pub trait FnParent { fn append(&mut self, fn_definition: StreamBuilder, fn_body: StreamBuilder) -> Result; } /// The `self` argument of a function #[allow(dead_code)] #[non_exhaustive] pub enum FnSelfArg { /// No `self` argument. The function will be a static function. None, /// `self`. The function will consume self. TakeSelf, /// `mut self`. The function will consume self. MutTakeSelf, /// `&self`. The function will take self by reference. RefSelf, /// `&mut self`. The function will take self by mutable reference. MutSelf, } impl FnSelfArg { fn into_token_tree(self) -> Option { let mut builder = StreamBuilder::new(); match self { Self::None => return None, Self::TakeSelf => { builder.ident_str("self"); } Self::MutTakeSelf => { builder.ident_str("mut"); builder.ident_str("self"); } Self::RefSelf => { builder.punct('&'); builder.ident_str("self"); } Self::MutSelf => { builder.punct('&'); builder.ident_str("mut"); builder.ident_str("self"); } } Some(builder) } } virtue-0.0.18/src/generate/generate_mod.rs000064400000000000000000000063771046102023000166250ustar 00000000000000use super::{GenEnum, GenStruct, Impl, Parent, StreamBuilder}; use crate::{ parse::Visibility, prelude::{Delimiter, Ident, Span}, Result, }; /// Builder for generating a module with its contents. pub struct GenerateMod<'a, P: Parent> { parent: &'a mut P, name: Ident, uses: Vec, vis: Visibility, content: StreamBuilder, } impl<'a, P: Parent> GenerateMod<'a, P> { pub(crate) fn new(parent: &'a mut P, name: impl Into) -> Self { Self { parent, name: Ident::new(name.into().as_str(), Span::call_site()), uses: Vec::new(), vis: Visibility::Default, content: StreamBuilder::new(), } } /// Add a `use ...;` to the current mod /// /// `generator.impl_mod("foo").add_use("bar")` will generate: /// /// ```ignore /// mod foo { /// use bar; /// } /// ``` /// /// This is especially useful with `.add_use("super::*");`, which will pull all parent imports into scope pub fn add_use(&mut self, r#use: impl AsRef) -> Result { let mut builder = StreamBuilder::new(); builder.ident_str("use").push_parsed(r#use)?.punct(';'); self.uses.push(builder); Ok(()) } /// Generate a struct with the given name. See [`GenStruct`] for more info. pub fn generate_struct(&mut self, name: impl Into) -> GenStruct { GenStruct::new(self, name) } /// Generate an enum with the given name. See [`GenEnum`] for more info. pub fn generate_enum(&mut self, name: impl Into) -> GenEnum { GenEnum::new(self, name) } /// Generate an `impl ` implementation. See [`Impl`] for more information. pub fn r#impl(&mut self, name: impl Into) -> Impl { Impl::new(self, name) } /// Generate an `impl ` implementation. See [`Impl`] for more information. /// /// Alias for [`impl`] which doesn't need a `r#` prefix. /// /// [`impl`]: #method.impl pub fn generate_impl(&mut self, name: impl Into) -> Impl { Impl::new(self, name) } } impl<'a, P: Parent> Drop for GenerateMod<'a, P> { fn drop(&mut self) { let mut builder = StreamBuilder::new(); if self.vis == Visibility::Pub { builder.ident_str("pub"); } builder .ident_str("mod") .ident(self.name.clone()) .group(Delimiter::Brace, |group| { for r#use in std::mem::take(&mut self.uses) { group.append(r#use); } group.append(std::mem::take(&mut self.content)); Ok(()) }) .unwrap(); self.parent.append(builder); } } impl Parent for GenerateMod<'_, P> { fn append(&mut self, builder: StreamBuilder) { self.content.append(builder); } fn name(&self) -> &crate::prelude::Ident { &self.name } fn generics(&self) -> Option<&crate::parse::Generics> { None } fn generic_constraints(&self) -> Option<&crate::parse::GenericConstraints> { None } } virtue-0.0.18/src/generate/generator.rs000064400000000000000000000327751046102023000161630ustar 00000000000000use super::{GenEnum, GenStruct, GenerateMod, Impl, ImplFor, StreamBuilder, StringOrIdent}; use crate::parse::{GenericConstraints, Generics}; use crate::prelude::{Ident, TokenStream}; #[must_use] /// The generator is used to generate code. /// /// Often you will want to use [`impl_for`] to generate an `impl for `. /// /// [`impl_for`]: #method.impl_for pub struct Generator { name: Ident, generics: Option, generic_constraints: Option, stream: StreamBuilder, } impl Generator { pub(crate) fn new( name: Ident, generics: Option, generic_constraints: Option, ) -> Self { Self { name, generics, generic_constraints, stream: StreamBuilder::new(), } } /// Return the name for the struct or enum that this is going to be implemented on. pub fn target_name(&self) -> Ident { self.name.clone() } /// Generate an `impl ` implementation. See [`Impl`] for more information. /// /// This will default to the type that is associated with this generator. If you need to generate an impl for another type you can use `impl_for_other_type` pub fn r#impl(&mut self) -> Impl { Impl::with_parent_name(self) } /// Generate an `impl ` implementation. See [`Impl`] for more information. /// /// Alias for [`impl`] which doesn't need a `r#` prefix. /// /// [`impl`]: #method.impl pub fn generate_impl(&mut self) -> Impl { Impl::with_parent_name(self) } /// Generate an `for for ` implementation. See [ImplFor] for more information. /// /// This will default to the type that is associated with this generator. If you need to generate an impl for another type you can use `impl_trait_for_other_type` pub fn impl_for(&mut self, trait_name: impl Into) -> ImplFor { ImplFor::new( self, self.name.clone().into(), Some(trait_name.into().into()), ) } /// Generate an `impl ` block. See [ImplFor] for more information. /// ``` /// # use virtue::prelude::*; /// # let mut generator = Generator::with_name("Baz"); /// generator.impl_for_other_type("Foo"); /// /// // will output: /// // impl Foo { } /// # generator.assert_eq("impl Foo { }"); /// ``` pub fn impl_for_other_type(&mut self, type_name: impl Into) -> ImplFor { ImplFor::new(self, type_name.into(), None) } /// Generate an `impl for ` block. See [ImplFor] for more information. /// ``` /// # use virtue::prelude::*; /// # let mut generator = Generator::with_name("Baz"); /// generator.impl_trait_for_other_type("Foo", "Bar"); /// /// // will output: /// // impl Foo for Bar { } /// # generator.assert_eq("impl Foo for Bar { }"); /// ``` pub fn impl_trait_for_other_type( &mut self, trait_name: impl Into, type_name: impl Into, ) -> ImplFor { ImplFor::new(self, type_name.into(), Some(trait_name.into())) } /// Generate an `for <..lifetimes> for ` implementation. See [ImplFor] for more information. /// /// Note: /// - Lifetimes should _not_ have the leading apostrophe. /// - `trait_name` should _not_ have custom lifetimes. These will be added automatically. /// /// ``` /// # use virtue::prelude::*; /// # let mut generator = Generator::with_name("Bar"); /// generator.impl_for_with_lifetimes("Foo", ["a", "b"]); /// /// // will output: /// // impl<'a, 'b> Foo<'a, 'b> for StructOrEnum { } /// # generator.assert_eq("impl < 'a , 'b > Foo < 'a , 'b > for Bar { }"); /// ``` /// /// The new lifetimes are not associated with any existing lifetimes. If you want this behavior you can call `.impl_for_with_lifetimes(...).new_lifetimes_depend_on_existing()` /// /// ``` /// # use virtue::prelude::*; /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); /// // given a derive on `struct<'a> Bar<'a>` /// generator.impl_for_with_lifetimes("Foo", ["b"]).new_lifetimes_depend_on_existing(); /// /// // will output: /// // impl<'a, 'b> Foo<'b> for Bar<'a> where 'b: 'a { } /// # generator.assert_eq("impl < 'b , 'a > Foo < 'b > for Bar < 'a > where 'b : 'a { }"); /// ``` pub fn impl_for_with_lifetimes( &mut self, trait_name: T, lifetimes: ITER, ) -> ImplFor where ITER: IntoIterator, ITER::Item: Into, T: Into, { ImplFor::new(self, self.name.clone().into(), Some(trait_name.into())) .with_lifetimes(lifetimes) } /// Generate a struct with the given name. See [`GenStruct`] for more info. pub fn generate_struct(&mut self, name: impl Into) -> GenStruct { GenStruct::new(self, name) } /// Generate an enum with the given name. See [`GenEnum`] for more info. pub fn generate_enum(&mut self, name: impl Into) -> GenEnum { GenEnum::new(self, name) } /// Generate a `mod { ... }`. See [`GenerateMod`] for more info. pub fn generate_mod(&mut self, mod_name: impl Into) -> GenerateMod { GenerateMod::new(self, mod_name) } /// Export the current stream to a file, making it very easy to debug the output of a derive macro. /// This will try to find rust's `target` directory, and write `target/generated//_.rs`. /// /// Will return `true` if the file is written, `false` otherwise. /// /// The outputted file is unformatted. Use `cargo fmt -- target/generated//.rs` to format the file. pub fn export_to_file(&self, crate_name: &str, file_postfix: &str) -> bool { use std::io::Write; if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") { let mut path = std::path::PathBuf::from(var); loop { { let mut path = path.clone(); path.push("target"); if path.exists() { path.push("generated"); path.push(crate_name); if std::fs::create_dir_all(&path).is_err() { return false; } path.push(format!("{}_{}.rs", self.target_name(), file_postfix)); if let Ok(mut file) = std::fs::File::create(path) { let _ = file.write_all(self.stream.stream.to_string().as_bytes()); return true; } } } if let Some(parent) = path.parent() { path = parent.into(); } else { break; } } } false } /// Consume the contents of this generator. This *must* be called, or else the generator will panic on drop. pub fn finish(mut self) -> crate::prelude::Result { Ok(std::mem::take(&mut self.stream).stream) } } #[cfg(feature = "proc-macro2")] impl Generator { /// Create a new generator with the name `name`. This is useful for testing purposes in combination with the `assert_eq` function. pub fn with_name(name: &str) -> Self { Self::new( Ident::new(name, crate::prelude::Span::call_site()), None, None, ) } /// Add a lifetime to this generator. pub fn with_lifetime(mut self, lt: &str) -> Self { self.generics .get_or_insert_with(|| Generics(Vec::new())) .push(crate::parse::Generic::Lifetime(crate::parse::Lifetime { ident: crate::prelude::Ident::new(lt, crate::prelude::Span::call_site()), constraint: Vec::new(), })); self } /// Assert that the generated code in this generator matches the given string. This is useful for testing purposes in combination with the `with_name` function. pub fn assert_eq(&self, expected: &str) { assert_eq!(expected, self.stream.stream.to_string()); } } impl Drop for Generator { fn drop(&mut self) { if !self.stream.stream.is_empty() && !std::thread::panicking() { eprintln!("WARNING: Generator dropped but the stream is not empty. Please call `.finish()` on the generator"); } } } impl super::Parent for Generator { fn append(&mut self, builder: StreamBuilder) { self.stream.append(builder); } fn name(&self) -> &Ident { &self.name } fn generics(&self) -> Option<&Generics> { self.generics.as_ref() } fn generic_constraints(&self) -> Option<&GenericConstraints> { self.generic_constraints.as_ref() } } #[cfg(test)] mod test { use proc_macro2::Span; use crate::token_stream; use super::*; #[test] fn impl_for_with_lifetimes() { // No generics let mut generator = Generator::new(Ident::new("StructOrEnum", Span::call_site()), None, None); let _ = generator.impl_for_with_lifetimes("Foo", ["a", "b"]); let output = generator.finish().unwrap(); assert_eq!( output .into_iter() .map(|v| v.to_string()) .collect::(), token_stream("impl<'a, 'b> Foo<'a, 'b> for StructOrEnum { }") .map(|v| v.to_string()) .collect::(), ); //with simple generics let mut generator = Generator::new( Ident::new("StructOrEnum", Span::call_site()), Generics::try_take(&mut token_stream("")).unwrap(), None, ); let _ = generator.impl_for_with_lifetimes("Foo", ["a", "b"]); let output = generator.finish().unwrap(); assert_eq!( output .into_iter() .map(|v| v.to_string()) .collect::(), token_stream("impl<'a, 'b, T1, T2> Foo<'a, 'b> for StructOrEnum { }") .map(|v| v.to_string()) .collect::() ); // with lifetimes let mut generator = Generator::new( Ident::new("StructOrEnum", Span::call_site()), Generics::try_take(&mut token_stream("<'alpha, 'beta>")).unwrap(), None, ); let _ = generator.impl_for_with_lifetimes("Foo", ["a", "b"]); let output = generator.finish().unwrap(); assert_eq!( output .into_iter() .map(|v| v.to_string()) .collect::(), token_stream( "impl<'a, 'b, 'alpha, 'beta> Foo<'a, 'b> for StructOrEnum<'alpha, 'beta> { }" ) .map(|v| v.to_string()) .collect::() ); } #[test] fn impl_for_with_trait_generics() { let mut generator = Generator::new( Ident::new("StructOrEnum", Span::call_site()), Generics::try_take(&mut token_stream("<'a>")).unwrap(), None, ); let _ = generator.impl_for("Foo").with_trait_generics(["&'a str"]); let output = generator.finish().unwrap(); assert_eq!( output .into_iter() .map(|v| v.to_string()) .collect::(), token_stream("impl<'a> Foo<&'a str> for StructOrEnum<'a> { }") .map(|v| v.to_string()) .collect::(), ); } #[test] fn impl_for_with_impl_generics() { //with simple generics let mut generator = Generator::new( Ident::new("StructOrEnum", Span::call_site()), Generics::try_take(&mut token_stream("")).unwrap(), None, ); let _ = generator.impl_for("Foo").with_impl_generics(["Bar"]); let output = generator.finish().unwrap(); assert_eq!( output .into_iter() .map(|v| v.to_string()) .collect::(), token_stream("impl Foo for StructOrEnum { }") .map(|v| v.to_string()) .collect::() ); // with lifetimes let mut generator = Generator::new( Ident::new("StructOrEnum", Span::call_site()), Generics::try_take(&mut token_stream("<'alpha, 'beta>")).unwrap(), None, ); let _ = generator.impl_for("Foo").with_impl_generics(["Bar"]); let output = generator.finish().unwrap(); assert_eq!( output .into_iter() .map(|v| v.to_string()) .collect::(), token_stream("impl<'alpha, 'beta, Bar> Foo for StructOrEnum<'alpha, 'beta> { }") .map(|v| v.to_string()) .collect::() ); } } virtue-0.0.18/src/generate/impl.rs000064400000000000000000000152611046102023000151250ustar 00000000000000use super::{generate_item::FnParent, FnBuilder, GenConst, Generator, Parent, StreamBuilder}; use crate::{ parse::{GenericConstraints, Generics}, prelude::{Delimiter, Result}, }; #[must_use] /// A helper struct for implementing functions for a given struct or enum. pub struct Impl<'a, P: Parent> { parent: &'a mut P, outer_attr: Vec, inner_attr: Vec, name: String, // pub(super) group: StreamBuilder, consts: Vec, custom_generic_constraints: Option, fns: Vec<(StreamBuilder, StreamBuilder)>, } impl<'a, P: Parent> Impl<'a, P> { pub(super) fn with_parent_name(parent: &'a mut P) -> Self { Self { outer_attr: Vec::new(), inner_attr: Vec::new(), name: parent.name().to_string(), parent, consts: Vec::new(), custom_generic_constraints: None, fns: Vec::new(), } } pub(super) fn new(parent: &'a mut P, name: impl Into) -> Self { Self { outer_attr: Vec::new(), inner_attr: Vec::new(), parent, name: name.into(), consts: Vec::new(), custom_generic_constraints: None, fns: Vec::new(), } } /// Add a outer attribute to the trait implementation pub fn impl_outer_attr(&mut self, attr: impl AsRef) -> Result { let mut builder = StreamBuilder::new(); builder.punct('#').group(Delimiter::Bracket, |builder| { builder.push_parsed(attr)?; Ok(()) })?; self.outer_attr.push(builder); Ok(()) } /// Add a inner attribute to the trait implementation pub fn impl_inner_attr(&mut self, attr: impl AsRef) -> Result { let mut builder = StreamBuilder::new(); builder .punct('#') .punct('!') .group(Delimiter::Brace, |builder| { builder.push_parsed(attr)?; Ok(()) })?; self.inner_attr.push(builder); Ok(()) } /// Add a function to the trait implementation. /// /// `generator.impl().generate_fn("bar")` results in code like: /// /// ```ignore /// impl { /// fn bar() {} /// } /// ``` /// /// See [`FnBuilder`] for more options, as well as information on how to fill the function body. pub fn generate_fn(&mut self, name: impl Into) -> FnBuilder { FnBuilder::new(self, name) } /// Add a const to the trait implementation /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// generator.impl_for("Foo") /// .generate_const("BAR", "u8") /// .with_value(|b| { /// b.push_parsed("5")?; /// Ok(()) /// })?; /// # generator.assert_eq("impl Foo for Bar { const BAR : u8 = 5 ; }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// impl Foo for { /// const BAR: u8 = 5; /// } pub fn generate_const(&mut self, name: impl Into, ty: impl Into) -> GenConst { GenConst::new(&mut self.consts, name, ty) } } impl<'a> Impl<'a, Generator> { /// Modify the generic constraints of a type. /// This can be used to add additional type constraints to your implementation. /// /// ```ignore /// // Your derive: /// #[derive(YourTrait)] /// pub struct Foo { /// ... /// } /// /// // With this code: /// generator /// .r#impl() /// .modify_generic_constraints(|generics, constraints| { /// for g in generics.iter_generics() { /// constraints.push_generic(g, "YourTrait"); /// } /// }) /// /// // will generate: /// impl Foo /// where B: YourTrait // <- /// { /// } /// ``` /// /// Note that this function is only implemented when you call `.r#impl` on [`Generator`]. pub fn modify_generic_constraints(&mut self, cb: CB) -> &mut Self where CB: FnOnce(&Generics, &mut GenericConstraints), { if let Some(generics) = self.parent.generics() { let constraints = self.custom_generic_constraints.get_or_insert_with(|| { self.parent .generic_constraints() .cloned() .unwrap_or_default() }); cb(generics, constraints); } self } } impl<'a, P: Parent> FnParent for Impl<'a, P> { fn append(&mut self, fn_definition: StreamBuilder, fn_body: StreamBuilder) -> Result { self.fns.push((fn_definition, fn_body)); Ok(()) } } impl<'a, P: Parent> Drop for Impl<'a, P> { fn drop(&mut self) { if std::thread::panicking() { return; } let mut builder = StreamBuilder::new(); for attr in std::mem::take(&mut self.outer_attr) { builder.append(attr); } builder.ident_str("impl"); if let Some(generics) = self.parent.generics() { builder.append(generics.impl_generics()); } builder.push_parsed(&self.name).unwrap(); if let Some(generics) = self.parent.generics() { builder.append(generics.type_generics()); } if let Some(generic_constraints) = self.custom_generic_constraints.take() { builder.append(generic_constraints.where_clause()); } else if let Some(generic_constraints) = self.parent.generic_constraints() { builder.append(generic_constraints.where_clause()); } builder .group(Delimiter::Brace, |builder| { for attr in std::mem::take(&mut self.inner_attr) { builder.append(attr); } for r#const in std::mem::take(&mut self.consts) { builder.append(r#const); } for (fn_def, fn_body) in std::mem::take(&mut self.fns) { builder.append(fn_def); builder .group(Delimiter::Brace, |body| { *body = fn_body; Ok(()) }) .unwrap(); } Ok(()) }) .unwrap(); self.parent.append(builder); } } virtue-0.0.18/src/generate/impl_for.rs000064400000000000000000000302241046102023000157670ustar 00000000000000use super::{generate_item::FnParent, FnBuilder, GenConst, Parent, StreamBuilder, StringOrIdent}; use crate::{ parse::{GenericConstraints, Generics}, prelude::{Delimiter, Result}, }; #[must_use] /// A helper struct for implementing a trait for a given struct or enum. pub struct ImplFor<'a, P: Parent> { generator: &'a mut P, outer_attr: Vec, inner_attr: Vec, type_name: StringOrIdent, trait_name: Option, lifetimes: Option>, trait_generics: Option>, impl_generics: Vec, consts: Vec, custom_generic_constraints: Option, impl_types: Vec, fns: Vec<(StreamBuilder, StreamBuilder)>, } impl<'a, P: Parent> ImplFor<'a, P> { pub(super) fn new( generator: &'a mut P, type_name: StringOrIdent, trait_name: Option, ) -> Self { Self { generator, outer_attr: Vec::new(), inner_attr: Vec::new(), trait_name, type_name, lifetimes: None, trait_generics: None, impl_generics: vec![], consts: Vec::new(), custom_generic_constraints: None, impl_types: Vec::new(), fns: Vec::new(), } } /// Internal helper function to set lifetimes pub(crate) fn with_lifetimes(mut self, lifetimes: ITER) -> Self where ITER: IntoIterator, ITER::Item: Into, { self.lifetimes = Some(lifetimes.into_iter().map(Into::into).collect()); self } /// Make the new lifetimes added by `Generator::impl_for_with_lifetimes` depend on the existing lifetimes from the original derive. /// /// See [`impl_for_with_lifetimes`] for more information. /// /// Calling this method in any other context has no effect. /// /// [`impl_for_with_lifetimes`]: struct.Generator.html#method.impl_for_with_lifetimes pub fn new_lifetimes_depend_on_existing(mut self) -> Self { if let Some(new_lt) = &self.lifetimes { if let Some(generics) = self.generator.generics() { let constraints = self.custom_generic_constraints.get_or_insert_with(|| { self.generator .generic_constraints() .cloned() .unwrap_or_default() }); for old_lt in generics.iter_lifetimes() { for new_lt in new_lt { constraints .push_parsed_constraint(format!("'{}: '{}", new_lt, old_lt.ident)) .expect("Could not ensure new lifetimes depend on existing lifetimes"); } } } } self } /// Add generic parameters to the trait implementation. ///``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// generator.impl_for("Foo") /// .with_trait_generics(["Baz"]); /// # generator.assert_eq("impl Foo < Baz > for Bar { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// impl Foo for { /// const BAR: u8 = 5; /// } /// ``` pub fn with_trait_generics(mut self, generics: ITER) -> Self where ITER: IntoIterator, ITER::Item: Into, { self.trait_generics = Some(generics.into_iter().map(Into::into).collect()); self } /// Add generic parameters to the impl block. ///``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// generator.impl_for("Foo") /// .with_impl_generics(["Baz"]); /// # generator.assert_eq("impl < Baz > Foo for Bar { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// impl Foo for Bar { } /// ``` pub fn with_impl_generics(mut self, generics: ITER) -> Self where ITER: IntoIterator, ITER::Item: Into, { self.impl_generics = generics.into_iter().map(Into::into).collect(); self } /// Add a outer attribute to the trait implementation pub fn impl_outer_attr(&mut self, attr: impl AsRef) -> Result { let mut builder = StreamBuilder::new(); builder.punct('#').group(Delimiter::Bracket, |builder| { builder.push_parsed(attr)?; Ok(()) })?; self.outer_attr.push(builder); Ok(()) } /// Add a inner attribute to the trait implementation pub fn impl_inner_attr(&mut self, attr: impl AsRef) -> Result { let mut builder = StreamBuilder::new(); builder .punct('#') .punct('!') .group(Delimiter::Brace, |builder| { builder.push_parsed(attr)?; Ok(()) })?; self.inner_attr.push(builder); Ok(()) } /// Add a const to the trait implementation /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Bar"); /// generator.impl_for("Foo") /// .generate_const("BAR", "u8") /// .with_value(|b| { /// b.push_parsed("5")?; /// Ok(()) /// })?; /// # generator.assert_eq("impl Foo for Bar { const BAR : u8 = 5 ; }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// impl Foo for { /// const BAR: u8 = 5; /// } pub fn generate_const(&mut self, name: impl Into, ty: impl Into) -> GenConst { GenConst::new(&mut self.consts, name, ty) } /// Add a function to the trait implementation. /// /// `generator.impl_for("Foo").generate_fn("bar")` results in code like: /// /// ```ignore /// impl Foo for { /// fn bar() {} /// } /// ``` /// /// See [`FnBuilder`] for more options, as well as information on how to fill the function body. pub fn generate_fn(&mut self, name: impl Into) -> FnBuilder> { FnBuilder::new(self, name) } /// Add a type to the impl /// /// `generator.impl_for("Foo").impl_type("Bar", "u8")` results in code like: /// /// ```ignore /// impl Foo for { /// type Bar = u8; /// } /// ``` pub fn impl_type(&mut self, name: impl AsRef, value: impl AsRef) -> Result { let mut builder = StreamBuilder::new(); builder .ident_str("type") .push_parsed(name)? .punct('=') .push_parsed(value)? .punct(';'); self.impl_types.push(builder); Ok(()) } /// /// Modify the generic constraints of a type. /// This can be used to add additional type constraints to your implementation. /// /// ```ignore /// // Your derive: /// #[derive(YourTrait)] /// pub struct Foo { /// ... /// } /// /// // With this code: /// generator /// .impl_for("YourTrait") /// .modify_generic_constraints(|generics, constraints| { /// for g in generics.iter_generics() { /// constraints.push_generic(g, "YourTrait"); /// } /// }) /// /// // will generate: /// impl YourTrait for Foo /// where B: YourTrait // <- /// { /// } /// ``` /// pub fn modify_generic_constraints(&mut self, cb: CB) -> Result<&mut Self> where CB: FnOnce(&Generics, &mut GenericConstraints) -> Result, { if let Some(generics) = self.generator.generics() { let constraints = self.custom_generic_constraints.get_or_insert_with(|| { self.generator .generic_constraints() .cloned() .unwrap_or_default() }); cb(generics, constraints)?; } Ok(self) } } impl<'a, P: Parent> FnParent for ImplFor<'a, P> { fn append(&mut self, fn_definition: StreamBuilder, fn_body: StreamBuilder) -> Result { self.fns.push((fn_definition, fn_body)); Ok(()) } } impl Drop for ImplFor<'_, P> { fn drop(&mut self) { if std::thread::panicking() { return; } let mut builder = StreamBuilder::new(); for attr in std::mem::take(&mut self.outer_attr) { builder.append(attr); } self.generate_impl_definition(&mut builder); builder .group(Delimiter::Brace, |builder| { for attr in std::mem::take(&mut self.inner_attr) { builder.append(attr); } for ty in std::mem::take(&mut self.impl_types) { builder.append(ty); } for r#const in std::mem::take(&mut self.consts) { builder.append(r#const); } for (fn_def, fn_body) in std::mem::take(&mut self.fns) { builder.append(fn_def); builder .group(Delimiter::Brace, |body| { *body = fn_body; Ok(()) }) .unwrap(); } Ok(()) }) .unwrap(); self.generator.append(builder); } } impl ImplFor<'_, P> { fn generate_impl_definition(&mut self, builder: &mut StreamBuilder) { builder.ident_str("impl"); let impl_generics = self.impl_generics.as_slice(); if let Some(lifetimes) = &self.lifetimes { if let Some(generics) = self.generator.generics() { builder.append(generics.impl_generics_with_additional(lifetimes, impl_generics)); } else { append_lifetimes_and_generics(builder, lifetimes, impl_generics); } } else if let Some(generics) = self.generator.generics() { builder.append(generics.impl_generics_with_additional(&[], impl_generics)); } else if !impl_generics.is_empty() { append_lifetimes_and_generics(builder, &[], impl_generics) } if let Some(t) = &self.trait_name { builder.push_parsed(t.to_string()).unwrap(); let lifetimes = self.lifetimes.as_deref().unwrap_or_default(); let generics = self.trait_generics.as_deref().unwrap_or_default(); append_lifetimes_and_generics(builder, lifetimes, generics); builder.ident_str("for"); } builder.push_parsed(self.type_name.to_string()).unwrap(); if let Some(generics) = &self.generator.generics() { builder.append(generics.type_generics()); } if let Some(generic_constraints) = self.custom_generic_constraints.take() { builder.append(generic_constraints.where_clause()); } else if let Some(generic_constraints) = &self.generator.generic_constraints() { builder.append(generic_constraints.where_clause()); } } } fn append_lifetimes_and_generics( builder: &mut StreamBuilder, lifetimes: &[String], generics: &[String], ) { if lifetimes.is_empty() && generics.is_empty() { return; } builder.punct('<'); for (idx, lt) in lifetimes.iter().enumerate() { if idx > 0 { builder.punct(','); } builder.lifetime_str(lt); } for (idx, gen) in generics.iter().enumerate() { if idx > 0 || !lifetimes.is_empty() { builder.punct(','); } builder.push_parsed(gen).unwrap(); } builder.punct('>'); } virtue-0.0.18/src/generate/mod.rs000064400000000000000000000307701046102023000147450ustar 00000000000000//! Code to help generate functions. //! //! The structure is: //! //! - [`Generator`] //! - `.impl_for()`: [`ImplFor`] //! - `.generate_fn()`: [`FnBuilder`] //! - `.body(|builder| { .. })`: [`StreamBuilder`] //! //! Afterwards, [`Generator::finish()`] **must** be called to take out the [`TokenStream`] produced. //! //! [`Generator::finish()`]: struct.Generator.html#method.finish //! [`TokenStream`]: ../prelude/struct.TokenStream.html mod gen_enum; mod gen_struct; mod generate_item; mod generate_mod; mod generator; mod r#impl; mod impl_for; mod stream_builder; use crate::parse::Visibility; use crate::{ parse::{GenericConstraints, Generics}, prelude::{Delimiter, Ident, TokenStream}, }; use std::fmt; use std::marker::PhantomData; pub use self::gen_enum::GenEnum; pub use self::gen_struct::GenStruct; pub use self::generate_item::{FnBuilder, FnSelfArg, GenConst}; pub use self::generate_mod::GenerateMod; pub use self::generator::Generator; pub use self::impl_for::ImplFor; pub use self::r#impl::Impl; pub use self::stream_builder::{PushParseError, StreamBuilder}; /// Helper trait to make it possible to nest several builders. Internal use only. #[allow(missing_docs)] pub trait Parent { fn append(&mut self, builder: StreamBuilder); fn name(&self) -> &Ident; fn generics(&self) -> Option<&Generics>; fn generic_constraints(&self) -> Option<&GenericConstraints>; } /// Helper enum to differentiate between a [`Ident`] or a [`String`]. #[allow(missing_docs)] pub enum StringOrIdent { String(String), // Note that when this is a `string` this could be much more than a single ident. // Therefor you should never use [`StreamBuilder`]`.ident_str(StringOrIdent.to_string())`, but instead use `.push_parsed(StringOrIdent.to_string())?`. Ident(Ident), } impl fmt::Display for StringOrIdent { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::String(s) => s.fmt(f), Self::Ident(i) => i.fmt(f), } } } impl From for StringOrIdent { fn from(s: String) -> Self { Self::String(s) } } impl From for StringOrIdent { fn from(i: Ident) -> Self { Self::Ident(i) } } impl<'a> From<&'a str> for StringOrIdent { fn from(s: &'a str) -> Self { Self::String(s.to_owned()) } } /// A path of identifiers, like `mod::Type`. pub struct Path(Vec); impl From for Path { fn from(s: String) -> Self { StringOrIdent::from(s).into() } } impl From for Path { fn from(i: Ident) -> Self { StringOrIdent::from(i).into() } } impl From<&str> for Path { fn from(s: &str) -> Self { StringOrIdent::from(s).into() } } impl From for Path { fn from(value: StringOrIdent) -> Self { Self(vec![value]) } } impl FromIterator for Path { fn from_iter>(iter: T) -> Self { iter.into_iter().map(StringOrIdent::from).collect() } } impl FromIterator for Path { fn from_iter>(iter: T) -> Self { iter.into_iter().map(StringOrIdent::from).collect() } } impl<'a> FromIterator<&'a str> for Path { fn from_iter>(iter: T) -> Self { iter.into_iter().map(StringOrIdent::from).collect() } } impl FromIterator for Path { fn from_iter>(iter: T) -> Self { Self(iter.into_iter().collect()) } } impl IntoIterator for Path { type Item = StringOrIdent; type IntoIter = std::vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } /// A struct or enum variant field. struct Field { name: String, vis: Visibility, ty: String, attributes: Vec, } impl Field { fn new(name: impl Into, vis: Visibility, ty: impl Into) -> Self { Self { name: name.into(), vis, ty: ty.into(), attributes: Vec::new(), } } } /// A builder for struct or enum variant fields. pub struct FieldBuilder<'a, P> { fields: &'a mut Vec, _parent: PhantomData

, // Keep this to disallow `pub` on enum fields } impl

FieldBuilder<'_, P> { /// Add an attribute to the field. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Fooz"); /// generator /// .generate_struct("Foo") /// .add_field("foo", "u16") /// .make_pub() /// .with_attribute("serde", |b| { /// b.push_parsed("(default)")?; /// Ok(()) /// })?; /// generator /// .generate_enum("Bar") /// .add_value("Baz") /// .add_field("baz", "bool") /// .with_attribute("serde", |b| { /// b.push_parsed("(default)")?; /// Ok(()) /// })?; /// # generator.assert_eq("struct Foo { # [serde (default)] pub foo : u16 , } \ /// enum Bar { Baz { # [serde (default)] baz : bool , } , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// struct Foo { /// #[serde(default)] /// pub bar: u16 /// } /// /// enum Bar { /// Baz { /// #[serde(default)] /// baz: bool /// } /// } /// ``` pub fn with_attribute( &mut self, name: impl AsRef, value: impl FnOnce(&mut StreamBuilder) -> crate::Result, ) -> crate::Result<&mut Self> { self.current().with_attribute(name, value)?; Ok(self) } /// Add a parsed attribute to the field. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Fooz"); /// generator /// .generate_struct("Foo") /// .add_field("foo", "u16") /// .make_pub() /// .with_parsed_attribute("serde(default)")?; /// generator /// .generate_enum("Bar") /// .add_value("Baz") /// .add_field("baz", "bool") /// .with_parsed_attribute("serde(default)")?; /// # generator.assert_eq("struct Foo { # [serde (default)] pub foo : u16 , } \ /// enum Bar { Baz { # [serde (default)] baz : bool , } , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// struct Foo { /// #[serde(default)] /// pub bar: u16 /// } /// /// enum Bar { /// Baz { /// #[serde(default)] /// baz: bool /// } /// } /// ``` pub fn with_parsed_attribute( &mut self, attribute: impl AsRef, ) -> crate::Result<&mut Self> { self.current().with_parsed_attribute(attribute)?; Ok(self) } /// Add a token stream as an attribute to the field. /// /// ``` /// # use virtue::prelude::{Generator, TokenStream}; /// # let mut generator = Generator::with_name("Fooz"); /// let attribute = "serde(default)".parse::().unwrap(); /// generator /// .generate_struct("Foo") /// .add_field("foo", "u16") /// .make_pub() /// .with_attribute_stream(attribute); /// # generator.assert_eq("struct Foo { # [serde (default)] pub foo : u16 , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore /// struct Foo { /// #[serde(default)] /// pub bar: u16 /// } /// ``` pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { self.current().with_attribute_stream(attribute); self } /// Add a field to the parent type. /// /// ``` /// # use virtue::prelude::Generator; /// # let mut generator = Generator::with_name("Fooz"); /// generator /// .generate_struct("Foo") /// .add_field("foo", "u16") /// .add_field("bar", "bool"); /// # generator.assert_eq("struct Foo { foo : u16 , bar : bool , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ``` /// struct Foo { /// foo: u16, /// bar: bool /// } /// ``` pub fn add_field(&mut self, name: impl Into, ty: impl Into) -> &mut Self { self.fields.push(Field::new(name, Visibility::Default, ty)); self } } // Only allow `pub` on struct fields impl<'a, P: Parent> FieldBuilder<'_, GenStruct<'a, P>> { /// Make the field public. pub fn make_pub(&mut self) -> &mut Self { self.current().vis = Visibility::Pub; self } } impl<'a, P> From<&'a mut Vec> for FieldBuilder<'a, P> { fn from(fields: &'a mut Vec) -> Self { Self { fields, _parent: PhantomData, } } } impl

FieldBuilder<'_, P> { fn current(&mut self) -> &mut Field { // A field is always added before this is called, so the unwrap doesn't fail. self.fields.last_mut().unwrap() } } /// A helper trait to share attribute code between struct and enum generators. trait AttributeContainer { fn derives(&mut self) -> &mut Vec; fn attributes(&mut self) -> &mut Vec; fn with_derive(&mut self, derive: impl Into) -> &mut Self { self.derives().push(derive.into()); self } fn with_derives>(&mut self, derives: impl IntoIterator) -> &mut Self { self.derives().extend(derives.into_iter().map(Into::into)); self } fn with_attribute( &mut self, name: impl AsRef, value: impl FnOnce(&mut StreamBuilder) -> crate::Result, ) -> crate::Result<&mut Self> { let mut stream = StreamBuilder::new(); value(stream.ident_str(name))?; self.attributes().push(stream); Ok(self) } fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> crate::Result<&mut Self> { let mut stream = StreamBuilder::new(); stream.push_parsed(attribute)?; self.attributes().push(stream); Ok(self) } fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { let stream = StreamBuilder { stream: attribute.into(), }; self.attributes().push(stream); self } fn build_derives(&mut self, b: &mut StreamBuilder) -> &mut Self { let derives = std::mem::take(self.derives()); if !derives.is_empty() { build_attribute(b, |b| { b.ident_str("derive").group(Delimiter::Parenthesis, |b| { for (idx, derive) in derives.into_iter().enumerate() { if idx > 0 { b.punct(','); } for (idx, component) in derive.into_iter().enumerate() { if idx > 0 { b.puncts("::"); } match component { StringOrIdent::String(s) => b.ident_str(s), StringOrIdent::Ident(i) => b.ident(i), }; } } Ok(()) }) }) .expect("could not build derives"); } self } fn build_attributes(&mut self, b: &mut StreamBuilder) -> &mut Self { for attr in std::mem::take(self.attributes()) { build_attribute(b, |b| Ok(b.extend(attr.stream))).expect("could not build attribute"); } self } } impl AttributeContainer for Field { fn derives(&mut self) -> &mut Vec { unreachable!("fields cannot have derives") } fn attributes(&mut self) -> &mut Vec { &mut self.attributes } } fn build_attribute(b: &mut StreamBuilder, build: T) -> crate::Result where T: FnOnce(&mut StreamBuilder) -> crate::Result<&mut StreamBuilder>, { b.punct('#').group(Delimiter::Bracket, |b| { build(b)?; Ok(()) })?; Ok(()) } virtue-0.0.18/src/generate/stream_builder.rs000064400000000000000000000154301046102023000171630ustar 00000000000000use crate::prelude::{ Delimiter, Group, Ident, LexError, Literal, Punct, Result, Spacing, Span, TokenStream, TokenTree, }; use std::str::FromStr; /// A helper struct build around a [TokenStream] to make it easier to build code. #[must_use] #[derive(Default)] pub struct StreamBuilder { pub(crate) stream: TokenStream, } impl StreamBuilder { /// Generate a new StreamBuilder pub fn new() -> Self { Self { stream: TokenStream::new(), } } /// Add multiple `TokenTree` items to the stream. pub fn extend(&mut self, item: impl IntoIterator) -> &mut Self { self.stream.extend(item); self } /// Append another StreamBuilder to the current StreamBuilder. pub fn append(&mut self, builder: StreamBuilder) -> &mut Self { self.stream.extend(builder.stream); self } /// Push a single token to the stream. pub fn push(&mut self, item: impl Into) -> &mut Self { self.stream.extend([item.into()]); self } /// Attempt to parse the given string as valid Rust code, and append the parsed result to the internal stream. /// /// Currently panics if the string could not be parsed as valid Rust code. pub fn push_parsed(&mut self, item: impl AsRef) -> Result<&mut Self> { let tokens = TokenStream::from_str(item.as_ref()).map_err(|e| PushParseError { error: e, code: item.as_ref().to_string(), })?; self.stream.extend(tokens); Ok(self) } /// Push a single ident to the stream. An ident is any word that a code file may contain, e.g. `fn`, `struct`, `where`, names of functions and structs, etc. pub fn ident(&mut self, ident: Ident) -> &mut Self { self.stream.extend([TokenTree::Ident(ident)]); self } /// Push a single ident to the stream. An ident is any word that a code file may contain, e.g. `fn`, `struct`, `where`, names of functions and structs, etc. pub fn ident_str(&mut self, ident: impl AsRef) -> &mut Self { self.stream.extend([TokenTree::Ident(Ident::new( ident.as_ref(), Span::call_site(), ))]); self } /// Add a group. A group is any block surrounded by `{ .. }`, `[ .. ]` or `( .. )`. /// /// `delim` indicates which group it is. The `inner` callback is used to fill the contents of the group. pub fn group(&mut self, delim: Delimiter, inner: FN) -> crate::Result<&mut Self> where FN: FnOnce(&mut StreamBuilder) -> crate::Result<()>, { let mut stream = StreamBuilder::new(); inner(&mut stream)?; self.stream .extend([TokenTree::Group(Group::new(delim, stream.stream))]); Ok(self) } /// Add a single punctuation to the stream. Puncts are single-character tokens like `.`, `<`, `#`, etc /// /// Note that this should not be used for multi-punct constructions like `::` or `->`. For that use [`puncts`] instead. /// /// [`puncts`]: #method.puncts pub fn punct(&mut self, p: char) -> &mut Self { self.stream .extend([TokenTree::Punct(Punct::new(p, Spacing::Alone))]); self } /// Add multiple punctuations to the stream. Multi punct tokens are e.g. `::`, `->` and `=>`. /// /// Note that this is the only way to add multi punct tokens. /// If you were to use [`Punct`] to insert `->` it would be inserted as `-` and then `>`, and not form a single token. Rust would interpret this as a "minus sign and then a greater than sign", not as a single arrow. pub fn puncts(&mut self, puncts: &str) -> &mut Self { self.stream.extend( puncts .chars() .map(|char| TokenTree::Punct(Punct::new(char, Spacing::Joint))), ); self } /// Add a lifetime to the stream. /// /// Note that this is the only way to add lifetimes, if you were to do: /// ```ignore /// builder.punct('\''); /// builder.ident_str("static"); /// ``` /// It would not add `'static`, but instead it would add `' static` as seperate tokens, and the lifetime would not work. pub fn lifetime(&mut self, lt: Ident) -> &mut Self { self.stream.extend([ TokenTree::Punct(Punct::new('\'', Spacing::Joint)), TokenTree::Ident(lt), ]); self } /// Add a lifetime to the stream. /// /// Note that this is the only way to add lifetimes, if you were to do: /// ```ignore /// builder.punct('\''); /// builder.ident_str("static"); /// ``` /// It would not add `'static`, but instead it would add `' static` as seperate tokens, and the lifetime would not work. pub fn lifetime_str(&mut self, lt: &str) -> &mut Self { self.stream.extend([ TokenTree::Punct(Punct::new('\'', Spacing::Joint)), TokenTree::Ident(Ident::new(lt, Span::call_site())), ]); self } /// Add a literal string (`&'static str`) to the stream. pub fn lit_str(&mut self, str: impl AsRef) -> &mut Self { self.stream .extend([TokenTree::Literal(Literal::string(str.as_ref()))]); self } /// Add an `usize` value to the stream. pub fn lit_usize(&mut self, val: usize) -> &mut Self { self.stream .extend([TokenTree::Literal(Literal::usize_unsuffixed(val))]); self } /// Set the given span on all tokens in the stream. This span is used by rust for e.g. compiler errors, to indicate the position of the error. /// /// Normally your derive will report an error on the derive, e.g.: /// /// ```text /// #[derive(YourMacro)] /// ^^^^^^^^^ /// | /// `self` value is a keyword only available in methods with a `self` parameter /// ``` /// /// If you want to improve feedback to the user of your macro, you can use this macro to set the location for a given streambuilder. /// /// A `span` can be obtained from e.g. an ident with `ident.span()`. pub fn set_span_on_all_tokens(&mut self, span: Span) { self.stream = std::mem::take(&mut self.stream) .into_iter() .map(|mut token| { token.set_span(span); token }) .collect(); } } /// Failed to parse the code passed to [`StreamBuilder::push_parsed`] /// /// [`StreamBuilder::push_parsed`]: struct.StreamBuilder.html#method.push_parsed #[derive(Debug)] pub struct PushParseError { /// The parsing error pub error: LexError, /// The code that was being parsed pub code: String, } virtue-0.0.18/src/lib.rs000064400000000000000000000054611046102023000131410ustar 00000000000000//! # Virtue, a sinless derive macro helper //! //! ## Goals //! //! - Zero dependencies, so fast compile times //! - No other dependencies needed //! - Declarative code generation //! - As much typesystem checking as possible //! - Build for modern rust: 1.57 and up //! - Build with popular crates in mind: //! - [bincode](https://docs.rs/bincode) //! - Will always respect semver. Minor releases will never have: //! - Breaking API changes //! - MSRV changes //! //! ## Example //! //! First, add this to your Cargo.toml: //! ```toml //! [lib] //! proc-macro = true //! ``` //! //! Then instantiate your project with: //! //! ```ignore //! use virtue::prelude::*; //! //! #[proc_macro_derive(RetHi)] // change this to change your #[derive(...)] name //! pub fn derive_ret_hi(input: TokenStream) -> TokenStream { //! derive_ret_hi_inner(input).unwrap_or_else(|error| error.into_token_stream()) //! } //! //! fn derive_ret_hi_inner(input: TokenStream) -> Result { //! let parse = Parse::new(input)?; //! let (mut generator, _attributes, _body) = parse.into_generator(); //! generator //! .generate_impl() //! .generate_fn("hi") //! .with_self_arg(FnSelfArg::RefSelf) //! .with_return_type("&'static str") //! .body(|body| { //! body.lit_str("hi"); //! Ok(()) //! })?; //! generator.finish() //! } //! ``` //! //! You can invoke this with //! //! ```ignore //! #[derive(RetHi)] //! struct Foo; //! //! fn main() { //! println!("{}", Foo.hi()); //! } //! ``` //! //! The generated code is: //! //! ```ignore //! impl Foo { //! fn hi(&self) -> &'static str { //! "hi" //! } //! } //! ``` #![warn(missing_docs)] mod error; pub mod generate; pub mod parse; pub mod utils; /// Result alias for virtue's errors pub type Result = std::result::Result; pub use self::error::Error; /// Useful includes pub mod prelude { pub use crate::generate::{FnSelfArg, Generator, StreamBuilder}; pub use crate::parse::{ AttributeAccess, Body, EnumVariant, Fields, FromAttribute, Parse, UnnamedField, }; pub use crate::{Error, Result}; #[cfg(any(test, feature = "proc-macro2"))] pub use proc_macro2::*; #[cfg(not(any(test, feature = "proc-macro2")))] extern crate proc_macro; #[cfg(not(any(test, feature = "proc-macro2")))] pub use proc_macro::*; } #[cfg(test)] pub(crate) fn token_stream( s: &str, ) -> std::iter::Peekable> { use std::str::FromStr; let stream = proc_macro2::TokenStream::from_str(s) .unwrap_or_else(|e| panic!("Could not parse code: {:?}\n{:?}", s, e)); stream.into_iter().peekable() } virtue-0.0.18/src/parse/attributes.rs000064400000000000000000000122241046102023000156660ustar 00000000000000use super::utils::*; use crate::prelude::{Delimiter, Group, Punct, TokenTree}; use crate::{Error, Result}; use std::iter::Peekable; /// An attribute for the given struct, enum, field, etc #[derive(Debug, Clone)] #[non_exhaustive] pub struct Attribute { /// The location this attribute was parsed at pub location: AttributeLocation, /// The punct token of the attribute. This will always be `Punct('#')` pub punct: Punct, /// The group of tokens of the attribute. You can parse this to get your custom attributes. pub tokens: Group, } /// The location an attribute can be found at #[derive(PartialEq, Eq, Debug, Hash, Copy, Clone)] #[non_exhaustive] pub enum AttributeLocation { /// The attribute is on a container, which will be either a `struct` or an `enum` Container, /// The attribute is on an enum variant Variant, /// The attribute is on a field, which can either be a struct field or an enum variant field /// ```ignore /// struct Foo { /// #[attr] // here /// pub a: u8 /// } /// struct Bar { /// Baz { /// #[attr] // or here /// a: u8 /// } /// } /// ``` Field, } impl Attribute { pub(crate) fn try_take( location: AttributeLocation, input: &mut Peekable>, ) -> Result> { let mut result = Vec::new(); while let Some(punct) = consume_punct_if(input, '#') { match input.peek() { Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => { let group = assume_group(input.next()); result.push(Attribute { location, punct, tokens: group, }); } Some(TokenTree::Group(g)) => { return Err(Error::InvalidRustSyntax { span: g.span(), expected: format!("[] bracket, got {:?}", g.delimiter()), }); } Some(TokenTree::Punct(p)) if p.as_char() == '#' => { // sometimes with empty lines of doc comments, we get two #'s in a row // Just ignore this } token => return Error::wrong_token(token, "[] group or next # attribute"), } } Ok(result) } } #[test] fn test_attributes_try_take() { use crate::token_stream; let stream = &mut token_stream("struct Foo;"); assert!(Attribute::try_take(AttributeLocation::Container, stream) .unwrap() .is_empty()); match stream.next().unwrap() { TokenTree::Ident(i) => assert_eq!(i, "struct"), x => panic!("Expected ident, found {:?}", x), } let stream = &mut token_stream("#[cfg(test)] struct Foo;"); assert!(!Attribute::try_take(AttributeLocation::Container, stream) .unwrap() .is_empty()); match stream.next().unwrap() { TokenTree::Ident(i) => assert_eq!(i, "struct"), x => panic!("Expected ident, found {:?}", x), } } /// Helper trait for [`AttributeAccess`] methods. /// /// This can be implemented on your own type to make parsing easier. /// /// Some functions that can make your life easier: /// - [`utils::parse_tagged_attribute`] is a helper for parsing attributes in the format of `#[prefix(...)]` /// /// [`AttributeAccess`]: trait.AttributeAccess.html /// [`utils::parse_tagged_attribute`]: ../utils/fn.parse_tagged_attribute.html pub trait FromAttribute: Sized { /// Try to parse the given group into your own type. Return `Ok(None)` if the parsing failed or if the attribute was not this type. fn parse(group: &Group) -> Result>; } /// Bring useful methods to access attributes of an element. pub trait AttributeAccess { /// Check to see if has the given attribute. See [`FromAttribute`] for more information. /// /// **note**: Will immediately return `Err(_)` on the first error `T` returns. fn has_attribute>(&self, attrib: T) -> Result; /// Returns the first attribute that returns `Some(Self)`. See [`FromAttribute`] for more information. /// /// **note**: Will immediately return `Err(_)` on the first error `T` returns. fn get_attribute(&self) -> Result>; } impl AttributeAccess for Vec { fn has_attribute>(&self, attrib: T) -> Result { for attribute in self.iter() { if let Some(attribute) = T::parse(&attribute.tokens)? { if attribute == attrib { return Ok(true); } } } Ok(false) } fn get_attribute(&self) -> Result> { for attribute in self.iter() { if let Some(attribute) = T::parse(&attribute.tokens)? { return Ok(Some(attribute)); } } Ok(None) } } virtue-0.0.18/src/parse/body.rs000064400000000000000000000554711046102023000144500ustar 00000000000000use super::attributes::AttributeLocation; use super::{utils::*, Attribute, Visibility}; use crate::prelude::{Delimiter, Ident, Literal, Span, TokenTree}; use crate::{Error, Result}; use std::iter::Peekable; /// The body of a struct #[derive(Debug)] pub struct StructBody { /// The fields of this struct, `None` if this struct has no fields pub fields: Option, } impl StructBody { pub(crate) fn take(input: &mut Peekable>) -> Result { match input.peek() { Some(TokenTree::Group(_)) => {} Some(TokenTree::Punct(p)) if p.as_char() == ';' => { return Ok(StructBody { fields: None }) } token => return Error::wrong_token(token, "group or punct"), } let group = assume_group(input.next()); let mut stream = group.stream().into_iter().peekable(); let fields = match group.delimiter() { Delimiter::Brace => { let fields = UnnamedField::parse_with_name(&mut stream)?; Some(Fields::Struct(fields)) } Delimiter::Parenthesis => { let fields = UnnamedField::parse(&mut stream)?; Some(Fields::Tuple(fields)) } found => { return Err(Error::InvalidRustSyntax { span: group.span(), expected: format!("brace or parenthesis, found {:?}", found), }) } }; Ok(StructBody { fields }) } } #[test] fn test_struct_body_take() { use crate::token_stream; let stream = &mut token_stream( "struct Foo { pub bar: u8, pub(crate) baz: u32, bla: Vec>> }", ); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let body = StructBody::take(stream).unwrap(); let fields = body.fields.as_ref().unwrap(); assert_eq!(fields.len(), 3); let (ident, field) = fields.get(0).unwrap(); assert_eq!(ident.unwrap(), "bar"); assert_eq!(field.vis, Visibility::Pub); assert_eq!(field.type_string(), "u8"); let (ident, field) = fields.get(1).unwrap(); assert_eq!(ident.unwrap(), "baz"); assert_eq!(field.vis, Visibility::Pub); assert_eq!(field.type_string(), "u32"); let (ident, field) = fields.get(2).unwrap(); assert_eq!(ident.unwrap(), "bla"); assert_eq!(field.vis, Visibility::Default); assert_eq!(field.type_string(), "Vec>>"); let stream = &mut token_stream( "struct Foo ( pub u8, pub(crate) u32, Vec>> )", ); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let body = StructBody::take(stream).unwrap(); let fields = body.fields.as_ref().unwrap(); assert_eq!(fields.len(), 3); let (ident, field) = fields.get(0).unwrap(); assert!(ident.is_none()); assert_eq!(field.vis, Visibility::Pub); assert_eq!(field.type_string(), "u8"); let (ident, field) = fields.get(1).unwrap(); assert!(ident.is_none()); assert_eq!(field.vis, Visibility::Pub); assert_eq!(field.type_string(), "u32"); let (ident, field) = fields.get(2).unwrap(); assert!(ident.is_none()); assert_eq!(field.vis, Visibility::Default); assert_eq!(field.type_string(), "Vec>>"); let stream = &mut token_stream("struct Foo;"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let body = StructBody::take(stream).unwrap(); assert!(body.fields.is_none()); let stream = &mut token_stream("struct Foo {}"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let body = StructBody::take(stream).unwrap(); if let Some(Fields::Struct(v)) = body.fields { assert!(v.is_empty()); } else { panic!("wrong fields {:?}", body.fields); } let stream = &mut token_stream("struct Foo ()"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let body = StructBody::take(stream).unwrap(); if let Some(Fields::Tuple(v)) = body.fields { assert!(v.is_empty()); } else { panic!("wrong fields {:?}", body.fields); } } #[test] fn issue_77() { // https://github.com/bincode-org/virtue/issues/77 use crate::token_stream; let stream = &mut token_stream("struct Test(pub [u8; 32])"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Test"); let body = StructBody::take(stream).unwrap(); let fields = body.fields.unwrap(); let Fields::Tuple(t) = fields else { panic!("Fields is not a tuple") }; assert_eq!(t.len(), 1); assert_eq!(t[0].r#type[0].to_string(), "[u8 ; 32]"); let stream = &mut token_stream("struct Foo(pub (u8, ))"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let body = StructBody::take(stream).unwrap(); let fields = body.fields.unwrap(); let Fields::Tuple(t) = fields else { panic!("Fields is not a tuple") }; assert_eq!(t.len(), 1); assert_eq!(t[0].r#type[0].to_string(), "(u8 ,)"); } /// The body of an enum #[derive(Debug)] pub struct EnumBody { /// The enum's variants pub variants: Vec, } impl EnumBody { pub(crate) fn take(input: &mut Peekable>) -> Result { match input.peek() { Some(TokenTree::Group(_)) => {} Some(TokenTree::Punct(p)) if p.as_char() == ';' => { return Ok(EnumBody { variants: Vec::new(), }) } token => return Error::wrong_token(token, "group or ;"), } let group = assume_group(input.next()); let mut variants = Vec::new(); let stream = &mut group.stream().into_iter().peekable(); while stream.peek().is_some() { let attributes = Attribute::try_take(AttributeLocation::Variant, stream)?; let ident = match super::utils::consume_ident(stream) { Some(ident) => ident, None => Error::wrong_token(stream.peek(), "ident")?, }; let mut fields = None; let mut value = None; if let Some(TokenTree::Group(_)) = stream.peek() { let group = assume_group(stream.next()); let stream = &mut group.stream().into_iter().peekable(); match group.delimiter() { Delimiter::Brace => { fields = Some(Fields::Struct(UnnamedField::parse_with_name(stream)?)); } Delimiter::Parenthesis => { fields = Some(Fields::Tuple(UnnamedField::parse(stream)?)); } delim => { return Err(Error::InvalidRustSyntax { span: group.span(), expected: format!("Brace or parenthesis, found {:?}", delim), }) } } } match stream.peek() { Some(TokenTree::Punct(p)) if p.as_char() == '=' => { assume_punct(stream.next(), '='); match stream.next() { Some(TokenTree::Literal(lit)) => { value = Some(lit); } Some(TokenTree::Punct(p)) if p.as_char() == '-' => match stream.next() { Some(TokenTree::Literal(lit)) => { match lit.to_string().parse::() { Ok(val) => value = Some(Literal::i64_unsuffixed(-val)), Err(_) => { return Err(Error::custom_at( "parse:: failed", lit.span(), )) } }; } token => return Error::wrong_token(token.as_ref(), "literal"), }, token => return Error::wrong_token(token.as_ref(), "literal"), } } Some(TokenTree::Punct(p)) if p.as_char() == ',' => { // next field } None => { // group done } token => return Error::wrong_token(token, "group, comma or ="), } consume_punct_if(stream, ','); variants.push(EnumVariant { name: ident, fields, value, attributes, }); } Ok(EnumBody { variants }) } } #[test] fn test_enum_body_take() { use crate::token_stream; let stream = &mut token_stream("enum Foo { }"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Enum); assert_eq!(ident, "Foo"); let body = EnumBody::take(stream).unwrap(); assert!(body.variants.is_empty()); let stream = &mut token_stream("enum Foo { Bar, Baz(u8), Blah { a: u32, b: u128 } }"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Enum); assert_eq!(ident, "Foo"); let body = EnumBody::take(stream).unwrap(); assert_eq!(3, body.variants.len()); assert_eq!(body.variants[0].name, "Bar"); assert!(body.variants[0].fields.is_none()); assert_eq!(body.variants[1].name, "Baz"); assert!(body.variants[1].fields.is_some()); let fields = body.variants[1].fields.as_ref().unwrap(); assert_eq!(1, fields.len()); let (ident, field) = fields.get(0).unwrap(); assert!(ident.is_none()); assert_eq!(field.type_string(), "u8"); assert_eq!(body.variants[2].name, "Blah"); assert!(body.variants[2].fields.is_some()); let fields = body.variants[2].fields.as_ref().unwrap(); assert_eq!(2, fields.len()); let (ident, field) = fields.get(0).unwrap(); assert_eq!(ident.unwrap(), "a"); assert_eq!(field.type_string(), "u32"); let (ident, field) = fields.get(1).unwrap(); assert_eq!(ident.unwrap(), "b"); assert_eq!(field.type_string(), "u128"); let stream = &mut token_stream("enum Foo { Bar = -1, Baz = 2 }"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Enum); assert_eq!(ident, "Foo"); let body = EnumBody::take(stream).unwrap(); assert_eq!(2, body.variants.len()); assert_eq!(body.variants[0].name, "Bar"); assert!(body.variants[0].fields.is_none()); assert_eq!(body.variants[0].get_integer(), -1); assert_eq!(body.variants[1].name, "Baz"); assert!(body.variants[1].fields.is_none()); assert_eq!(body.variants[1].get_integer(), 2); let stream = &mut token_stream("enum Foo { Bar(i32) = -1, Baz { a: i32 } = 2 }"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Enum); assert_eq!(ident, "Foo"); let body = EnumBody::take(stream).unwrap(); assert_eq!(2, body.variants.len()); assert_eq!(body.variants[0].name, "Bar"); assert!(body.variants[0].fields.is_some()); let fields = body.variants[0].fields.as_ref().unwrap(); assert_eq!(fields.len(), 1); assert!(matches!(fields.names()[0], IdentOrIndex::Index { index, .. } if index == 0)); assert_eq!(body.variants[0].get_integer(), -1); assert_eq!(body.variants[1].name, "Baz"); assert!(body.variants[1].fields.is_some()); let fields = body.variants[1].fields.as_ref().unwrap(); assert_eq!(fields.len(), 1); assert_eq!(fields.names().len(), 1); assert!(matches!(&fields.names()[0], IdentOrIndex::Ident { ident, .. } if *ident == "a")); assert_eq!(body.variants[1].get_integer(), 2); let stream = &mut token_stream("enum Foo { Round(), Curly{}, Without }"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Enum); assert_eq!(ident, "Foo"); let body = EnumBody::take(stream).unwrap(); assert_eq!(3, body.variants.len()); assert_eq!(body.variants[0].name, "Round"); assert!(body.variants[0].fields.is_some()); let fields = body.variants[0].fields.as_ref().unwrap(); assert!(fields.names().is_empty()); assert_eq!(fields.len(), 0); assert_eq!(body.variants[1].name, "Curly"); assert!(body.variants[1].fields.is_some()); let fields = body.variants[1].fields.as_ref().unwrap(); assert!(fields.names().is_empty()); assert_eq!(fields.len(), 0); assert_eq!(body.variants[2].name, "Without"); assert!(body.variants[2].fields.is_none()); } /// A variant of an enum #[derive(Debug)] pub struct EnumVariant { /// The name of the variant pub name: Ident, /// The field of the variant. See [`Fields`] for more info pub fields: Option, /// The value of this variant. This can be one of: /// - `Baz = 5` /// - `Baz(i32) = 5` /// - `Baz { a: i32} = 5` /// /// In either case this value will be `Some(Literal::i32(5))` pub value: Option, /// The attributes of this variant pub attributes: Vec, } #[cfg(test)] impl EnumVariant { fn get_integer(&self) -> i64 { let value = self.value.as_ref().expect("Variant has no value"); value .to_string() .parse() .expect("Value is not a valid integer") } } /// The different field types an enum variant can have. #[derive(Debug)] pub enum Fields { /// Tuple-like variant /// ```rs /// enum Foo { /// Baz(u32) /// } /// struct Bar(u32); /// ``` Tuple(Vec), /// Struct-like variant /// ```rs /// enum Foo { /// Baz { /// baz: u32 /// } /// } /// struct Bar { /// baz: u32 /// } /// ``` Struct(Vec<(Ident, UnnamedField)>), } impl Fields { /// Returns a list of names for the variant. /// /// ``` /// enum Foo { /// C(u32, u32), // will return `vec[Index { index: 0 }, Index { index: 1 }]` /// D { a: u32, b: u32 }, // will return `vec[Ident { ident: "a" }, Ident { ident: "b" }]` /// } pub fn names(&self) -> Vec { let result: Vec = match self { Self::Tuple(fields) => fields .iter() .enumerate() .map(|(index, field)| IdentOrIndex::Index { index, span: field.span(), attributes: field.attributes.clone(), }) .collect(), Self::Struct(fields) => fields .iter() .map(|(ident, field)| IdentOrIndex::Ident { ident: ident.clone(), attributes: field.attributes.clone(), }) .collect(), }; result } /// Return the delimiter of the group for this variant /// /// ``` /// enum Foo { /// C(u32, u32), // will return `Delimiter::Paranthesis` /// D { a: u32, b: u32 }, // will return `Delimiter::Brace` /// } /// ``` pub fn delimiter(&self) -> Delimiter { match self { Self::Tuple(_) => Delimiter::Parenthesis, Self::Struct(_) => Delimiter::Brace, } } } #[cfg(test)] impl Fields { fn len(&self) -> usize { match self { Self::Tuple(fields) => fields.len(), Self::Struct(fields) => fields.len(), } } fn get(&self, index: usize) -> Option<(Option<&Ident>, &UnnamedField)> { match self { Self::Tuple(fields) => fields.get(index).map(|f| (None, f)), Self::Struct(fields) => fields.get(index).map(|(ident, field)| (Some(ident), field)), } } } /// An unnamed field #[derive(Debug)] pub struct UnnamedField { /// The visibility of the field pub vis: Visibility, /// The type of the field pub r#type: Vec, /// The attributes of the field pub attributes: Vec, } impl UnnamedField { pub(crate) fn parse_with_name( input: &mut Peekable>, ) -> Result> { let mut result = Vec::new(); loop { let attributes = Attribute::try_take(AttributeLocation::Field, input)?; let vis = Visibility::try_take(input)?; let ident = match input.peek() { Some(TokenTree::Ident(_)) => assume_ident(input.next()), Some(x) => { return Err(Error::InvalidRustSyntax { span: x.span(), expected: format!("ident or end of group, got {:?}", x), }) } None => break, }; match input.peek() { Some(TokenTree::Punct(p)) if p.as_char() == ':' => { input.next(); } token => return Error::wrong_token(token, ":"), } let r#type = read_tokens_until_punct(input, &[','])?; consume_punct_if(input, ','); result.push(( ident, Self { vis, r#type, attributes, }, )); } Ok(result) } pub(crate) fn parse( input: &mut Peekable>, ) -> Result> { let mut result = Vec::new(); while input.peek().is_some() { let attributes = Attribute::try_take(AttributeLocation::Field, input)?; let vis = Visibility::try_take(input)?; let r#type = read_tokens_until_punct(input, &[','])?; consume_punct_if(input, ','); result.push(Self { vis, r#type, attributes, }); } Ok(result) } /// Return [`type`] as a string. Useful for comparing it for known values. /// /// [`type`]: #structfield.type pub fn type_string(&self) -> String { self.r#type.iter().map(|t| t.to_string()).collect() } /// Return the span of [`type`]. /// /// **note**: Until is stable, this will return the first span of the type instead /// /// [`type`]: #structfield.type pub fn span(&self) -> Span { // BlockedTODO: https://github.com/rust-lang/rust/issues/54725 // Span::join is unstable // if let Some(first) = self.r#type.first() { // let mut span = first.span(); // for token in self.r#type.iter().skip(1) { // span = span.join(span).unwrap(); // } // span // } else { // Span::call_site() // } match self.r#type.first() { Some(first) => first.span(), None => Span::call_site(), } } } /// Reference to an enum variant's field. Either by index or by ident. /// /// ``` /// enum Foo { /// Bar(u32), // will be IdentOrIndex::Index { index: 0, .. } /// Baz { /// a: u32, // will be IdentOrIndex::Ident { ident: "a", .. } /// }, /// } #[derive(Debug, Clone)] pub enum IdentOrIndex { /// The variant is a named field Ident { /// The name of the field ident: Ident, /// The attributes of the field attributes: Vec, }, /// The variant is an unnamed field Index { /// The field index index: usize, /// The span of the field type span: Span, /// The attributes of this field attributes: Vec, }, } impl IdentOrIndex { /// Get the ident. Will panic if this is an `IdentOrIndex::Index` pub fn unwrap_ident(&self) -> Ident { match self { Self::Ident { ident, .. } => ident.clone(), x => panic!("Expected ident, found {:?}", x), } } /// Convert this ident into a TokenTree. If this is an `Index`, will return `prefix + index` instead. pub fn to_token_tree_with_prefix(&self, prefix: &str) -> TokenTree { TokenTree::Ident(match self { IdentOrIndex::Ident { ident, .. } => (*ident).clone(), IdentOrIndex::Index { index, span, .. } => { let name = format!("{}{}", prefix, index); Ident::new(&name, *span) } }) } /// Return either the index or the ident of this field with a fixed prefix. The prefix will always be added. pub fn to_string_with_prefix(&self, prefix: &str) -> String { match self { IdentOrIndex::Ident { ident, .. } => ident.to_string(), IdentOrIndex::Index { index, .. } => { format!("{}{}", prefix, index) } } } /// Returns the attributes of this field. pub fn attributes(&self) -> &Vec { match self { Self::Ident { attributes, .. } => attributes, Self::Index { attributes, .. } => attributes, } } } impl std::fmt::Display for IdentOrIndex { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { match self { IdentOrIndex::Ident { ident, .. } => write!(fmt, "{}", ident), IdentOrIndex::Index { index, .. } => write!(fmt, "{}", index), } } } #[test] fn enum_explicit_variants() { use crate::token_stream; let stream = &mut token_stream("{ A = 1, B = 2 }"); let body = EnumBody::take(stream).unwrap(); assert_eq!(body.variants.len(), 2); } virtue-0.0.18/src/parse/data_type.rs000064400000000000000000000050251046102023000154530ustar 00000000000000use crate::prelude::{Ident, TokenTree}; use crate::{Error, Result}; use std::iter::Peekable; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) enum DataType { Enum, Struct, } impl DataType { pub(crate) fn take( input: &mut Peekable>, ) -> Result<(Self, Ident)> { if let Some(ident) = super::utils::consume_ident(input) { let result = match ident.to_string().as_str() { "struct" => DataType::Struct, "enum" => DataType::Enum, _ => return Err(Error::UnknownDataType(ident.span())), }; if let Some(ident) = super::utils::consume_ident(input) { return Ok((result, ident)); } } Error::wrong_token(input.peek(), "ident") } } #[test] fn test_datatype_take() { use crate::token_stream; fn validate_output_eq(input: &str, expected_dt: DataType, expected_ident: &str) { let (dt, ident) = DataType::take(&mut token_stream(input)).unwrap_or_else(|e| { panic!("Could not parse tokenstream {:?}: {:?}", input, e); }); if dt != expected_dt || ident != expected_ident { println!("While parsing {:?}", input); panic!( "Expected {:?} {:?}, received {:?} {:?}", dt, ident, expected_dt, expected_ident ); } } assert!(DataType::take(&mut token_stream("enum")) .unwrap_err() .is_invalid_rust_syntax()); validate_output_eq("enum Foo", DataType::Enum, "Foo"); validate_output_eq("enum Foo { }", DataType::Enum, "Foo"); validate_output_eq("enum Foo { bar, baz }", DataType::Enum, "Foo"); validate_output_eq("enum Foo<'a, T> { bar, baz }", DataType::Enum, "Foo"); assert!(DataType::take(&mut token_stream("struct")) .unwrap_err() .is_invalid_rust_syntax()); validate_output_eq("struct Foo { }", DataType::Struct, "Foo"); validate_output_eq("struct Foo { bar: u32, baz: u32 }", DataType::Struct, "Foo"); validate_output_eq("struct Foo<'a, T> { bar: &'a T }", DataType::Struct, "Foo"); assert!(DataType::take(&mut token_stream("fn foo() {}")) .unwrap_err() .is_unknown_data_type()); assert!(DataType::take(&mut token_stream("() {}")) .unwrap_err() .is_invalid_rust_syntax()); assert!(DataType::take(&mut token_stream("")) .unwrap_err() .is_invalid_rust_syntax()); } virtue-0.0.18/src/parse/generics.rs000064400000000000000000000532631046102023000153070ustar 00000000000000use super::utils::*; use crate::generate::StreamBuilder; use crate::prelude::{Ident, TokenTree}; use crate::{Error, Result}; use std::iter::Peekable; use std::ops::{Deref, DerefMut}; /// A generic parameter for a struct or enum. /// /// ``` /// use std::marker::PhantomData; /// use std::fmt::Display; /// /// // Generic will be `Generic::Generic("F")` /// struct Foo { /// f: PhantomData /// } /// // Generics will be `Generic::Generic("F: Display")` /// struct Bar { /// f: PhantomData /// } /// // Generics will be `[Generic::Lifetime("a"), Generic::Generic("F: Display")]` /// struct Baz<'a, F> { /// f: PhantomData<&'a F> /// } /// ``` #[derive(Debug, Clone)] pub struct Generics(pub Vec); impl Generics { pub(crate) fn try_take( input: &mut Peekable>, ) -> Result> { let maybe_punct = input.peek(); if let Some(TokenTree::Punct(punct)) = maybe_punct { if punct.as_char() == '<' { let punct = assume_punct(input.next(), '<'); let mut result = Generics(Vec::new()); loop { match input.peek() { Some(TokenTree::Punct(punct)) if punct.as_char() == '\'' => { result.push(Lifetime::take(input)?.into()); consume_punct_if(input, ','); } Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => { assume_punct(input.next(), '>'); break; } Some(TokenTree::Ident(ident)) if ident_eq(ident, "const") => { result.push(ConstGeneric::take(input)?.into()); consume_punct_if(input, ','); } Some(TokenTree::Ident(_)) => { result.push(SimpleGeneric::take(input)?.into()); consume_punct_if(input, ','); } x => { return Err(Error::InvalidRustSyntax { span: x.map(|x| x.span()).unwrap_or_else(|| punct.span()), expected: format!("', > or an ident, got {:?}", x), }); } } } return Ok(Some(result)); } } Ok(None) } /// Returns `true` if any of the generics is a [`Generic::Lifetime`] pub fn has_lifetime(&self) -> bool { self.iter().any(|lt| lt.is_lifetime()) } /// Returns an iterator which contains only the simple type generics pub fn iter_generics(&self) -> impl Iterator { self.iter().filter_map(|g| match g { Generic::Generic(s) => Some(s), _ => None, }) } /// Returns an iterator which contains only the lifetimes pub fn iter_lifetimes(&self) -> impl Iterator { self.iter().filter_map(|g| match g { Generic::Lifetime(s) => Some(s), _ => None, }) } /// Returns an iterator which contains only the const generics pub fn iter_consts(&self) -> impl Iterator { self.iter().filter_map(|g| match g { Generic::Const(s) => Some(s), _ => None, }) } pub(crate) fn impl_generics(&self) -> StreamBuilder { let mut result = StreamBuilder::new(); result.punct('<'); for (idx, generic) in self.iter().enumerate() { if idx > 0 { result.punct(','); } generic.append_to_result_with_constraints(&mut result); } result.punct('>'); result } pub(crate) fn impl_generics_with_additional( &self, lifetimes: &[String], types: &[String], ) -> StreamBuilder { let mut result = StreamBuilder::new(); result.punct('<'); let mut is_first = true; for lt in lifetimes.iter() { if !is_first { result.punct(','); } else { is_first = false; } result.lifetime_str(lt); } for generic in self.iter() { if !is_first { result.punct(','); } else { is_first = false; } generic.append_to_result_with_constraints(&mut result); } for ty in types { if !is_first { result.punct(','); } else { is_first = false; } result.ident_str(ty); } result.punct('>'); result } pub(crate) fn type_generics(&self) -> StreamBuilder { let mut result = StreamBuilder::new(); result.punct('<'); for (idx, generic) in self.iter().enumerate() { if idx > 0 { result.punct(','); } if generic.is_lifetime() { result.lifetime(generic.ident().clone()); } else { result.ident(generic.ident().clone()); } } result.punct('>'); result } } impl Deref for Generics { type Target = Vec; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for Generics { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } /// A single generic argument on a type #[derive(Debug, Clone)] #[allow(clippy::enum_variant_names)] #[non_exhaustive] pub enum Generic { /// A lifetime generic /// /// ``` /// # use std::marker::PhantomData; /// struct Foo<'a> { // will be Generic::Lifetime("a") /// # a: PhantomData<&'a ()>, /// } /// ``` Lifetime(Lifetime), /// A simple generic /// /// ``` /// # use std::marker::PhantomData; /// struct Foo { // will be Generic::Generic("F") /// # a: PhantomData, /// } /// ``` Generic(SimpleGeneric), /// A const generic /// /// ``` /// struct Foo { // will be Generic::Const("N") /// # a: [u8; N], /// } /// ``` Const(ConstGeneric), } impl Generic { fn is_lifetime(&self) -> bool { matches!(self, Generic::Lifetime(_)) } /// The ident of this generic pub fn ident(&self) -> &Ident { match self { Self::Lifetime(lt) => <.ident, Self::Generic(gen) => &gen.ident, Self::Const(gen) => &gen.ident, } } fn has_constraints(&self) -> bool { match self { Self::Lifetime(lt) => !lt.constraint.is_empty(), Self::Generic(gen) => !gen.constraints.is_empty(), Self::Const(_) => true, // const generics always have a constraint } } fn constraints(&self) -> Vec { match self { Self::Lifetime(lt) => lt.constraint.clone(), Self::Generic(gen) => gen.constraints.clone(), Self::Const(gen) => gen.constraints.clone(), } } fn append_to_result_with_constraints(&self, builder: &mut StreamBuilder) { match self { Self::Lifetime(lt) => builder.lifetime(lt.ident.clone()), Self::Generic(gen) => builder.ident(gen.ident.clone()), Self::Const(gen) => { builder.ident(gen.const_token.clone()); builder.ident(gen.ident.clone()) } }; if self.has_constraints() { builder.punct(':'); builder.extend(self.constraints()); } } } impl From for Generic { fn from(lt: Lifetime) -> Self { Self::Lifetime(lt) } } impl From for Generic { fn from(gen: SimpleGeneric) -> Self { Self::Generic(gen) } } impl From for Generic { fn from(gen: ConstGeneric) -> Self { Self::Const(gen) } } #[test] fn test_generics_try_take() { use crate::token_stream; assert!(Generics::try_take(&mut token_stream("")).unwrap().is_none()); assert!(Generics::try_take(&mut token_stream("foo")) .unwrap() .is_none()); assert!(Generics::try_take(&mut token_stream("()")) .unwrap() .is_none()); let stream = &mut token_stream("struct Foo<'a, T>()"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let generics = Generics::try_take(stream).unwrap().unwrap(); assert_eq!(generics.len(), 2); assert_eq!(generics[0].ident(), "a"); assert_eq!(generics[1].ident(), "T"); let stream = &mut token_stream("struct Foo()"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let generics = Generics::try_take(stream).unwrap().unwrap(); assert_eq!(generics.len(), 2); assert_eq!(generics[0].ident(), "A"); assert_eq!(generics[1].ident(), "B"); let stream = &mut token_stream("struct Foo<'a, T: Display>()"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); let generics = Generics::try_take(stream).unwrap().unwrap(); dbg!(&generics); assert_eq!(generics.len(), 2); assert_eq!(generics[0].ident(), "a"); assert_eq!(generics[1].ident(), "T"); let stream = &mut token_stream("struct Foo<'a, T: for<'a> Bar<'a> + 'static>()"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Foo"); dbg!(&generics); assert_eq!(generics.len(), 2); assert_eq!(generics[0].ident(), "a"); assert_eq!(generics[1].ident(), "T"); let stream = &mut token_stream( "struct Baz Bar<'a, for<'b> Bar<'b, for<'c> Bar<'c, u32>>>> {}", ); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Baz"); let generics = Generics::try_take(stream).unwrap().unwrap(); dbg!(&generics); assert_eq!(generics.len(), 1); assert_eq!(generics[0].ident(), "T"); let stream = &mut token_stream("struct Baz<()> {}"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Baz"); assert!(Generics::try_take(stream) .unwrap_err() .is_invalid_rust_syntax()); let stream = &mut token_stream("struct Bar SomeStruct, B>"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Bar"); let generics = Generics::try_take(stream).unwrap().unwrap(); dbg!(&generics); assert_eq!(generics.len(), 2); assert_eq!(generics[0].ident(), "A"); assert_eq!(generics[1].ident(), "B"); let stream = &mut token_stream("struct Bar"); let (data_type, ident) = super::DataType::take(stream).unwrap(); assert_eq!(data_type, super::DataType::Struct); assert_eq!(ident, "Bar"); let generics = Generics::try_take(stream).unwrap().unwrap(); dbg!(&generics); assert_eq!(generics.len(), 1); if let Generic::Generic(generic) = &generics[0] { assert_eq!(generic.ident, "A"); assert_eq!(generic.default_value.len(), 1); assert_eq!(generic.default_value[0].to_string(), "()"); } else { panic!("Expected simple generic, got {:?}", generics[0]); } } /// a lifetime generic parameter, e.g. `struct Foo<'a> { ... }` #[derive(Debug, Clone)] pub struct Lifetime { /// The ident of this lifetime pub ident: Ident, /// Any constraints that this lifetime may have pub constraint: Vec, } impl Lifetime { pub(crate) fn take(input: &mut Peekable>) -> Result { let start = assume_punct(input.next(), '\''); let ident = match input.peek() { Some(TokenTree::Ident(_)) => assume_ident(input.next()), Some(t) => return Err(Error::ExpectedIdent(t.span())), None => return Err(Error::ExpectedIdent(start.span())), }; let mut constraint = Vec::new(); if let Some(TokenTree::Punct(p)) = input.peek() { if p.as_char() == ':' { assume_punct(input.next(), ':'); constraint = read_tokens_until_punct(input, &[',', '>'])?; } } Ok(Self { ident, constraint }) } #[cfg(test)] fn is_ident(&self, s: &str) -> bool { self.ident == s } } #[test] fn test_lifetime_take() { use crate::token_stream; use std::panic::catch_unwind; assert!(Lifetime::take(&mut token_stream("'a")) .unwrap() .is_ident("a")); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'0"))).is_err()); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'("))).is_err()); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("')"))).is_err()); assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'0'"))).is_err()); let stream = &mut token_stream("'a: 'b>"); let lifetime = Lifetime::take(stream).unwrap(); assert_eq!(lifetime.ident, "a"); assert_eq!(lifetime.constraint.len(), 2); assume_punct(stream.next(), '>'); assert!(stream.next().is_none()); } /// a simple generic parameter, e.g. `struct Foo { .. }` #[derive(Debug, Clone)] #[non_exhaustive] pub struct SimpleGeneric { /// The ident of this generic pub ident: Ident, /// The constraints of this generic, e.g. `F: SomeTrait` pub constraints: Vec, /// The default value of this generic, e.g. `F = ()` pub default_value: Vec, } impl SimpleGeneric { pub(crate) fn take(input: &mut Peekable>) -> Result { let ident = assume_ident(input.next()); let mut constraints = Vec::new(); let mut default_value = Vec::new(); if let Some(TokenTree::Punct(punct)) = input.peek() { let punct_char = punct.as_char(); if punct_char == ':' { assume_punct(input.next(), ':'); constraints = read_tokens_until_punct(input, &['>', ','])?; } if punct_char == '=' { assume_punct(input.next(), '='); default_value = read_tokens_until_punct(input, &['>', ','])?; } } Ok(Self { ident, constraints, default_value, }) } /// The name of this generic, e.g. `T` pub fn name(&self) -> Ident { self.ident.clone() } } /// a const generic parameter, e.g. `struct Foo { .. }` #[derive(Debug, Clone)] pub struct ConstGeneric { /// The `const` token for this generic pub const_token: Ident, /// The ident of this generic pub ident: Ident, /// The "constraints" (type) of this generic, e.g. the `usize` from `const N: usize` pub constraints: Vec, } impl ConstGeneric { pub(crate) fn take(input: &mut Peekable>) -> Result { let const_token = assume_ident(input.next()); let ident = assume_ident(input.next()); let mut constraints = Vec::new(); if let Some(TokenTree::Punct(punct)) = input.peek() { if punct.as_char() == ':' { assume_punct(input.next(), ':'); constraints = read_tokens_until_punct(input, &['>', ','])?; } } Ok(Self { const_token, ident, constraints, }) } } /// Constraints on generic types. /// /// ``` /// # use std::marker::PhantomData; /// # use std::fmt::Display; /// /// struct Foo /// where F: Display // These are `GenericConstraints` /// { /// f: PhantomData /// } #[derive(Debug, Clone, Default)] pub struct GenericConstraints { constraints: Vec, } impl GenericConstraints { pub(crate) fn try_take( input: &mut Peekable>, ) -> Result> { match input.peek() { Some(TokenTree::Ident(ident)) => { if !ident_eq(ident, "where") { return Ok(None); } } _ => { return Ok(None); } } input.next(); let constraints = read_tokens_until_punct(input, &['{', '('])?; Ok(Some(Self { constraints })) } pub(crate) fn where_clause(&self) -> StreamBuilder { let mut result = StreamBuilder::new(); result.ident_str("where"); result.extend(self.constraints.clone()); result } /// Push the given constraint onto this stream. /// /// ```ignore /// let mut generic_constraints = GenericConstraints::parse("T: Foo"); // imaginary function /// let mut generic = SimpleGeneric::new("U"); // imaginary function /// /// generic_constraints.push_constraint(&generic, "Bar"); /// /// // generic_constraints is now: /// // `T: Foo, U: Bar` /// ``` pub fn push_constraint( &mut self, generic: &SimpleGeneric, constraint: impl AsRef, ) -> Result<()> { let mut builder = StreamBuilder::new(); let last_constraint_was_comma = self.constraints.last().map_or( false, |l| matches!(l, TokenTree::Punct(c) if c.as_char() == ','), ); if !self.constraints.is_empty() && !last_constraint_was_comma { builder.punct(','); } builder.ident(generic.ident.clone()); builder.punct(':'); builder.push_parsed(constraint)?; self.constraints.extend(builder.stream); Ok(()) } /// Push the given constraint onto this stream. /// /// ```ignore /// let mut generic_constraints = GenericConstraints::parse("T: Foo"); // imaginary function /// /// generic_constraints.push_parsed_constraint("u32: SomeTrait"); /// /// // generic_constraints is now: /// // `T: Foo, u32: SomeTrait` /// ``` pub fn push_parsed_constraint(&mut self, constraint: impl AsRef) -> Result<()> { let mut builder = StreamBuilder::new(); if !self.constraints.is_empty() { builder.punct(','); } builder.push_parsed(constraint)?; self.constraints.extend(builder.stream); Ok(()) } /// Clear the constraints pub fn clear(&mut self) { self.constraints.clear(); } } #[test] fn test_generic_constraints_try_take() { use super::{DataType, StructBody, Visibility}; use crate::parse::body::Fields; use crate::token_stream; let stream = &mut token_stream("struct Foo where Foo: Bar { }"); DataType::take(stream).unwrap(); assert!(GenericConstraints::try_take(stream).unwrap().is_some()); let stream = &mut token_stream("struct Foo { }"); DataType::take(stream).unwrap(); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream("struct Foo where Foo: Bar(Foo)"); DataType::take(stream).unwrap(); assert!(GenericConstraints::try_take(stream).unwrap().is_some()); let stream = &mut token_stream("struct Foo()"); DataType::take(stream).unwrap(); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream("struct Foo()"); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream("{}"); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream(""); assert!(GenericConstraints::try_take(stream).unwrap().is_none()); let stream = &mut token_stream("pub(crate) struct Test {}"); assert_eq!(Visibility::Pub, Visibility::try_take(stream).unwrap()); let (data_type, ident) = DataType::take(stream).unwrap(); assert_eq!(data_type, DataType::Struct); assert_eq!(ident, "Test"); let constraints = Generics::try_take(stream).unwrap().unwrap(); assert_eq!(constraints.len(), 1); assert_eq!(constraints[0].ident(), "T"); let body = StructBody::take(stream).unwrap(); if let Some(Fields::Struct(v)) = body.fields { assert!(v.is_empty()); } else { panic!("wrong fields {:?}", body.fields); } } #[test] fn test_generic_constraints_trailing_comma() { use crate::parse::{ Attribute, AttributeLocation, DataType, GenericConstraints, Generics, StructBody, Visibility, }; use crate::token_stream; let source = &mut token_stream("pub struct MyStruct where T: Clone, { }"); Attribute::try_take(AttributeLocation::Container, source).unwrap(); Visibility::try_take(source).unwrap(); DataType::take(source).unwrap(); Generics::try_take(source).unwrap().unwrap(); GenericConstraints::try_take(source).unwrap().unwrap(); StructBody::take(source).unwrap(); } virtue-0.0.18/src/parse/mod.rs000064400000000000000000000107021046102023000142560ustar 00000000000000//! Module for parsing code. The main enum is [`Parse`]. use crate::prelude::*; mod attributes; mod body; mod data_type; mod generics; mod utils; mod visibility; pub use self::attributes::{Attribute, AttributeAccess, AttributeLocation, FromAttribute}; pub use self::body::{EnumBody, EnumVariant, Fields, IdentOrIndex, StructBody, UnnamedField}; pub(crate) use self::data_type::DataType; pub use self::generics::{ ConstGeneric, Generic, GenericConstraints, Generics, Lifetime, SimpleGeneric, }; pub use self::visibility::Visibility; use crate::generate::Generator; /// Parser for Enum and Struct derives. /// /// You can generate this enum by calling /// /// ```ignore /// use virtue::prelude::*; /// /// #[proc_macro_derive(YourDerive)] /// pub fn derive_your_derive(input: TokenStream) -> TokenStream { /// let parse = Parse::new(input).unwrap(); /// // rest /// # unimplemented!() /// } /// ``` #[non_exhaustive] pub enum Parse { /// The given input is a struct Struct { /// The attributes of the struct attributes: Vec, /// The visibility of the struct visibility: Visibility, /// The name of the struct name: Ident, /// The generics of the struct, e.g. `struct Foo { ... }` will be `F` generics: Option, /// The generic constraits of the struct, e.g. `struct Foo { ... } where F: Display` will be `F: Display` generic_constraints: Option, /// The body of the struct body: StructBody, }, /// The given input is an enum Enum { /// The attributes of the enum attributes: Vec, /// The visibility of the enum visibility: Visibility, /// The name of the enum name: Ident, /// The generics of the enum, e.g. `enum Foo { ... }` will be `F` generics: Option, /// The generic constraits of the enum, e.g. `enum Foo { ... } where F: Display` will be `F: Display` generic_constraints: Option, /// The body of the enum body: EnumBody, }, } impl Parse { /// Parse the given [`TokenStream`] and return the result. pub fn new(input: TokenStream) -> Result { let source = &mut input.into_iter().peekable(); let attributes = Attribute::try_take(AttributeLocation::Container, source)?; let visibility = Visibility::try_take(source)?; let (datatype, name) = DataType::take(source)?; let generics = Generics::try_take(source)?; let generic_constraints = GenericConstraints::try_take(source)?; match datatype { DataType::Struct => { let body = StructBody::take(source)?; Ok(Self::Struct { attributes, visibility, name, generics, generic_constraints, body, }) } DataType::Enum => { let body = EnumBody::take(source)?; Ok(Self::Enum { attributes, visibility, name, generics, generic_constraints, body, }) } } } /// Split this struct or enum into a [`Generator`], list of [`Attribute`] and [`Body`]. pub fn into_generator(self) -> (Generator, Vec, Body) { match self { Parse::Struct { name, generics, generic_constraints, body, attributes, .. } => ( Generator::new(name, generics, generic_constraints), attributes, Body::Struct(body), ), Parse::Enum { name, generics, generic_constraints, body, attributes, .. } => ( Generator::new(name, generics, generic_constraints), attributes, Body::Enum(body), ), } } } /// The body of the enum or struct #[allow(missing_docs)] pub enum Body { Struct(StructBody), Enum(EnumBody), } virtue-0.0.18/src/parse/utils.rs000064400000000000000000000125371046102023000146470ustar 00000000000000use crate::error::Error; use crate::prelude::{Delimiter, Group, Ident, Punct, TokenTree}; use std::iter::Peekable; pub fn assume_group(t: Option) -> Group { match t { Some(TokenTree::Group(group)) => group, _ => unreachable!(), } } pub fn assume_ident(t: Option) -> Ident { match t { Some(TokenTree::Ident(ident)) => ident, _ => unreachable!(), } } pub fn assume_punct(t: Option, punct: char) -> Punct { match t { Some(TokenTree::Punct(p)) => { debug_assert_eq!(punct, p.as_char()); p } _ => unreachable!(), } } pub fn consume_ident(input: &mut Peekable>) -> Option { match input.peek() { Some(TokenTree::Ident(_)) => Some(super::utils::assume_ident(input.next())), Some(TokenTree::Group(group)) => { // When calling from a macro_rules!, sometimes an ident is defined as : // Group { delimiter: None, stream: TokenStream [Ident] } let mut stream = group.stream().into_iter(); if let Some(TokenTree::Ident(i)) = stream.next() { if stream.next().is_none() { let _ = input.next(); return Some(i); } } None } _ => None, } } pub fn consume_punct_if( input: &mut Peekable>, punct: char, ) -> Option { if let Some(TokenTree::Punct(p)) = input.peek() { if p.as_char() == punct { match input.next() { Some(TokenTree::Punct(p)) => return Some(p), _ => unreachable!(), } } } None } #[cfg(any(test, feature = "proc-macro2"))] pub fn ident_eq(ident: &Ident, text: &str) -> bool { ident == text } #[cfg(not(any(test, feature = "proc-macro2")))] pub fn ident_eq(ident: &Ident, text: &str) -> bool { ident.to_string() == text } fn check_if_arrow(tokens: &[TokenTree], punct: &Punct) -> bool { if punct.as_char() == '>' { if let Some(TokenTree::Punct(previous_punct)) = tokens.last() { if previous_punct.as_char() == '-' { return true; } } } false } const OPEN_BRACKETS: &[char] = &['<', '(', '[', '{']; const CLOSING_BRACKETS: &[char] = &['>', ')', ']', '}']; const BRACKET_DELIMITER: &[Option] = &[ None, Some(Delimiter::Parenthesis), Some(Delimiter::Bracket), Some(Delimiter::Brace), ]; pub fn read_tokens_until_punct( input: &mut Peekable>, expected_puncts: &[char], ) -> Result, Error> { let mut result = Vec::new(); let mut open_brackets = Vec::::new(); 'outer: loop { match input.peek() { Some(TokenTree::Punct(punct)) => { if check_if_arrow(&result, punct) { // do nothing } else if OPEN_BRACKETS.contains(&punct.as_char()) { open_brackets.push(punct.as_char()); } else if let Some(index) = CLOSING_BRACKETS.iter().position(|c| c == &punct.as_char()) { let last_bracket = match open_brackets.pop() { Some(bracket) => bracket, None => { if expected_puncts.contains(&punct.as_char()) { break; } return Err(Error::InvalidRustSyntax { span: punct.span(), expected: format!( "one of {:?}, got '{}'", expected_puncts, punct.as_char() ), }); } }; let expected = OPEN_BRACKETS[index]; assert_eq!( expected, last_bracket, "Unexpected closing bracket: found {}, expected {}", punct.as_char(), expected ); } else if expected_puncts.contains(&punct.as_char()) && open_brackets.is_empty() { break; } result.push(input.next().unwrap()); } Some(TokenTree::Group(g)) if open_brackets.is_empty() => { for punct in expected_puncts { if let Some(idx) = OPEN_BRACKETS.iter().position(|c| c == punct) { if let Some(delim) = BRACKET_DELIMITER[idx] { if delim == g.delimiter() { // we need to split on this delimiter break 'outer; } } } } result.push(input.next().unwrap()); } Some(_) => result.push(input.next().unwrap()), None => { break; } } } Ok(result) } virtue-0.0.18/src/parse/visibility.rs000064400000000000000000000070531046102023000156730ustar 00000000000000use super::utils::*; use crate::prelude::{Delimiter, TokenTree}; use crate::Result; use std::iter::Peekable; /// The visibility of a struct, enum, field, etc #[derive(Debug, PartialEq, Eq, Clone)] pub enum Visibility { /// Default visibility. Most items are private by default. Default, /// Public visibility Pub, } impl Visibility { pub(crate) fn try_take(input: &mut Peekable>) -> Result { match input.peek() { Some(TokenTree::Ident(ident)) if ident_eq(ident, "pub") => { // Consume this token assume_ident(input.next()); // check if the next token is `pub(...)` if let Some(TokenTree::Group(g)) = input.peek() { if g.delimiter() == Delimiter::Parenthesis { // check if this is one of: // - pub ( crate ) // - pub ( self ) // - pub ( super ) // - pub ( in ... ) if let Some(TokenTree::Ident(i)) = g.stream().into_iter().next() { if matches!(i.to_string().as_str(), "crate" | "self" | "super" | "in") { // it is, ignore this token assume_group(input.next()); } } } } Ok(Visibility::Pub) } Some(TokenTree::Group(group)) => { // sometimes this is a group instead of an ident // e.g. when used in `bitflags! {}` let mut iter = group.stream().into_iter(); match (iter.next(), iter.next()) { (Some(TokenTree::Ident(ident)), None) if ident_eq(&ident, "pub") => { // Consume this token assume_group(input.next()); // check if the next token is `pub(...)` if let Some(TokenTree::Group(_)) = input.peek() { // we just consume the visibility, we're not actually using it for generation assume_group(input.next()); } Ok(Visibility::Pub) } _ => Ok(Visibility::Default), } } _ => Ok(Visibility::Default), } } } #[test] fn test_visibility_try_take() { use crate::token_stream; assert_eq!( Visibility::Default, Visibility::try_take(&mut token_stream("")).unwrap() ); assert_eq!( Visibility::Pub, Visibility::try_take(&mut token_stream("pub")).unwrap() ); assert_eq!( Visibility::Pub, Visibility::try_take(&mut token_stream(" pub ")).unwrap(), ); assert_eq!( Visibility::Pub, Visibility::try_take(&mut token_stream("\tpub\t")).unwrap() ); assert_eq!( Visibility::Pub, Visibility::try_take(&mut token_stream("pub(crate)")).unwrap() ); assert_eq!( Visibility::Pub, Visibility::try_take(&mut token_stream(" pub ( crate ) ")).unwrap() ); assert_eq!( Visibility::Pub, Visibility::try_take(&mut token_stream("\tpub\t(\tcrate\t)\t")).unwrap() ); assert_eq!( Visibility::Default, Visibility::try_take(&mut token_stream("pb")).unwrap() ); } virtue-0.0.18/src/utils.rs000064400000000000000000000132141046102023000135260ustar 00000000000000//! Utility functions use crate::{prelude::*, Error}; /// Parse a tagged attribute. This is very helpful for implementing [`FromAttribute`]. /// /// A tagged attribute is an attribute in the form of `#[prefix(result)]`. This function will return `Some(result)` if the `prefix` matches. /// /// The contents of the result can be either: /// - `ParsedAttribute::Tagged(Ident)`, e.g. `#[serde(skip)]` will be `Tagged("skip")` /// - `ParsedAttribute::Property(Ident, lit)`, e.g. `#[bincode(crate = "foo")]` will be `Property("crate", "foo")` /// /// # Examples /// ``` /// # use virtue::prelude::*; /// # use std::str::FromStr; /// # fn parse_token_stream_group(input: &'static str) -> Group { /// # let token_stream: TokenStream = proc_macro2::TokenStream::from_str(input).unwrap().into(); /// # let mut iter = token_stream.into_iter(); /// # let Some(TokenTree::Punct(_)) = iter.next() else { panic!() }; /// # let Some(TokenTree::Group(group)) = iter.next() else { panic!() }; /// # group /// # } /// use virtue::utils::{parse_tagged_attribute, ParsedAttribute}; /// /// // The attribute being parsed /// let group: Group = parse_token_stream_group("#[prefix(result, foo = \"bar\")]"); /// /// let attributes = parse_tagged_attribute(&group, "prefix").unwrap().unwrap(); /// let mut iter = attributes.into_iter(); /// /// // The stream will contain the contents of the `prefix(...)` /// match iter.next() { /// Some(ParsedAttribute::Tag(i)) => { /// assert_eq!(i.to_string(), String::from("result")); /// }, /// x => panic!("Unexpected attribute: {:?}", x) /// } /// match iter.next() { /// Some(ParsedAttribute::Property(key, val)) => { /// assert_eq!(key.to_string(), String::from("foo")); /// assert_eq!(val.to_string(), String::from("\"bar\"")); /// }, /// x => panic!("Unexpected attribute: {:?}", x) /// } /// /// ``` pub fn parse_tagged_attribute(group: &Group, prefix: &str) -> Result>> { let stream = &mut group.stream().into_iter(); if let Some(TokenTree::Ident(attribute_ident)) = stream.next() { #[allow(clippy::cmp_owned)] // clippy is wrong if attribute_ident.to_string() == prefix { if let Some(TokenTree::Group(group)) = stream.next() { let mut result = Vec::new(); let mut stream = group.stream().into_iter().peekable(); while let Some(token) = stream.next() { match (token, stream.peek()) { (TokenTree::Ident(key), Some(TokenTree::Punct(p))) if p.as_char() == ',' => { result.push(ParsedAttribute::Tag(key)); stream.next(); } (TokenTree::Ident(key), None) => { result.push(ParsedAttribute::Tag(key)); stream.next(); } (TokenTree::Ident(key), Some(TokenTree::Punct(p))) if p.as_char() == '=' => { stream.next(); if let Some(TokenTree::Literal(lit)) = stream.next() { result.push(ParsedAttribute::Property(key, lit)); match stream.next() { Some(TokenTree::Punct(p)) if p.as_char() == ',' => {} None => {} x => { return Err(Error::custom_at_opt_token("Expected `,`", x)); } } } } (x, _) => { return Err(Error::custom_at( "Expected `key` or `key = \"val\"`", x.span(), )); } } } return Ok(Some(result)); } } } Ok(None) } #[derive(Clone, Debug)] #[non_exhaustive] /// A parsed attribute. See [`parse_tagged_attribute`] for more information. pub enum ParsedAttribute { /// A tag, created by parsing `#[prefix(foo)]` Tag(Ident), /// A property, created by parsing `#[prefix(foo = "bar")]` Property(Ident, Literal), } #[test] fn test_parse_tagged_attribute() { let group: Group = match crate::token_stream("[prefix(result, foo = \"bar\", baz)]").next() { Some(TokenTree::Group(group)) => group, x => panic!("Unexpected token {:?}", x), }; let attributes = parse_tagged_attribute(&group, "prefix").unwrap().unwrap(); let mut iter = attributes.into_iter(); // The stream will contain the contents of the `prefix(...)` match iter.next() { Some(ParsedAttribute::Tag(i)) => { assert_eq!(i.to_string(), String::from("result")); } x => panic!("Unexpected attribute: {:?}", x), } match iter.next() { Some(ParsedAttribute::Property(key, val)) => { assert_eq!(key.to_string(), String::from("foo")); assert_eq!(val.to_string(), String::from("\"bar\"")); } x => panic!("Unexpected attribute: {:?}", x), } match iter.next() { Some(ParsedAttribute::Tag(i)) => { assert_eq!(i.to_string(), String::from("baz")); } x => panic!("Unexpected attribute: {:?}", x), } }