├── .gitignore ├── justfile ├── test ├── derive │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── Cargo.toml └── src │ └── main.rs ├── .github ├── dependabot.yml ├── workflows │ ├── security.yml │ └── rust.yml └── stale.yml ├── Cargo.toml ├── LICENSE.md ├── README.md └── src ├── parse ├── data_type.rs ├── visibility.rs ├── mod.rs ├── attributes.rs ├── utils.rs ├── generics.rs └── body.rs ├── lib.rs ├── generate ├── generate_mod.rs ├── stream_builder.rs ├── impl.rs ├── impl_for.rs ├── mod.rs ├── generator.rs ├── gen_struct.rs ├── generate_item.rs └── gen_enum.rs ├── utils.rs └── error.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | default: 2 | just --list 3 | 4 | ci: 5 | cargo fmt --all -- --check 6 | cargo clippy --all-features -- -D warnings 7 | cargo test -------------------------------------------------------------------------------- /test/derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtue_test_derive" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [lib] 8 | proc-macro = true 9 | 10 | [dependencies] 11 | virtue = { path = "../.." } 12 | -------------------------------------------------------------------------------- /test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtue_test" 3 | version = "0.0.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | virtue_test_derive = { path = "derive" } 11 | bitflags = "2.3" 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: / 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | 9 | - package-ecosystem: "github-actions" 10 | directory: "/" 11 | schedule: 12 | # Check for updates to GitHub Actions every weekday 13 | interval: "daily" 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtue" 3 | version = "0.0.19" 4 | edition = "2021" 5 | description = "A sinless derive macro helper" 6 | license = "MIT" 7 | documentation = "https://docs.rs/virtue" 8 | repository = "https://github.com/bincode-org/virtue" 9 | readme = "README.md" 10 | 11 | [dev-dependencies] 12 | proc-macro2 = "1.0" 13 | 14 | [dependencies] 15 | proc-macro2 = { version = "1.0", optional = true } 16 | 17 | [workspace] 18 | members = ["test", "test/derive"] 19 | -------------------------------------------------------------------------------- /.github/workflows/security.yml: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Security audit", 3 | "on": { "schedule": [{ "cron": "0 0 * * *" }] }, 4 | "jobs": 5 | { 6 | "audit": 7 | { 8 | "runs-on": "ubuntu-latest", 9 | "steps": 10 | [ 11 | { "uses": "actions/checkout@v4" }, 12 | { 13 | "uses": "actions-rs/audit-check@v1", 14 | "with": { "token": "${{ secrets.GITHUB_TOKEN }}" }, 15 | }, 16 | ], 17 | }, 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /test/src/main.rs: -------------------------------------------------------------------------------- 1 | bitflags::bitflags! { 2 | #[derive(virtue_test_derive::RetHi)] 3 | pub struct Foo: u8 { 4 | const A = 1; 5 | const B = 1; 6 | } 7 | } 8 | 9 | #[derive(virtue_test_derive::RetHi)] 10 | pub struct DefaultGeneric { 11 | pub t: T, 12 | } 13 | 14 | #[derive(virtue_test_derive::RetHi, Default)] 15 | pub struct MyStruct 16 | where 17 | T: Clone, 18 | { 19 | pub b: Vec, 20 | } 21 | 22 | fn main() { 23 | assert_eq!("hi", Foo::A.hi()); 24 | assert_eq!("hi", Foo::B.hi()); 25 | assert_eq!("hi", MyStruct::::default().hi()); 26 | } 27 | -------------------------------------------------------------------------------- /test/derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | use virtue::prelude::*; 2 | 3 | #[proc_macro_derive(RetHi)] 4 | pub fn derive_ret_hi(input: TokenStream) -> TokenStream { 5 | derive_ret_hi_inner(input).unwrap_or_else(|error| error.into_token_stream()) 6 | } 7 | 8 | fn derive_ret_hi_inner(input: TokenStream) -> Result { 9 | let parse = Parse::new(input)?; 10 | let (mut generator, _, _) = parse.into_generator(); 11 | generator 12 | .generate_impl() 13 | .generate_fn("hi") 14 | .with_attr("inline(never)") 15 | .with_self_arg(FnSelfArg::RefSelf) 16 | .with_return_type("&'static str") 17 | .body(|body| { 18 | body.lit_str("hi"); 19 | Ok(()) 20 | })?; 21 | generator.finish() 22 | } 23 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - bug 8 | - security 9 | - not-stale 10 | # Label to use when marking an issue as stale 11 | staleLabel: stale 12 | # Comment to post when marking an issue as stale. Set to `false` to disable 13 | markComment: > 14 | This issue has been automatically marked as stale because it has not had 15 | recent activity. It will be closed if no further activity occurs. Thank you 16 | for your contributions. 17 | # Comment to post when closing a stale issue. Set to `false` to disable 18 | closeComment: false 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Victor Koenders 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Due to a doxxing incident bincode development has officially ceased and will not resume. Updates will only be pushed to the in the unlikely event of CVEs. Do not contact us for any other reason. 2 | 3 | To those of you who bothered doxxing us. Go touch grass and maybe for once consider your actions have consequences for real people. 4 | 5 | Fuck off and worst regards, The Bincode Team 6 | 7 | # Virtue, a sinless derive macro helper 8 | 9 | ## Goals 10 | 11 | - Zero dependencies, so fast compile times 12 | - No other dependencies needed 13 | - Declarative code generation 14 | - As much typesystem checking as possible 15 | - Build for modern rust: 1.57 and up 16 | - Build with popular crates in mind: 17 | - [bincode](https://docs.rs/bincode) 18 | - Will always respect semver. Minor releases will never have: 19 | - Breaking API changes 20 | - MSRV changes 21 | 22 | ## Example 23 | 24 | ```rust 25 | use virtue::prelude::*; 26 | 27 | #[proc_macro_derive(YourDerive, attributes(some, attributes, go, here))] 28 | pub fn derive_your_derive(input: TokenStream) -> TokenStream { 29 | derive_your_derive_inner(input) 30 | .unwrap_or_else(|error| error.into_token_stream()) 31 | } 32 | 33 | fn derive_your_derive_inner(input: TokenStream) -> Result { 34 | // Parse the struct or enum you want to implement a derive for 35 | let parse = Parse::new(input)?; 36 | // Get a reference to the generator 37 | let (mut generator, body) = parse.into_generator(); 38 | match body { 39 | Body::Struct(body) => { 40 | // Implement your struct body here 41 | // See `Generator` for more information 42 | generator.impl_for("YourTrait")? 43 | .generate_fn("your_fn") 44 | .with_self_arg(FnSelfArg::RefSelf) 45 | .body(|fn_body| { 46 | fn_body.push_parsed("println!(\"Hello world\");"); 47 | })?; 48 | }, 49 | Body::Enum(body) => { 50 | // Implement your enum body here 51 | // See `Generator` for more information 52 | generator.impl_for("YourTrait")? 53 | .generate_fn("your_fn") 54 | .with_self_arg(FnSelfArg::RefSelf) 55 | .body(|fn_body| { 56 | fn_body.push_parsed("println!(\"Hello world\");"); 57 | })?; 58 | }, 59 | } 60 | generator.finish() 61 | } 62 | ``` 63 | 64 | Will generate 65 | 66 | ```ignore 67 | impl YourTrait for { 68 | fn your_fn(&self) { // .generate_fn("your_fn").with_self_arg(FnSelfArg::RefSelf) 69 | println!("Hello world"); // fn_body.push_parsed(...) 70 | } 71 | } 72 | ``` 73 | -------------------------------------------------------------------------------- /src/parse/data_type.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{Ident, TokenTree}; 2 | use crate::{Error, Result}; 3 | use std::iter::Peekable; 4 | 5 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 6 | pub(crate) enum DataType { 7 | Enum, 8 | Struct, 9 | } 10 | 11 | impl DataType { 12 | pub(crate) fn take( 13 | input: &mut Peekable>, 14 | ) -> Result<(Self, Ident)> { 15 | if let Some(ident) = super::utils::consume_ident(input) { 16 | let result = match ident.to_string().as_str() { 17 | "struct" => DataType::Struct, 18 | "enum" => DataType::Enum, 19 | _ => return Err(Error::UnknownDataType(ident.span())), 20 | }; 21 | 22 | if let Some(ident) = super::utils::consume_ident(input) { 23 | return Ok((result, ident)); 24 | } 25 | } 26 | Error::wrong_token(input.peek(), "ident") 27 | } 28 | } 29 | 30 | #[test] 31 | fn test_datatype_take() { 32 | use crate::token_stream; 33 | 34 | fn validate_output_eq(input: &str, expected_dt: DataType, expected_ident: &str) { 35 | let (dt, ident) = DataType::take(&mut token_stream(input)).unwrap_or_else(|e| { 36 | panic!("Could not parse tokenstream {:?}: {:?}", input, e); 37 | }); 38 | if dt != expected_dt || ident != expected_ident { 39 | println!("While parsing {:?}", input); 40 | panic!( 41 | "Expected {:?} {:?}, received {:?} {:?}", 42 | dt, ident, expected_dt, expected_ident 43 | ); 44 | } 45 | } 46 | 47 | assert!(DataType::take(&mut token_stream("enum")) 48 | .unwrap_err() 49 | .is_invalid_rust_syntax()); 50 | validate_output_eq("enum Foo", DataType::Enum, "Foo"); 51 | validate_output_eq("enum Foo { }", DataType::Enum, "Foo"); 52 | validate_output_eq("enum Foo { bar, baz }", DataType::Enum, "Foo"); 53 | validate_output_eq("enum Foo<'a, T> { bar, baz }", DataType::Enum, "Foo"); 54 | 55 | assert!(DataType::take(&mut token_stream("struct")) 56 | .unwrap_err() 57 | .is_invalid_rust_syntax()); 58 | validate_output_eq("struct Foo { }", DataType::Struct, "Foo"); 59 | validate_output_eq("struct Foo { bar: u32, baz: u32 }", DataType::Struct, "Foo"); 60 | validate_output_eq("struct Foo<'a, T> { bar: &'a T }", DataType::Struct, "Foo"); 61 | 62 | assert!(DataType::take(&mut token_stream("fn foo() {}")) 63 | .unwrap_err() 64 | .is_unknown_data_type()); 65 | 66 | assert!(DataType::take(&mut token_stream("() {}")) 67 | .unwrap_err() 68 | .is_invalid_rust_syntax()); 69 | 70 | assert!(DataType::take(&mut token_stream("")) 71 | .unwrap_err() 72 | .is_invalid_rust_syntax()); 73 | } 74 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Virtue, a sinless derive macro helper 2 | //! 3 | //! ## Goals 4 | //! 5 | //! - Zero dependencies, so fast compile times 6 | //! - No other dependencies needed 7 | //! - Declarative code generation 8 | //! - As much typesystem checking as possible 9 | //! - Build for modern rust: 1.57 and up 10 | //! - Build with popular crates in mind: 11 | //! - [bincode](https://docs.rs/bincode) 12 | //! - Will always respect semver. Minor releases will never have: 13 | //! - Breaking API changes 14 | //! - MSRV changes 15 | //! 16 | //! ## Example 17 | //! 18 | //! First, add this to your Cargo.toml: 19 | //! ```toml 20 | //! [lib] 21 | //! proc-macro = true 22 | //! ``` 23 | //! 24 | //! Then instantiate your project with: 25 | //! 26 | //! ```ignore 27 | //! use virtue::prelude::*; 28 | //! 29 | //! #[proc_macro_derive(RetHi)] // change this to change your #[derive(...)] name 30 | //! pub fn derive_ret_hi(input: TokenStream) -> TokenStream { 31 | //! derive_ret_hi_inner(input).unwrap_or_else(|error| error.into_token_stream()) 32 | //! } 33 | //! 34 | //! fn derive_ret_hi_inner(input: TokenStream) -> Result { 35 | //! let parse = Parse::new(input)?; 36 | //! let (mut generator, _attributes, _body) = parse.into_generator(); 37 | //! generator 38 | //! .generate_impl() 39 | //! .generate_fn("hi") 40 | //! .with_self_arg(FnSelfArg::RefSelf) 41 | //! .with_return_type("&'static str") 42 | //! .body(|body| { 43 | //! body.lit_str("hi"); 44 | //! Ok(()) 45 | //! })?; 46 | //! generator.finish() 47 | //! } 48 | //! ``` 49 | //! 50 | //! You can invoke this with 51 | //! 52 | //! ```ignore 53 | //! #[derive(RetHi)] 54 | //! struct Foo; 55 | //! 56 | //! fn main() { 57 | //! println!("{}", Foo.hi()); 58 | //! } 59 | //! ``` 60 | //! 61 | //! The generated code is: 62 | //! 63 | //! ```ignore 64 | //! impl Foo { 65 | //! fn hi(&self) -> &'static str { 66 | //! "hi" 67 | //! } 68 | //! } 69 | //! ``` 70 | #![warn(missing_docs)] 71 | 72 | mod error; 73 | 74 | pub mod generate; 75 | pub mod parse; 76 | pub mod utils; 77 | 78 | /// Result alias for virtue's errors 79 | pub type Result = std::result::Result; 80 | 81 | pub use self::error::Error; 82 | 83 | /// Useful includes 84 | pub mod prelude { 85 | pub use crate::generate::{FnSelfArg, Generator, StreamBuilder}; 86 | pub use crate::parse::{ 87 | AttributeAccess, Body, EnumVariant, Fields, FromAttribute, Parse, UnnamedField, 88 | }; 89 | pub use crate::{Error, Result}; 90 | 91 | #[cfg(any(test, feature = "proc-macro2"))] 92 | pub use proc_macro2::*; 93 | 94 | #[cfg(not(any(test, feature = "proc-macro2")))] 95 | extern crate proc_macro; 96 | #[cfg(not(any(test, feature = "proc-macro2")))] 97 | pub use proc_macro::*; 98 | } 99 | 100 | #[cfg(test)] 101 | pub(crate) fn token_stream( 102 | s: &str, 103 | ) -> std::iter::Peekable> { 104 | use std::str::FromStr; 105 | 106 | let stream = proc_macro2::TokenStream::from_str(s) 107 | .unwrap_or_else(|e| panic!("Could not parse code: {:?}\n{:?}", s, e)); 108 | stream.into_iter().peekable() 109 | } 110 | -------------------------------------------------------------------------------- /src/generate/generate_mod.rs: -------------------------------------------------------------------------------- 1 | use super::{GenEnum, GenStruct, Impl, Parent, StreamBuilder}; 2 | use crate::{ 3 | parse::Visibility, 4 | prelude::{Delimiter, Ident, Span}, 5 | Result, 6 | }; 7 | 8 | /// Builder for generating a module with its contents. 9 | pub struct GenerateMod<'a, P: Parent> { 10 | parent: &'a mut P, 11 | name: Ident, 12 | uses: Vec, 13 | vis: Visibility, 14 | content: StreamBuilder, 15 | } 16 | 17 | impl<'a, P: Parent> GenerateMod<'a, P> { 18 | pub(crate) fn new(parent: &'a mut P, name: impl Into) -> Self { 19 | Self { 20 | parent, 21 | name: Ident::new(name.into().as_str(), Span::call_site()), 22 | uses: Vec::new(), 23 | vis: Visibility::Default, 24 | content: StreamBuilder::new(), 25 | } 26 | } 27 | 28 | /// Add a `use ...;` to the current mod 29 | /// 30 | /// `generator.impl_mod("foo").add_use("bar")` will generate: 31 | /// 32 | /// ```ignore 33 | /// mod foo { 34 | /// use bar; 35 | /// } 36 | /// ``` 37 | /// 38 | /// This is especially useful with `.add_use("super::*");`, which will pull all parent imports into scope 39 | pub fn add_use(&mut self, r#use: impl AsRef) -> Result { 40 | let mut builder = StreamBuilder::new(); 41 | builder.ident_str("use").push_parsed(r#use)?.punct(';'); 42 | self.uses.push(builder); 43 | Ok(()) 44 | } 45 | 46 | /// Generate a struct with the given name. See [`GenStruct`] for more info. 47 | pub fn generate_struct(&mut self, name: impl Into) -> GenStruct { 48 | GenStruct::new(self, name) 49 | } 50 | 51 | /// Generate an enum with the given name. See [`GenEnum`] for more info. 52 | pub fn generate_enum(&mut self, name: impl Into) -> GenEnum { 53 | GenEnum::new(self, name) 54 | } 55 | 56 | /// Generate an `impl ` implementation. See [`Impl`] for more information. 57 | pub fn r#impl(&mut self, name: impl Into) -> Impl { 58 | Impl::new(self, name) 59 | } 60 | 61 | /// Generate an `impl ` implementation. See [`Impl`] for more information. 62 | /// 63 | /// Alias for [`impl`] which doesn't need a `r#` prefix. 64 | /// 65 | /// [`impl`]: #method.impl 66 | pub fn generate_impl(&mut self, name: impl Into) -> Impl { 67 | Impl::new(self, name) 68 | } 69 | } 70 | 71 | impl<'a, P: Parent> Drop for GenerateMod<'a, P> { 72 | fn drop(&mut self) { 73 | let mut builder = StreamBuilder::new(); 74 | if self.vis == Visibility::Pub { 75 | builder.ident_str("pub"); 76 | } 77 | builder 78 | .ident_str("mod") 79 | .ident(self.name.clone()) 80 | .group(Delimiter::Brace, |group| { 81 | for r#use in std::mem::take(&mut self.uses) { 82 | group.append(r#use); 83 | } 84 | group.append(std::mem::take(&mut self.content)); 85 | Ok(()) 86 | }) 87 | .unwrap(); 88 | 89 | self.parent.append(builder); 90 | } 91 | } 92 | 93 | impl Parent for GenerateMod<'_, P> { 94 | fn append(&mut self, builder: StreamBuilder) { 95 | self.content.append(builder); 96 | } 97 | 98 | fn name(&self) -> &crate::prelude::Ident { 99 | &self.name 100 | } 101 | 102 | fn generics(&self) -> Option<&crate::parse::Generics> { 103 | None 104 | } 105 | 106 | fn generic_constraints(&self) -> Option<&crate::parse::GenericConstraints> { 107 | None 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/parse/visibility.rs: -------------------------------------------------------------------------------- 1 | use super::utils::*; 2 | use crate::prelude::{Delimiter, TokenTree}; 3 | use crate::Result; 4 | use std::iter::Peekable; 5 | 6 | /// The visibility of a struct, enum, field, etc 7 | #[derive(Debug, PartialEq, Eq, Clone)] 8 | pub enum Visibility { 9 | /// Default visibility. Most items are private by default. 10 | Default, 11 | 12 | /// Public visibility 13 | Pub, 14 | } 15 | 16 | impl Visibility { 17 | pub(crate) fn try_take(input: &mut Peekable>) -> Result { 18 | match input.peek() { 19 | Some(TokenTree::Ident(ident)) if ident_eq(ident, "pub") => { 20 | // Consume this token 21 | assume_ident(input.next()); 22 | 23 | // check if the next token is `pub(...)` 24 | if let Some(TokenTree::Group(g)) = input.peek() { 25 | if g.delimiter() == Delimiter::Parenthesis { 26 | // check if this is one of: 27 | // - pub ( crate ) 28 | // - pub ( self ) 29 | // - pub ( super ) 30 | // - pub ( in ... ) 31 | if let Some(TokenTree::Ident(i)) = g.stream().into_iter().next() { 32 | if matches!(i.to_string().as_str(), "crate" | "self" | "super" | "in") { 33 | // it is, ignore this token 34 | assume_group(input.next()); 35 | } 36 | } 37 | } 38 | } 39 | 40 | Ok(Visibility::Pub) 41 | } 42 | Some(TokenTree::Group(group)) => { 43 | // sometimes this is a group instead of an ident 44 | // e.g. when used in `bitflags! {}` 45 | let mut iter = group.stream().into_iter(); 46 | match (iter.next(), iter.next()) { 47 | (Some(TokenTree::Ident(ident)), None) if ident_eq(&ident, "pub") => { 48 | // Consume this token 49 | assume_group(input.next()); 50 | 51 | // check if the next token is `pub(...)` 52 | if let Some(TokenTree::Group(_)) = input.peek() { 53 | // we just consume the visibility, we're not actually using it for generation 54 | assume_group(input.next()); 55 | } 56 | Ok(Visibility::Pub) 57 | } 58 | _ => Ok(Visibility::Default), 59 | } 60 | } 61 | _ => Ok(Visibility::Default), 62 | } 63 | } 64 | } 65 | 66 | #[test] 67 | fn test_visibility_try_take() { 68 | use crate::token_stream; 69 | 70 | assert_eq!( 71 | Visibility::Default, 72 | Visibility::try_take(&mut token_stream("")).unwrap() 73 | ); 74 | assert_eq!( 75 | Visibility::Pub, 76 | Visibility::try_take(&mut token_stream("pub")).unwrap() 77 | ); 78 | assert_eq!( 79 | Visibility::Pub, 80 | Visibility::try_take(&mut token_stream(" pub ")).unwrap(), 81 | ); 82 | assert_eq!( 83 | Visibility::Pub, 84 | Visibility::try_take(&mut token_stream("\tpub\t")).unwrap() 85 | ); 86 | assert_eq!( 87 | Visibility::Pub, 88 | Visibility::try_take(&mut token_stream("pub(crate)")).unwrap() 89 | ); 90 | assert_eq!( 91 | Visibility::Pub, 92 | Visibility::try_take(&mut token_stream(" pub ( crate ) ")).unwrap() 93 | ); 94 | assert_eq!( 95 | Visibility::Pub, 96 | Visibility::try_take(&mut token_stream("\tpub\t(\tcrate\t)\t")).unwrap() 97 | ); 98 | 99 | assert_eq!( 100 | Visibility::Default, 101 | Visibility::try_take(&mut token_stream("pb")).unwrap() 102 | ); 103 | } 104 | -------------------------------------------------------------------------------- /src/parse/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module for parsing code. The main enum is [`Parse`]. 2 | 3 | use crate::prelude::*; 4 | 5 | mod attributes; 6 | mod body; 7 | mod data_type; 8 | mod generics; 9 | mod utils; 10 | mod visibility; 11 | 12 | pub use self::attributes::{Attribute, AttributeAccess, AttributeLocation, FromAttribute}; 13 | pub use self::body::{EnumBody, EnumVariant, Fields, IdentOrIndex, StructBody, UnnamedField}; 14 | pub(crate) use self::data_type::DataType; 15 | pub use self::generics::{ 16 | ConstGeneric, Generic, GenericConstraints, Generics, Lifetime, SimpleGeneric, 17 | }; 18 | pub use self::visibility::Visibility; 19 | 20 | use crate::generate::Generator; 21 | 22 | /// Parser for Enum and Struct derives. 23 | /// 24 | /// You can generate this enum by calling 25 | /// 26 | /// ```ignore 27 | /// use virtue::prelude::*; 28 | /// 29 | /// #[proc_macro_derive(YourDerive)] 30 | /// pub fn derive_your_derive(input: TokenStream) -> TokenStream { 31 | /// let parse = Parse::new(input).unwrap(); 32 | /// // rest 33 | /// # unimplemented!() 34 | /// } 35 | /// ``` 36 | #[non_exhaustive] 37 | pub enum Parse { 38 | /// The given input is a struct 39 | Struct { 40 | /// The attributes of the struct 41 | attributes: Vec, 42 | /// The visibility of the struct 43 | visibility: Visibility, 44 | /// The name of the struct 45 | name: Ident, 46 | /// The generics of the struct, e.g. `struct Foo { ... }` will be `F` 47 | generics: Option, 48 | /// The generic constraits of the struct, e.g. `struct Foo { ... } where F: Display` will be `F: Display` 49 | generic_constraints: Option, 50 | /// The body of the struct 51 | body: StructBody, 52 | }, 53 | /// The given input is an enum 54 | Enum { 55 | /// The attributes of the enum 56 | attributes: Vec, 57 | /// The visibility of the enum 58 | visibility: Visibility, 59 | /// The name of the enum 60 | name: Ident, 61 | /// The generics of the enum, e.g. `enum Foo { ... }` will be `F` 62 | generics: Option, 63 | /// The generic constraits of the enum, e.g. `enum Foo { ... } where F: Display` will be `F: Display` 64 | generic_constraints: Option, 65 | /// The body of the enum 66 | body: EnumBody, 67 | }, 68 | } 69 | 70 | impl Parse { 71 | /// Parse the given [`TokenStream`] and return the result. 72 | pub fn new(input: TokenStream) -> Result { 73 | let source = &mut input.into_iter().peekable(); 74 | 75 | let attributes = Attribute::try_take(AttributeLocation::Container, source)?; 76 | let visibility = Visibility::try_take(source)?; 77 | let (datatype, name) = DataType::take(source)?; 78 | let generics = Generics::try_take(source)?; 79 | let generic_constraints = GenericConstraints::try_take(source)?; 80 | match datatype { 81 | DataType::Struct => { 82 | let body = StructBody::take(source)?; 83 | Ok(Self::Struct { 84 | attributes, 85 | visibility, 86 | name, 87 | generics, 88 | generic_constraints, 89 | body, 90 | }) 91 | } 92 | DataType::Enum => { 93 | let body = EnumBody::take(source)?; 94 | Ok(Self::Enum { 95 | attributes, 96 | visibility, 97 | name, 98 | generics, 99 | generic_constraints, 100 | body, 101 | }) 102 | } 103 | } 104 | } 105 | 106 | /// Split this struct or enum into a [`Generator`], list of [`Attribute`] and [`Body`]. 107 | pub fn into_generator(self) -> (Generator, Vec, Body) { 108 | match self { 109 | Parse::Struct { 110 | name, 111 | generics, 112 | generic_constraints, 113 | body, 114 | attributes, 115 | .. 116 | } => ( 117 | Generator::new(name, generics, generic_constraints), 118 | attributes, 119 | Body::Struct(body), 120 | ), 121 | Parse::Enum { 122 | name, 123 | generics, 124 | generic_constraints, 125 | body, 126 | attributes, 127 | .. 128 | } => ( 129 | Generator::new(name, generics, generic_constraints), 130 | attributes, 131 | Body::Enum(body), 132 | ), 133 | } 134 | } 135 | } 136 | 137 | /// The body of the enum or struct 138 | #[allow(missing_docs)] 139 | pub enum Body { 140 | Struct(StructBody), 141 | Enum(EnumBody), 142 | } 143 | -------------------------------------------------------------------------------- /src/parse/attributes.rs: -------------------------------------------------------------------------------- 1 | use super::utils::*; 2 | use crate::prelude::{Delimiter, Group, Punct, TokenTree}; 3 | use crate::{Error, Result}; 4 | use std::iter::Peekable; 5 | 6 | /// An attribute for the given struct, enum, field, etc 7 | #[derive(Debug, Clone)] 8 | #[non_exhaustive] 9 | pub struct Attribute { 10 | /// The location this attribute was parsed at 11 | pub location: AttributeLocation, 12 | /// The punct token of the attribute. This will always be `Punct('#')` 13 | pub punct: Punct, 14 | /// The group of tokens of the attribute. You can parse this to get your custom attributes. 15 | pub tokens: Group, 16 | } 17 | 18 | /// The location an attribute can be found at 19 | #[derive(PartialEq, Eq, Debug, Hash, Copy, Clone)] 20 | #[non_exhaustive] 21 | pub enum AttributeLocation { 22 | /// The attribute is on a container, which will be either a `struct` or an `enum` 23 | Container, 24 | /// The attribute is on an enum variant 25 | Variant, 26 | /// The attribute is on a field, which can either be a struct field or an enum variant field 27 | /// ```ignore 28 | /// struct Foo { 29 | /// #[attr] // here 30 | /// pub a: u8 31 | /// } 32 | /// struct Bar { 33 | /// Baz { 34 | /// #[attr] // or here 35 | /// a: u8 36 | /// } 37 | /// } 38 | /// ``` 39 | Field, 40 | } 41 | 42 | impl Attribute { 43 | pub(crate) fn try_take( 44 | location: AttributeLocation, 45 | input: &mut Peekable>, 46 | ) -> Result> { 47 | let mut result = Vec::new(); 48 | 49 | while let Some(punct) = consume_punct_if(input, '#') { 50 | match input.peek() { 51 | Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => { 52 | let group = assume_group(input.next()); 53 | result.push(Attribute { 54 | location, 55 | punct, 56 | tokens: group, 57 | }); 58 | } 59 | Some(TokenTree::Group(g)) => { 60 | return Err(Error::InvalidRustSyntax { 61 | span: g.span(), 62 | expected: format!("[] bracket, got {:?}", g.delimiter()), 63 | }); 64 | } 65 | Some(TokenTree::Punct(p)) if p.as_char() == '#' => { 66 | // sometimes with empty lines of doc comments, we get two #'s in a row 67 | // Just ignore this 68 | } 69 | token => return Error::wrong_token(token, "[] group or next # attribute"), 70 | } 71 | } 72 | Ok(result) 73 | } 74 | } 75 | 76 | #[test] 77 | fn test_attributes_try_take() { 78 | use crate::token_stream; 79 | 80 | let stream = &mut token_stream("struct Foo;"); 81 | assert!(Attribute::try_take(AttributeLocation::Container, stream) 82 | .unwrap() 83 | .is_empty()); 84 | match stream.next().unwrap() { 85 | TokenTree::Ident(i) => assert_eq!(i, "struct"), 86 | x => panic!("Expected ident, found {:?}", x), 87 | } 88 | 89 | let stream = &mut token_stream("#[cfg(test)] struct Foo;"); 90 | assert!(!Attribute::try_take(AttributeLocation::Container, stream) 91 | .unwrap() 92 | .is_empty()); 93 | match stream.next().unwrap() { 94 | TokenTree::Ident(i) => assert_eq!(i, "struct"), 95 | x => panic!("Expected ident, found {:?}", x), 96 | } 97 | } 98 | 99 | /// Helper trait for [`AttributeAccess`] methods. 100 | /// 101 | /// This can be implemented on your own type to make parsing easier. 102 | /// 103 | /// Some functions that can make your life easier: 104 | /// - [`utils::parse_tagged_attribute`] is a helper for parsing attributes in the format of `#[prefix(...)]` 105 | /// 106 | /// [`AttributeAccess`]: trait.AttributeAccess.html 107 | /// [`utils::parse_tagged_attribute`]: ../utils/fn.parse_tagged_attribute.html 108 | pub trait FromAttribute: Sized { 109 | /// 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. 110 | fn parse(group: &Group) -> Result>; 111 | } 112 | 113 | /// Bring useful methods to access attributes of an element. 114 | pub trait AttributeAccess { 115 | /// Check to see if has the given attribute. See [`FromAttribute`] for more information. 116 | /// 117 | /// **note**: Will immediately return `Err(_)` on the first error `T` returns. 118 | fn has_attribute>(&self, attrib: T) -> Result; 119 | 120 | /// Returns the first attribute that returns `Some(Self)`. See [`FromAttribute`] for more information. 121 | /// 122 | /// **note**: Will immediately return `Err(_)` on the first error `T` returns. 123 | fn get_attribute(&self) -> Result>; 124 | } 125 | 126 | impl AttributeAccess for Vec { 127 | fn has_attribute>(&self, attrib: T) -> Result { 128 | for attribute in self.iter() { 129 | if let Some(attribute) = T::parse(&attribute.tokens)? { 130 | if attribute == attrib { 131 | return Ok(true); 132 | } 133 | } 134 | } 135 | Ok(false) 136 | } 137 | 138 | fn get_attribute(&self) -> Result> { 139 | for attribute in self.iter() { 140 | if let Some(attribute) = T::parse(&attribute.tokens)? { 141 | return Ok(Some(attribute)); 142 | } 143 | } 144 | Ok(None) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/parse/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::prelude::{Delimiter, Group, Ident, Punct, TokenTree}; 3 | use std::iter::Peekable; 4 | 5 | pub fn assume_group(t: Option) -> Group { 6 | match t { 7 | Some(TokenTree::Group(group)) => group, 8 | _ => unreachable!(), 9 | } 10 | } 11 | pub fn assume_ident(t: Option) -> Ident { 12 | match t { 13 | Some(TokenTree::Ident(ident)) => ident, 14 | _ => unreachable!(), 15 | } 16 | } 17 | pub fn assume_punct(t: Option, punct: char) -> Punct { 18 | match t { 19 | Some(TokenTree::Punct(p)) => { 20 | debug_assert_eq!(punct, p.as_char()); 21 | p 22 | } 23 | _ => unreachable!(), 24 | } 25 | } 26 | 27 | pub fn consume_ident(input: &mut Peekable>) -> Option { 28 | match input.peek() { 29 | Some(TokenTree::Ident(_)) => Some(super::utils::assume_ident(input.next())), 30 | Some(TokenTree::Group(group)) => { 31 | // When calling from a macro_rules!, sometimes an ident is defined as : 32 | // Group { delimiter: None, stream: TokenStream [Ident] } 33 | let mut stream = group.stream().into_iter(); 34 | if let Some(TokenTree::Ident(i)) = stream.next() { 35 | if stream.next().is_none() { 36 | let _ = input.next(); 37 | return Some(i); 38 | } 39 | } 40 | None 41 | } 42 | _ => None, 43 | } 44 | } 45 | 46 | pub fn consume_punct_if( 47 | input: &mut Peekable>, 48 | punct: char, 49 | ) -> Option { 50 | if let Some(TokenTree::Punct(p)) = input.peek() { 51 | if p.as_char() == punct { 52 | match input.next() { 53 | Some(TokenTree::Punct(p)) => return Some(p), 54 | _ => unreachable!(), 55 | } 56 | } 57 | } 58 | None 59 | } 60 | 61 | #[cfg(any(test, feature = "proc-macro2"))] 62 | pub fn ident_eq(ident: &Ident, text: &str) -> bool { 63 | ident == text 64 | } 65 | 66 | #[cfg(not(any(test, feature = "proc-macro2")))] 67 | pub fn ident_eq(ident: &Ident, text: &str) -> bool { 68 | ident.to_string() == text 69 | } 70 | 71 | fn check_if_arrow(tokens: &[TokenTree], punct: &Punct) -> bool { 72 | if punct.as_char() == '>' { 73 | if let Some(TokenTree::Punct(previous_punct)) = tokens.last() { 74 | if previous_punct.as_char() == '-' { 75 | return true; 76 | } 77 | } 78 | } 79 | false 80 | } 81 | 82 | const OPEN_BRACKETS: &[char] = &['<', '(', '[', '{']; 83 | const CLOSING_BRACKETS: &[char] = &['>', ')', ']', '}']; 84 | const BRACKET_DELIMITER: &[Option] = &[ 85 | None, 86 | Some(Delimiter::Parenthesis), 87 | Some(Delimiter::Bracket), 88 | Some(Delimiter::Brace), 89 | ]; 90 | 91 | pub fn read_tokens_until_punct( 92 | input: &mut Peekable>, 93 | expected_puncts: &[char], 94 | ) -> Result, Error> { 95 | let mut result = Vec::new(); 96 | let mut open_brackets = Vec::::new(); 97 | 'outer: loop { 98 | match input.peek() { 99 | Some(TokenTree::Punct(punct)) => { 100 | if check_if_arrow(&result, punct) { 101 | // do nothing 102 | } else if OPEN_BRACKETS.contains(&punct.as_char()) { 103 | open_brackets.push(punct.as_char()); 104 | } else if let Some(index) = 105 | CLOSING_BRACKETS.iter().position(|c| c == &punct.as_char()) 106 | { 107 | let last_bracket = match open_brackets.pop() { 108 | Some(bracket) => bracket, 109 | None => { 110 | if expected_puncts.contains(&punct.as_char()) { 111 | break; 112 | } 113 | return Err(Error::InvalidRustSyntax { 114 | span: punct.span(), 115 | expected: format!( 116 | "one of {:?}, got '{}'", 117 | expected_puncts, 118 | punct.as_char() 119 | ), 120 | }); 121 | } 122 | }; 123 | let expected = OPEN_BRACKETS[index]; 124 | assert_eq!( 125 | expected, 126 | last_bracket, 127 | "Unexpected closing bracket: found {}, expected {}", 128 | punct.as_char(), 129 | expected 130 | ); 131 | } else if expected_puncts.contains(&punct.as_char()) && open_brackets.is_empty() { 132 | break; 133 | } 134 | result.push(input.next().unwrap()); 135 | } 136 | Some(TokenTree::Group(g)) if open_brackets.is_empty() => { 137 | for punct in expected_puncts { 138 | if let Some(idx) = OPEN_BRACKETS.iter().position(|c| c == punct) { 139 | if let Some(delim) = BRACKET_DELIMITER[idx] { 140 | if delim == g.delimiter() { 141 | // we need to split on this delimiter 142 | break 'outer; 143 | } 144 | } 145 | } 146 | } 147 | result.push(input.next().unwrap()); 148 | } 149 | Some(_) => result.push(input.next().unwrap()), 150 | None => { 151 | break; 152 | } 153 | } 154 | } 155 | Ok(result) 156 | } 157 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Utility functions 2 | use crate::{prelude::*, Error}; 3 | 4 | /// Parse a tagged attribute. This is very helpful for implementing [`FromAttribute`]. 5 | /// 6 | /// A tagged attribute is an attribute in the form of `#[prefix(result)]`. This function will return `Some(result)` if the `prefix` matches. 7 | /// 8 | /// The contents of the result can be either: 9 | /// - `ParsedAttribute::Tagged(Ident)`, e.g. `#[serde(skip)]` will be `Tagged("skip")` 10 | /// - `ParsedAttribute::Property(Ident, lit)`, e.g. `#[bincode(crate = "foo")]` will be `Property("crate", "foo")` 11 | /// 12 | /// # Examples 13 | /// ``` 14 | /// # use virtue::prelude::*; 15 | /// # use std::str::FromStr; 16 | /// # fn parse_token_stream_group(input: &'static str) -> Group { 17 | /// # let token_stream: TokenStream = proc_macro2::TokenStream::from_str(input).unwrap().into(); 18 | /// # let mut iter = token_stream.into_iter(); 19 | /// # let Some(TokenTree::Punct(_)) = iter.next() else { panic!() }; 20 | /// # let Some(TokenTree::Group(group)) = iter.next() else { panic!() }; 21 | /// # group 22 | /// # } 23 | /// use virtue::utils::{parse_tagged_attribute, ParsedAttribute}; 24 | /// 25 | /// // The attribute being parsed 26 | /// let group: Group = parse_token_stream_group("#[prefix(result, foo = \"bar\")]"); 27 | /// 28 | /// let attributes = parse_tagged_attribute(&group, "prefix").unwrap().unwrap(); 29 | /// let mut iter = attributes.into_iter(); 30 | /// 31 | /// // The stream will contain the contents of the `prefix(...)` 32 | /// match iter.next() { 33 | /// Some(ParsedAttribute::Tag(i)) => { 34 | /// assert_eq!(i.to_string(), String::from("result")); 35 | /// }, 36 | /// x => panic!("Unexpected attribute: {:?}", x) 37 | /// } 38 | /// match iter.next() { 39 | /// Some(ParsedAttribute::Property(key, val)) => { 40 | /// assert_eq!(key.to_string(), String::from("foo")); 41 | /// assert_eq!(val.to_string(), String::from("\"bar\"")); 42 | /// }, 43 | /// x => panic!("Unexpected attribute: {:?}", x) 44 | /// } 45 | /// 46 | /// ``` 47 | pub fn parse_tagged_attribute(group: &Group, prefix: &str) -> Result>> { 48 | let stream = &mut group.stream().into_iter(); 49 | if let Some(TokenTree::Ident(attribute_ident)) = stream.next() { 50 | #[allow(clippy::cmp_owned)] // clippy is wrong 51 | if attribute_ident.to_string() == prefix { 52 | if let Some(TokenTree::Group(group)) = stream.next() { 53 | let mut result = Vec::new(); 54 | let mut stream = group.stream().into_iter().peekable(); 55 | while let Some(token) = stream.next() { 56 | match (token, stream.peek()) { 57 | (TokenTree::Ident(key), Some(TokenTree::Punct(p))) 58 | if p.as_char() == ',' => 59 | { 60 | result.push(ParsedAttribute::Tag(key)); 61 | stream.next(); 62 | } 63 | (TokenTree::Ident(key), None) => { 64 | result.push(ParsedAttribute::Tag(key)); 65 | stream.next(); 66 | } 67 | (TokenTree::Ident(key), Some(TokenTree::Punct(p))) 68 | if p.as_char() == '=' => 69 | { 70 | stream.next(); 71 | if let Some(TokenTree::Literal(lit)) = stream.next() { 72 | result.push(ParsedAttribute::Property(key, lit)); 73 | 74 | match stream.next() { 75 | Some(TokenTree::Punct(p)) if p.as_char() == ',' => {} 76 | None => {} 77 | x => { 78 | return Err(Error::custom_at_opt_token("Expected `,`", x)); 79 | } 80 | } 81 | } 82 | } 83 | (x, _) => { 84 | return Err(Error::custom_at( 85 | "Expected `key` or `key = \"val\"`", 86 | x.span(), 87 | )); 88 | } 89 | } 90 | } 91 | 92 | return Ok(Some(result)); 93 | } 94 | } 95 | } 96 | Ok(None) 97 | } 98 | 99 | #[derive(Clone, Debug)] 100 | #[non_exhaustive] 101 | /// A parsed attribute. See [`parse_tagged_attribute`] for more information. 102 | pub enum ParsedAttribute { 103 | /// A tag, created by parsing `#[prefix(foo)]` 104 | Tag(Ident), 105 | /// A property, created by parsing `#[prefix(foo = "bar")]` 106 | Property(Ident, Literal), 107 | } 108 | 109 | #[test] 110 | fn test_parse_tagged_attribute() { 111 | let group: Group = match crate::token_stream("[prefix(result, foo = \"bar\", baz)]").next() { 112 | Some(TokenTree::Group(group)) => group, 113 | x => panic!("Unexpected token {:?}", x), 114 | }; 115 | 116 | let attributes = parse_tagged_attribute(&group, "prefix").unwrap().unwrap(); 117 | let mut iter = attributes.into_iter(); 118 | 119 | // The stream will contain the contents of the `prefix(...)` 120 | match iter.next() { 121 | Some(ParsedAttribute::Tag(i)) => { 122 | assert_eq!(i.to_string(), String::from("result")); 123 | } 124 | x => panic!("Unexpected attribute: {:?}", x), 125 | } 126 | match iter.next() { 127 | Some(ParsedAttribute::Property(key, val)) => { 128 | assert_eq!(key.to_string(), String::from("foo")); 129 | assert_eq!(val.to_string(), String::from("\"bar\"")); 130 | } 131 | x => panic!("Unexpected attribute: {:?}", x), 132 | } 133 | match iter.next() { 134 | Some(ParsedAttribute::Tag(i)) => { 135 | assert_eq!(i.to_string(), String::from("baz")); 136 | } 137 | x => panic!("Unexpected attribute: {:?}", x), 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CI", 3 | "on": 4 | { 5 | "push": { "branches": ["trunk", "v*", "ci/*"] }, 6 | "pull_request": { "branches": ["trunk", "v*"] }, 7 | }, 8 | "jobs": 9 | { 10 | "check": 11 | { 12 | "name": "Check", 13 | "runs-on": "ubuntu-latest", 14 | "strategy": { "fail-fast": false, "matrix": { "rust": [ 15 | "stable", 16 | "beta", 17 | "nightly", 18 | # "1.55.0" TODO: Pick latest stable version when we release 2.0 19 | ] } }, 20 | "steps": 21 | [ 22 | { "uses": "actions/checkout@v4", "name": "Checkout" }, 23 | { 24 | "uses": "actions-rs/toolchain@v1", 25 | "with": 26 | { 27 | "profile": "minimal", 28 | "toolchain": "${{ matrix.rust }}", 29 | "override": true, 30 | }, 31 | "name": "Install Rust ${{ matrix.rust }}", 32 | }, 33 | { 34 | "uses": "actions-rs/cargo@v1", 35 | "with": { "command": "check", "args": "--all-features" }, 36 | "name": "Run `cargo check`", 37 | }, 38 | { 39 | "uses": "actions-rs/cargo@v1", 40 | "with": { "command": "check", "args": "--examples" }, 41 | "name": "Check examples", 42 | }, 43 | ], 44 | }, 45 | "test": 46 | { 47 | "name": "Test", 48 | "runs-on": "ubuntu-latest", 49 | "strategy": { "matrix": { "rust": [ 50 | "stable", 51 | # "1.55.0" TODO: Pick latest stable version when we release 2.0 52 | ] } }, 53 | "steps": 54 | [ 55 | { "uses": "actions/checkout@v4", "name": "Checkout" }, 56 | { 57 | "uses": "actions-rs/toolchain@v1", 58 | "with": 59 | { 60 | "profile": "minimal", 61 | "toolchain": "${{ matrix.rust }}", 62 | "override": true, 63 | }, 64 | "name": "Install Rust ${{ matrix.rust }}", 65 | }, 66 | { 67 | "run": "cargo test --all --features proc-macro2", 68 | "name": "Run `cargo test`", 69 | "env": { "RUSTFLAGS": "-D warnings" }, 70 | }, 71 | ], 72 | }, 73 | "test_project": 74 | { 75 | "name": "Test Project", 76 | "runs-on": "ubuntu-latest", 77 | "strategy": { "matrix": { "rust": [ 78 | "stable", 79 | # "1.55.0" TODO: Pick latest stable version when we release 0.1 80 | ] } }, 81 | "steps": 82 | [ 83 | { "uses": "actions/checkout@v4", "name": "Checkout" }, 84 | { 85 | "uses": "actions-rs/toolchain@v1", 86 | "with": 87 | { 88 | "profile": "minimal", 89 | "toolchain": "${{ matrix.rust }}", 90 | "override": true, 91 | }, 92 | "name": "Install Rust ${{ matrix.rust }}", 93 | }, 94 | { 95 | "run": "cd test && cargo run", 96 | "name": "Run the test project", 97 | "env": { "RUSTFLAGS": "-D warnings" }, 98 | }, 99 | ], 100 | }, 101 | "lints": 102 | { 103 | "name": "Lints", 104 | "runs-on": "ubuntu-latest", 105 | "steps": 106 | [ 107 | { "uses": "actions/checkout@v4", "name": "Checkout" }, 108 | { 109 | "uses": "actions-rs/toolchain@v1", 110 | "with": 111 | { 112 | "profile": "minimal", 113 | "toolchain": "stable", 114 | "override": true, 115 | "components": "rustfmt, clippy", 116 | }, 117 | "name": "Install Rust stable", 118 | }, 119 | { 120 | "uses": "actions-rs/cargo@v1", 121 | "with": { "command": "fmt", "args": "--all -- --check" }, 122 | "name": "Run `cargo fmt`", 123 | }, 124 | { 125 | "uses": "actions-rs/cargo@v1", 126 | "with": 127 | { 128 | "command": "clippy", 129 | "args": "--all-features -- -D warnings", 130 | }, 131 | "name": "Run `cargo clippy`", 132 | }, 133 | ], 134 | }, 135 | "coverage": 136 | { 137 | "name": "Code Coverage", 138 | "runs-on": "ubuntu-latest", 139 | "steps": 140 | [ 141 | { "uses": "actions/checkout@v4", "name": "Checkout" }, 142 | { 143 | "uses": "actions-rs/toolchain@v1", 144 | "with": 145 | { 146 | "profile": "minimal", 147 | "toolchain": "nightly", 148 | "override": true, 149 | }, 150 | "name": "Install Rust nightly", 151 | }, 152 | { 153 | "name": "Run cargo-tarpaulin", 154 | "uses": "actions-rs/tarpaulin@v0.1", 155 | "with": { "version": "0.18.2", "args": "--all" }, 156 | }, 157 | { 158 | "name": "Upload to codecov.io", 159 | "uses": "codecov/codecov-action@v3", 160 | }, 161 | { 162 | "name": "Archive code coverage results", 163 | "uses": "actions/upload-artifact@v3", 164 | "with": 165 | { "name": "code-coverage-report", "path": "cobertura.xml" }, 166 | }, 167 | ], 168 | }, 169 | }, 170 | } 171 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | generate::{PushParseError, StreamBuilder}, 3 | prelude::*, 4 | }; 5 | use std::fmt; 6 | 7 | /// Errors that can occur while parsing or generator your derive macro. 8 | #[derive(Debug)] 9 | pub enum Error { 10 | /// 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`. 11 | /// 12 | /// [`Parse::new`]: enum.Parse.html#method.new 13 | UnknownDataType(Span), 14 | 15 | /// The rust syntax is invalid. This can be returned while parsing the Enum or Struct. 16 | /// 17 | /// This error is assumed to not appear as rustc will do syntax checking before virtue gets access to the [`TokenStream`]. 18 | /// However this error could still be returned. 19 | InvalidRustSyntax { 20 | /// The span at which the invalid syntax is found 21 | span: Span, 22 | /// The expected rust syntax when this parsing occured 23 | expected: String, 24 | }, 25 | 26 | /// Expected an ident at the given span. 27 | ExpectedIdent(Span), 28 | 29 | /// Failed to parse the code passed to [`StreamBuilder::push_parsed`]. 30 | /// 31 | /// [`StreamBuilder::push_parsed`]: struct.StreamBuilder.html#method.push_parsed 32 | PushParse { 33 | /// An optional span. Normally this is `None`, unless `.with_span` is called. 34 | span: Option, 35 | /// The internal parse error 36 | error: PushParseError, 37 | }, 38 | 39 | /// A custom error thrown by the developer 40 | Custom { 41 | /// The error message 42 | error: String, 43 | /// Optionally the position that the error occurred at 44 | span: Option, 45 | }, 46 | } 47 | 48 | impl From for Error { 49 | fn from(e: PushParseError) -> Self { 50 | Self::PushParse { 51 | span: None, 52 | error: e, 53 | } 54 | } 55 | } 56 | 57 | impl Error { 58 | /// Throw a custom error 59 | pub fn custom(s: impl Into) -> Self { 60 | Self::Custom { 61 | error: s.into(), 62 | span: None, 63 | } 64 | } 65 | 66 | /// Throw a custom error at a given location 67 | pub fn custom_at(s: impl Into, span: Span) -> Self { 68 | Self::Custom { 69 | error: s.into(), 70 | span: Some(span), 71 | } 72 | } 73 | 74 | /// Throw a custom error at a given token 75 | pub fn custom_at_token(s: impl Into, token: TokenTree) -> Self { 76 | Self::Custom { 77 | error: s.into(), 78 | span: Some(token.span()), 79 | } 80 | } 81 | 82 | /// Throw a custom error at a given `Option` 83 | pub fn custom_at_opt_token(s: impl Into, token: Option) -> Self { 84 | Self::Custom { 85 | error: s.into(), 86 | span: token.map(|t| t.span()), 87 | } 88 | } 89 | 90 | pub(crate) fn wrong_token(token: Option<&TokenTree>, expected: &str) -> Result { 91 | Err(Self::InvalidRustSyntax { 92 | span: token.map(|t| t.span()).unwrap_or_else(Span::call_site), 93 | expected: format!("{}, got {:?}", expected, token), 94 | }) 95 | } 96 | 97 | /// Return a new error that is located at the given span 98 | pub fn with_span(mut self, new_span: Span) -> Self { 99 | match &mut self { 100 | Error::UnknownDataType(span) => *span = new_span, 101 | Error::InvalidRustSyntax { span, .. } => *span = new_span, 102 | Error::ExpectedIdent(span) => *span = new_span, 103 | Error::PushParse { span, .. } => { 104 | *span = Some(new_span); 105 | } 106 | Error::Custom { span, .. } => *span = Some(new_span), 107 | } 108 | 109 | self 110 | } 111 | } 112 | 113 | // helper functions for the unit tests 114 | #[cfg(test)] 115 | impl Error { 116 | pub fn is_unknown_data_type(&self) -> bool { 117 | matches!(self, Error::UnknownDataType(_)) 118 | } 119 | 120 | pub fn is_invalid_rust_syntax(&self) -> bool { 121 | matches!(self, Error::InvalidRustSyntax { .. }) 122 | } 123 | } 124 | 125 | impl fmt::Display for Error { 126 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 127 | match self { 128 | Self::UnknownDataType(_) => { 129 | write!(fmt, "Unknown data type, only enum and struct are supported") 130 | } 131 | Self::InvalidRustSyntax { expected, .. } => { 132 | write!(fmt, "Invalid rust syntax, expected {}", expected) 133 | } 134 | Self::ExpectedIdent(_) => write!(fmt, "Expected ident"), 135 | Self::PushParse { error, .. } => write!( 136 | fmt, 137 | "Invalid code passed to `StreamBuilder::push_parsed`: {:?}", 138 | error 139 | ), 140 | Self::Custom { error, .. } => write!(fmt, "{}", error), 141 | } 142 | } 143 | } 144 | 145 | impl Error { 146 | /// Turn this error into a [`TokenStream`] so it shows up as a [`compile_error`] for the user. 147 | pub fn into_token_stream(self) -> TokenStream { 148 | let maybe_span = match &self { 149 | Self::UnknownDataType(span) 150 | | Self::ExpectedIdent(span) 151 | | Self::InvalidRustSyntax { span, .. } => Some(*span), 152 | Self::Custom { span, .. } | Self::PushParse { span, .. } => *span, 153 | }; 154 | self.throw_with_span(maybe_span.unwrap_or_else(Span::call_site)) 155 | } 156 | 157 | /// 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`. 158 | pub fn throw_with_span(self, span: Span) -> TokenStream { 159 | // compile_error!($message) 160 | let mut builder = StreamBuilder::new(); 161 | builder.ident_str("compile_error"); 162 | builder.punct('!'); 163 | builder 164 | .group(Delimiter::Brace, |b| { 165 | b.lit_str(self.to_string()); 166 | Ok(()) 167 | }) 168 | .unwrap(); 169 | builder.set_span_on_all_tokens(span); 170 | builder.stream 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/generate/stream_builder.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{ 2 | Delimiter, Group, Ident, LexError, Literal, Punct, Result, Spacing, Span, TokenStream, 3 | TokenTree, 4 | }; 5 | use std::str::FromStr; 6 | 7 | /// A helper struct build around a [TokenStream] to make it easier to build code. 8 | #[must_use] 9 | #[derive(Default)] 10 | pub struct StreamBuilder { 11 | pub(crate) stream: TokenStream, 12 | } 13 | 14 | impl StreamBuilder { 15 | /// Generate a new StreamBuilder 16 | pub fn new() -> Self { 17 | Self { 18 | stream: TokenStream::new(), 19 | } 20 | } 21 | 22 | /// Add multiple `TokenTree` items to the stream. 23 | pub fn extend(&mut self, item: impl IntoIterator) -> &mut Self { 24 | self.stream.extend(item); 25 | self 26 | } 27 | 28 | /// Append another StreamBuilder to the current StreamBuilder. 29 | pub fn append(&mut self, builder: StreamBuilder) -> &mut Self { 30 | self.stream.extend(builder.stream); 31 | self 32 | } 33 | 34 | /// Push a single token to the stream. 35 | pub fn push(&mut self, item: impl Into) -> &mut Self { 36 | self.stream.extend([item.into()]); 37 | self 38 | } 39 | 40 | /// Attempt to parse the given string as valid Rust code, and append the parsed result to the internal stream. 41 | /// 42 | /// Currently panics if the string could not be parsed as valid Rust code. 43 | pub fn push_parsed(&mut self, item: impl AsRef) -> Result<&mut Self> { 44 | let tokens = TokenStream::from_str(item.as_ref()).map_err(|e| PushParseError { 45 | error: e, 46 | code: item.as_ref().to_string(), 47 | })?; 48 | self.stream.extend(tokens); 49 | Ok(self) 50 | } 51 | 52 | /// 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. 53 | pub fn ident(&mut self, ident: Ident) -> &mut Self { 54 | self.stream.extend([TokenTree::Ident(ident)]); 55 | self 56 | } 57 | 58 | /// 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. 59 | pub fn ident_str(&mut self, ident: impl AsRef) -> &mut Self { 60 | self.stream.extend([TokenTree::Ident(Ident::new( 61 | ident.as_ref(), 62 | Span::call_site(), 63 | ))]); 64 | self 65 | } 66 | 67 | /// Add a group. A group is any block surrounded by `{ .. }`, `[ .. ]` or `( .. )`. 68 | /// 69 | /// `delim` indicates which group it is. The `inner` callback is used to fill the contents of the group. 70 | pub fn group(&mut self, delim: Delimiter, inner: FN) -> crate::Result<&mut Self> 71 | where 72 | FN: FnOnce(&mut StreamBuilder) -> crate::Result<()>, 73 | { 74 | let mut stream = StreamBuilder::new(); 75 | inner(&mut stream)?; 76 | self.stream 77 | .extend([TokenTree::Group(Group::new(delim, stream.stream))]); 78 | Ok(self) 79 | } 80 | 81 | /// Add a single punctuation to the stream. Puncts are single-character tokens like `.`, `<`, `#`, etc 82 | /// 83 | /// Note that this should not be used for multi-punct constructions like `::` or `->`. For that use [`puncts`] instead. 84 | /// 85 | /// [`puncts`]: #method.puncts 86 | pub fn punct(&mut self, p: char) -> &mut Self { 87 | self.stream 88 | .extend([TokenTree::Punct(Punct::new(p, Spacing::Alone))]); 89 | self 90 | } 91 | 92 | /// Add multiple punctuations to the stream. Multi punct tokens are e.g. `::`, `->` and `=>`. 93 | /// 94 | /// Note that this is the only way to add multi punct tokens. 95 | /// 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. 96 | pub fn puncts(&mut self, puncts: &str) -> &mut Self { 97 | self.stream.extend( 98 | puncts 99 | .chars() 100 | .map(|char| TokenTree::Punct(Punct::new(char, Spacing::Joint))), 101 | ); 102 | self 103 | } 104 | 105 | /// Add a lifetime to the stream. 106 | /// 107 | /// Note that this is the only way to add lifetimes, if you were to do: 108 | /// ```ignore 109 | /// builder.punct('\''); 110 | /// builder.ident_str("static"); 111 | /// ``` 112 | /// It would not add `'static`, but instead it would add `' static` as seperate tokens, and the lifetime would not work. 113 | pub fn lifetime(&mut self, lt: Ident) -> &mut Self { 114 | self.stream.extend([ 115 | TokenTree::Punct(Punct::new('\'', Spacing::Joint)), 116 | TokenTree::Ident(lt), 117 | ]); 118 | self 119 | } 120 | 121 | /// Add a lifetime to the stream. 122 | /// 123 | /// Note that this is the only way to add lifetimes, if you were to do: 124 | /// ```ignore 125 | /// builder.punct('\''); 126 | /// builder.ident_str("static"); 127 | /// ``` 128 | /// It would not add `'static`, but instead it would add `' static` as seperate tokens, and the lifetime would not work. 129 | pub fn lifetime_str(&mut self, lt: &str) -> &mut Self { 130 | self.stream.extend([ 131 | TokenTree::Punct(Punct::new('\'', Spacing::Joint)), 132 | TokenTree::Ident(Ident::new(lt, Span::call_site())), 133 | ]); 134 | self 135 | } 136 | 137 | /// Add a literal string (`&'static str`) to the stream. 138 | pub fn lit_str(&mut self, str: impl AsRef) -> &mut Self { 139 | self.stream 140 | .extend([TokenTree::Literal(Literal::string(str.as_ref()))]); 141 | self 142 | } 143 | 144 | /// Add an `usize` value to the stream. 145 | pub fn lit_usize(&mut self, val: usize) -> &mut Self { 146 | self.stream 147 | .extend([TokenTree::Literal(Literal::usize_unsuffixed(val))]); 148 | self 149 | } 150 | 151 | /// 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. 152 | /// 153 | /// Normally your derive will report an error on the derive, e.g.: 154 | /// 155 | /// ```text 156 | /// #[derive(YourMacro)] 157 | /// ^^^^^^^^^ 158 | /// | 159 | /// `self` value is a keyword only available in methods with a `self` parameter 160 | /// ``` 161 | /// 162 | /// 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. 163 | /// 164 | /// A `span` can be obtained from e.g. an ident with `ident.span()`. 165 | pub fn set_span_on_all_tokens(&mut self, span: Span) { 166 | self.stream = std::mem::take(&mut self.stream) 167 | .into_iter() 168 | .map(|mut token| { 169 | token.set_span(span); 170 | token 171 | }) 172 | .collect(); 173 | } 174 | } 175 | 176 | /// Failed to parse the code passed to [`StreamBuilder::push_parsed`] 177 | /// 178 | /// [`StreamBuilder::push_parsed`]: struct.StreamBuilder.html#method.push_parsed 179 | #[derive(Debug)] 180 | pub struct PushParseError { 181 | /// The parsing error 182 | pub error: LexError, 183 | /// The code that was being parsed 184 | pub code: String, 185 | } 186 | -------------------------------------------------------------------------------- /src/generate/impl.rs: -------------------------------------------------------------------------------- 1 | use super::{generate_item::FnParent, FnBuilder, GenConst, Generator, Parent, StreamBuilder}; 2 | use crate::{ 3 | parse::{GenericConstraints, Generics}, 4 | prelude::{Delimiter, Result}, 5 | }; 6 | 7 | #[must_use] 8 | /// A helper struct for implementing functions for a given struct or enum. 9 | pub struct Impl<'a, P: Parent> { 10 | parent: &'a mut P, 11 | outer_attr: Vec, 12 | inner_attr: Vec, 13 | name: String, 14 | // pub(super) group: StreamBuilder, 15 | consts: Vec, 16 | custom_generic_constraints: Option, 17 | fns: Vec<(StreamBuilder, StreamBuilder)>, 18 | } 19 | 20 | impl<'a, P: Parent> Impl<'a, P> { 21 | pub(super) fn with_parent_name(parent: &'a mut P) -> Self { 22 | Self { 23 | outer_attr: Vec::new(), 24 | inner_attr: Vec::new(), 25 | name: parent.name().to_string(), 26 | parent, 27 | consts: Vec::new(), 28 | custom_generic_constraints: None, 29 | fns: Vec::new(), 30 | } 31 | } 32 | 33 | pub(super) fn new(parent: &'a mut P, name: impl Into) -> Self { 34 | Self { 35 | outer_attr: Vec::new(), 36 | inner_attr: Vec::new(), 37 | parent, 38 | name: name.into(), 39 | consts: Vec::new(), 40 | custom_generic_constraints: None, 41 | fns: Vec::new(), 42 | } 43 | } 44 | 45 | /// Add a outer attribute to the trait implementation 46 | pub fn impl_outer_attr(&mut self, attr: impl AsRef) -> Result { 47 | let mut builder = StreamBuilder::new(); 48 | builder.punct('#').group(Delimiter::Bracket, |builder| { 49 | builder.push_parsed(attr)?; 50 | Ok(()) 51 | })?; 52 | self.outer_attr.push(builder); 53 | Ok(()) 54 | } 55 | 56 | /// Add a inner attribute to the trait implementation 57 | pub fn impl_inner_attr(&mut self, attr: impl AsRef) -> Result { 58 | let mut builder = StreamBuilder::new(); 59 | builder 60 | .punct('#') 61 | .punct('!') 62 | .group(Delimiter::Brace, |builder| { 63 | builder.push_parsed(attr)?; 64 | Ok(()) 65 | })?; 66 | self.inner_attr.push(builder); 67 | Ok(()) 68 | } 69 | 70 | /// Add a function to the trait implementation. 71 | /// 72 | /// `generator.impl().generate_fn("bar")` results in code like: 73 | /// 74 | /// ```ignore 75 | /// impl { 76 | /// fn bar() {} 77 | /// } 78 | /// ``` 79 | /// 80 | /// See [`FnBuilder`] for more options, as well as information on how to fill the function body. 81 | pub fn generate_fn(&mut self, name: impl Into) -> FnBuilder { 82 | FnBuilder::new(self, name) 83 | } 84 | 85 | /// Add a const to the trait implementation 86 | /// ``` 87 | /// # use virtue::prelude::Generator; 88 | /// # let mut generator = Generator::with_name("Bar"); 89 | /// generator.impl_for("Foo") 90 | /// .generate_const("BAR", "u8") 91 | /// .with_value(|b| { 92 | /// b.push_parsed("5")?; 93 | /// Ok(()) 94 | /// })?; 95 | /// # generator.assert_eq("impl Foo for Bar { const BAR : u8 = 5 ; }"); 96 | /// # Ok::<_, virtue::Error>(()) 97 | /// ``` 98 | /// 99 | /// Generates: 100 | /// ```ignore 101 | /// impl Foo for { 102 | /// const BAR: u8 = 5; 103 | /// } 104 | pub fn generate_const(&mut self, name: impl Into, ty: impl Into) -> GenConst { 105 | GenConst::new(&mut self.consts, name, ty) 106 | } 107 | } 108 | 109 | impl<'a> Impl<'a, Generator> { 110 | /// Modify the generic constraints of a type. 111 | /// This can be used to add additional type constraints to your implementation. 112 | /// 113 | /// ```ignore 114 | /// // Your derive: 115 | /// #[derive(YourTrait)] 116 | /// pub struct Foo { 117 | /// ... 118 | /// } 119 | /// 120 | /// // With this code: 121 | /// generator 122 | /// .r#impl() 123 | /// .modify_generic_constraints(|generics, constraints| { 124 | /// for g in generics.iter_generics() { 125 | /// constraints.push_generic(g, "YourTrait"); 126 | /// } 127 | /// }) 128 | /// 129 | /// // will generate: 130 | /// impl Foo 131 | /// where B: YourTrait // <- 132 | /// { 133 | /// } 134 | /// ``` 135 | /// 136 | /// Note that this function is only implemented when you call `.r#impl` on [`Generator`]. 137 | pub fn modify_generic_constraints(&mut self, cb: CB) -> &mut Self 138 | where 139 | CB: FnOnce(&Generics, &mut GenericConstraints), 140 | { 141 | if let Some(generics) = self.parent.generics() { 142 | let constraints = self.custom_generic_constraints.get_or_insert_with(|| { 143 | self.parent 144 | .generic_constraints() 145 | .cloned() 146 | .unwrap_or_default() 147 | }); 148 | cb(generics, constraints); 149 | } 150 | self 151 | } 152 | } 153 | 154 | impl<'a, P: Parent> FnParent for Impl<'a, P> { 155 | fn append(&mut self, fn_definition: StreamBuilder, fn_body: StreamBuilder) -> Result { 156 | self.fns.push((fn_definition, fn_body)); 157 | Ok(()) 158 | } 159 | } 160 | 161 | impl<'a, P: Parent> Drop for Impl<'a, P> { 162 | fn drop(&mut self) { 163 | if std::thread::panicking() { 164 | return; 165 | } 166 | let mut builder = StreamBuilder::new(); 167 | for attr in std::mem::take(&mut self.outer_attr) { 168 | builder.append(attr); 169 | } 170 | builder.ident_str("impl"); 171 | 172 | if let Some(generics) = self.parent.generics() { 173 | builder.append(generics.impl_generics()); 174 | } 175 | builder.push_parsed(&self.name).unwrap(); 176 | 177 | if let Some(generics) = self.parent.generics() { 178 | builder.append(generics.type_generics()); 179 | } 180 | if let Some(generic_constraints) = self.custom_generic_constraints.take() { 181 | builder.append(generic_constraints.where_clause()); 182 | } else if let Some(generic_constraints) = self.parent.generic_constraints() { 183 | builder.append(generic_constraints.where_clause()); 184 | } 185 | 186 | builder 187 | .group(Delimiter::Brace, |builder| { 188 | for attr in std::mem::take(&mut self.inner_attr) { 189 | builder.append(attr); 190 | } 191 | for r#const in std::mem::take(&mut self.consts) { 192 | builder.append(r#const); 193 | } 194 | for (fn_def, fn_body) in std::mem::take(&mut self.fns) { 195 | builder.append(fn_def); 196 | builder 197 | .group(Delimiter::Brace, |body| { 198 | *body = fn_body; 199 | Ok(()) 200 | }) 201 | .unwrap(); 202 | } 203 | Ok(()) 204 | }) 205 | .unwrap(); 206 | 207 | self.parent.append(builder); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/generate/impl_for.rs: -------------------------------------------------------------------------------- 1 | use super::{generate_item::FnParent, FnBuilder, GenConst, Parent, StreamBuilder, StringOrIdent}; 2 | use crate::{ 3 | parse::{GenericConstraints, Generics}, 4 | prelude::{Delimiter, Result}, 5 | }; 6 | 7 | #[must_use] 8 | /// A helper struct for implementing a trait for a given struct or enum. 9 | pub struct ImplFor<'a, P: Parent> { 10 | generator: &'a mut P, 11 | outer_attr: Vec, 12 | inner_attr: Vec, 13 | type_name: StringOrIdent, 14 | trait_name: Option, 15 | lifetimes: Option>, 16 | trait_generics: Option>, 17 | impl_generics: Vec, 18 | consts: Vec, 19 | custom_generic_constraints: Option, 20 | impl_types: Vec, 21 | fns: Vec<(StreamBuilder, StreamBuilder)>, 22 | } 23 | 24 | impl<'a, P: Parent> ImplFor<'a, P> { 25 | pub(super) fn new( 26 | generator: &'a mut P, 27 | type_name: StringOrIdent, 28 | trait_name: Option, 29 | ) -> Self { 30 | Self { 31 | generator, 32 | outer_attr: Vec::new(), 33 | inner_attr: Vec::new(), 34 | trait_name, 35 | type_name, 36 | lifetimes: None, 37 | trait_generics: None, 38 | impl_generics: vec![], 39 | consts: Vec::new(), 40 | custom_generic_constraints: None, 41 | impl_types: Vec::new(), 42 | fns: Vec::new(), 43 | } 44 | } 45 | 46 | /// Internal helper function to set lifetimes 47 | pub(crate) fn with_lifetimes(mut self, lifetimes: ITER) -> Self 48 | where 49 | ITER: IntoIterator, 50 | ITER::Item: Into, 51 | { 52 | self.lifetimes = Some(lifetimes.into_iter().map(Into::into).collect()); 53 | self 54 | } 55 | 56 | /// Make the new lifetimes added by `Generator::impl_for_with_lifetimes` depend on the existing lifetimes from the original derive. 57 | /// 58 | /// See [`impl_for_with_lifetimes`] for more information. 59 | /// 60 | /// Calling this method in any other context has no effect. 61 | /// 62 | /// [`impl_for_with_lifetimes`]: struct.Generator.html#method.impl_for_with_lifetimes 63 | pub fn new_lifetimes_depend_on_existing(mut self) -> Self { 64 | if let Some(new_lt) = &self.lifetimes { 65 | if let Some(generics) = self.generator.generics() { 66 | let constraints = self.custom_generic_constraints.get_or_insert_with(|| { 67 | self.generator 68 | .generic_constraints() 69 | .cloned() 70 | .unwrap_or_default() 71 | }); 72 | for old_lt in generics.iter_lifetimes() { 73 | for new_lt in new_lt { 74 | constraints 75 | .push_parsed_constraint(format!("'{}: '{}", new_lt, old_lt.ident)) 76 | .expect("Could not ensure new lifetimes depend on existing lifetimes"); 77 | } 78 | } 79 | } 80 | } 81 | self 82 | } 83 | 84 | /// Add generic parameters to the trait implementation. 85 | ///``` 86 | /// # use virtue::prelude::Generator; 87 | /// # let mut generator = Generator::with_name("Bar"); 88 | /// generator.impl_for("Foo") 89 | /// .with_trait_generics(["Baz"]); 90 | /// # generator.assert_eq("impl Foo < Baz > for Bar { }"); 91 | /// # Ok::<_, virtue::Error>(()) 92 | /// ``` 93 | /// 94 | /// Generates: 95 | /// ```ignore 96 | /// impl Foo for { 97 | /// const BAR: u8 = 5; 98 | /// } 99 | /// ``` 100 | pub fn with_trait_generics(mut self, generics: ITER) -> Self 101 | where 102 | ITER: IntoIterator, 103 | ITER::Item: Into, 104 | { 105 | self.trait_generics = Some(generics.into_iter().map(Into::into).collect()); 106 | self 107 | } 108 | 109 | /// Add generic parameters to the impl block. 110 | ///``` 111 | /// # use virtue::prelude::Generator; 112 | /// # let mut generator = Generator::with_name("Bar"); 113 | /// generator.impl_for("Foo") 114 | /// .with_impl_generics(["Baz"]); 115 | /// # generator.assert_eq("impl < Baz > Foo for Bar { }"); 116 | /// # Ok::<_, virtue::Error>(()) 117 | /// ``` 118 | /// 119 | /// Generates: 120 | /// ```ignore 121 | /// impl Foo for Bar { } 122 | /// ``` 123 | pub fn with_impl_generics(mut self, generics: ITER) -> Self 124 | where 125 | ITER: IntoIterator, 126 | ITER::Item: Into, 127 | { 128 | self.impl_generics = generics.into_iter().map(Into::into).collect(); 129 | self 130 | } 131 | 132 | /// Add a outer attribute to the trait implementation 133 | pub fn impl_outer_attr(&mut self, attr: impl AsRef) -> Result { 134 | let mut builder = StreamBuilder::new(); 135 | builder.punct('#').group(Delimiter::Bracket, |builder| { 136 | builder.push_parsed(attr)?; 137 | Ok(()) 138 | })?; 139 | self.outer_attr.push(builder); 140 | Ok(()) 141 | } 142 | 143 | /// Add a inner attribute to the trait implementation 144 | pub fn impl_inner_attr(&mut self, attr: impl AsRef) -> Result { 145 | let mut builder = StreamBuilder::new(); 146 | builder 147 | .punct('#') 148 | .punct('!') 149 | .group(Delimiter::Brace, |builder| { 150 | builder.push_parsed(attr)?; 151 | Ok(()) 152 | })?; 153 | self.inner_attr.push(builder); 154 | Ok(()) 155 | } 156 | 157 | /// Add a const to the trait implementation 158 | /// ``` 159 | /// # use virtue::prelude::Generator; 160 | /// # let mut generator = Generator::with_name("Bar"); 161 | /// generator.impl_for("Foo") 162 | /// .generate_const("BAR", "u8") 163 | /// .with_value(|b| { 164 | /// b.push_parsed("5")?; 165 | /// Ok(()) 166 | /// })?; 167 | /// # generator.assert_eq("impl Foo for Bar { const BAR : u8 = 5 ; }"); 168 | /// # Ok::<_, virtue::Error>(()) 169 | /// ``` 170 | /// 171 | /// Generates: 172 | /// ```ignore 173 | /// impl Foo for { 174 | /// const BAR: u8 = 5; 175 | /// } 176 | pub fn generate_const(&mut self, name: impl Into, ty: impl Into) -> GenConst { 177 | GenConst::new(&mut self.consts, name, ty) 178 | } 179 | 180 | /// Add a function to the trait implementation. 181 | /// 182 | /// `generator.impl_for("Foo").generate_fn("bar")` results in code like: 183 | /// 184 | /// ```ignore 185 | /// impl Foo for { 186 | /// fn bar() {} 187 | /// } 188 | /// ``` 189 | /// 190 | /// See [`FnBuilder`] for more options, as well as information on how to fill the function body. 191 | pub fn generate_fn(&mut self, name: impl Into) -> FnBuilder> { 192 | FnBuilder::new(self, name) 193 | } 194 | 195 | /// Add a type to the impl 196 | /// 197 | /// `generator.impl_for("Foo").impl_type("Bar", "u8")` results in code like: 198 | /// 199 | /// ```ignore 200 | /// impl Foo for { 201 | /// type Bar = u8; 202 | /// } 203 | /// ``` 204 | pub fn impl_type(&mut self, name: impl AsRef, value: impl AsRef) -> Result { 205 | let mut builder = StreamBuilder::new(); 206 | builder 207 | .ident_str("type") 208 | .push_parsed(name)? 209 | .punct('=') 210 | .push_parsed(value)? 211 | .punct(';'); 212 | self.impl_types.push(builder); 213 | Ok(()) 214 | } 215 | 216 | /// 217 | /// Modify the generic constraints of a type. 218 | /// This can be used to add additional type constraints to your implementation. 219 | /// 220 | /// ```ignore 221 | /// // Your derive: 222 | /// #[derive(YourTrait)] 223 | /// pub struct Foo { 224 | /// ... 225 | /// } 226 | /// 227 | /// // With this code: 228 | /// generator 229 | /// .impl_for("YourTrait") 230 | /// .modify_generic_constraints(|generics, constraints| { 231 | /// for g in generics.iter_generics() { 232 | /// constraints.push_generic(g, "YourTrait"); 233 | /// } 234 | /// }) 235 | /// 236 | /// // will generate: 237 | /// impl YourTrait for Foo 238 | /// where B: YourTrait // <- 239 | /// { 240 | /// } 241 | /// ``` 242 | /// 243 | pub fn modify_generic_constraints(&mut self, cb: CB) -> Result<&mut Self> 244 | where 245 | CB: FnOnce(&Generics, &mut GenericConstraints) -> Result, 246 | { 247 | if let Some(generics) = self.generator.generics() { 248 | let constraints = self.custom_generic_constraints.get_or_insert_with(|| { 249 | self.generator 250 | .generic_constraints() 251 | .cloned() 252 | .unwrap_or_default() 253 | }); 254 | cb(generics, constraints)?; 255 | } 256 | Ok(self) 257 | } 258 | } 259 | 260 | impl<'a, P: Parent> FnParent for ImplFor<'a, P> { 261 | fn append(&mut self, fn_definition: StreamBuilder, fn_body: StreamBuilder) -> Result { 262 | self.fns.push((fn_definition, fn_body)); 263 | Ok(()) 264 | } 265 | } 266 | 267 | impl Drop for ImplFor<'_, P> { 268 | fn drop(&mut self) { 269 | if std::thread::panicking() { 270 | return; 271 | } 272 | let mut builder = StreamBuilder::new(); 273 | for attr in std::mem::take(&mut self.outer_attr) { 274 | builder.append(attr); 275 | } 276 | 277 | self.generate_impl_definition(&mut builder); 278 | 279 | builder 280 | .group(Delimiter::Brace, |builder| { 281 | for attr in std::mem::take(&mut self.inner_attr) { 282 | builder.append(attr); 283 | } 284 | for ty in std::mem::take(&mut self.impl_types) { 285 | builder.append(ty); 286 | } 287 | for r#const in std::mem::take(&mut self.consts) { 288 | builder.append(r#const); 289 | } 290 | for (fn_def, fn_body) in std::mem::take(&mut self.fns) { 291 | builder.append(fn_def); 292 | builder 293 | .group(Delimiter::Brace, |body| { 294 | *body = fn_body; 295 | Ok(()) 296 | }) 297 | .unwrap(); 298 | } 299 | Ok(()) 300 | }) 301 | .unwrap(); 302 | 303 | self.generator.append(builder); 304 | } 305 | } 306 | 307 | impl ImplFor<'_, P> { 308 | fn generate_impl_definition(&mut self, builder: &mut StreamBuilder) { 309 | builder.ident_str("impl"); 310 | 311 | let impl_generics = self.impl_generics.as_slice(); 312 | if let Some(lifetimes) = &self.lifetimes { 313 | if let Some(generics) = self.generator.generics() { 314 | builder.append(generics.impl_generics_with_additional(lifetimes, impl_generics)); 315 | } else { 316 | append_lifetimes_and_generics(builder, lifetimes, impl_generics); 317 | } 318 | } else if let Some(generics) = self.generator.generics() { 319 | builder.append(generics.impl_generics_with_additional(&[], impl_generics)); 320 | } else if !impl_generics.is_empty() { 321 | append_lifetimes_and_generics(builder, &[], impl_generics) 322 | } 323 | if let Some(t) = &self.trait_name { 324 | builder.push_parsed(t.to_string()).unwrap(); 325 | 326 | let lifetimes = self.lifetimes.as_deref().unwrap_or_default(); 327 | let generics = self.trait_generics.as_deref().unwrap_or_default(); 328 | append_lifetimes_and_generics(builder, lifetimes, generics); 329 | builder.ident_str("for"); 330 | } 331 | builder.push_parsed(self.type_name.to_string()).unwrap(); 332 | if let Some(generics) = &self.generator.generics() { 333 | builder.append(generics.type_generics()); 334 | } 335 | if let Some(generic_constraints) = self.custom_generic_constraints.take() { 336 | builder.append(generic_constraints.where_clause()); 337 | } else if let Some(generic_constraints) = &self.generator.generic_constraints() { 338 | builder.append(generic_constraints.where_clause()); 339 | } 340 | } 341 | } 342 | 343 | fn append_lifetimes_and_generics( 344 | builder: &mut StreamBuilder, 345 | lifetimes: &[String], 346 | generics: &[String], 347 | ) { 348 | if lifetimes.is_empty() && generics.is_empty() { 349 | return; 350 | } 351 | 352 | builder.punct('<'); 353 | 354 | for (idx, lt) in lifetimes.iter().enumerate() { 355 | if idx > 0 { 356 | builder.punct(','); 357 | } 358 | builder.lifetime_str(lt); 359 | } 360 | 361 | for (idx, gen) in generics.iter().enumerate() { 362 | if idx > 0 || !lifetimes.is_empty() { 363 | builder.punct(','); 364 | } 365 | builder.push_parsed(gen).unwrap(); 366 | } 367 | 368 | builder.punct('>'); 369 | } 370 | -------------------------------------------------------------------------------- /src/generate/mod.rs: -------------------------------------------------------------------------------- 1 | //! Code to help generate functions. 2 | //! 3 | //! The structure is: 4 | //! 5 | //! - [`Generator`] 6 | //! - `.impl_for()`: [`ImplFor`] 7 | //! - `.generate_fn()`: [`FnBuilder`] 8 | //! - `.body(|builder| { .. })`: [`StreamBuilder`] 9 | //! 10 | //! Afterwards, [`Generator::finish()`] **must** be called to take out the [`TokenStream`] produced. 11 | //! 12 | //! [`Generator::finish()`]: struct.Generator.html#method.finish 13 | //! [`TokenStream`]: ../prelude/struct.TokenStream.html 14 | 15 | mod gen_enum; 16 | mod gen_struct; 17 | mod generate_item; 18 | mod generate_mod; 19 | mod generator; 20 | mod r#impl; 21 | mod impl_for; 22 | mod stream_builder; 23 | 24 | use crate::parse::Visibility; 25 | use crate::{ 26 | parse::{GenericConstraints, Generics}, 27 | prelude::{Delimiter, Ident, TokenStream}, 28 | }; 29 | use std::fmt; 30 | use std::marker::PhantomData; 31 | 32 | pub use self::gen_enum::GenEnum; 33 | pub use self::gen_struct::GenStruct; 34 | pub use self::generate_item::{FnBuilder, FnSelfArg, GenConst}; 35 | pub use self::generate_mod::GenerateMod; 36 | pub use self::generator::Generator; 37 | pub use self::impl_for::ImplFor; 38 | pub use self::r#impl::Impl; 39 | pub use self::stream_builder::{PushParseError, StreamBuilder}; 40 | 41 | /// Helper trait to make it possible to nest several builders. Internal use only. 42 | #[allow(missing_docs)] 43 | pub trait Parent { 44 | fn append(&mut self, builder: StreamBuilder); 45 | fn name(&self) -> &Ident; 46 | fn generics(&self) -> Option<&Generics>; 47 | fn generic_constraints(&self) -> Option<&GenericConstraints>; 48 | } 49 | 50 | /// Helper enum to differentiate between a [`Ident`] or a [`String`]. 51 | #[allow(missing_docs)] 52 | pub enum StringOrIdent { 53 | String(String), 54 | // Note that when this is a `string` this could be much more than a single ident. 55 | // Therefor you should never use [`StreamBuilder`]`.ident_str(StringOrIdent.to_string())`, but instead use `.push_parsed(StringOrIdent.to_string())?`. 56 | Ident(Ident), 57 | } 58 | 59 | impl fmt::Display for StringOrIdent { 60 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 61 | match self { 62 | Self::String(s) => s.fmt(f), 63 | Self::Ident(i) => i.fmt(f), 64 | } 65 | } 66 | } 67 | 68 | impl From for StringOrIdent { 69 | fn from(s: String) -> Self { 70 | Self::String(s) 71 | } 72 | } 73 | impl From for StringOrIdent { 74 | fn from(i: Ident) -> Self { 75 | Self::Ident(i) 76 | } 77 | } 78 | impl<'a> From<&'a str> for StringOrIdent { 79 | fn from(s: &'a str) -> Self { 80 | Self::String(s.to_owned()) 81 | } 82 | } 83 | 84 | /// A path of identifiers, like `mod::Type`. 85 | pub struct Path(Vec); 86 | 87 | impl From for Path { 88 | fn from(s: String) -> Self { 89 | StringOrIdent::from(s).into() 90 | } 91 | } 92 | 93 | impl From for Path { 94 | fn from(i: Ident) -> Self { 95 | StringOrIdent::from(i).into() 96 | } 97 | } 98 | 99 | impl From<&str> for Path { 100 | fn from(s: &str) -> Self { 101 | StringOrIdent::from(s).into() 102 | } 103 | } 104 | 105 | impl From for Path { 106 | fn from(value: StringOrIdent) -> Self { 107 | Self(vec![value]) 108 | } 109 | } 110 | 111 | impl FromIterator for Path { 112 | fn from_iter>(iter: T) -> Self { 113 | iter.into_iter().map(StringOrIdent::from).collect() 114 | } 115 | } 116 | 117 | impl FromIterator for Path { 118 | fn from_iter>(iter: T) -> Self { 119 | iter.into_iter().map(StringOrIdent::from).collect() 120 | } 121 | } 122 | 123 | impl<'a> FromIterator<&'a str> for Path { 124 | fn from_iter>(iter: T) -> Self { 125 | iter.into_iter().map(StringOrIdent::from).collect() 126 | } 127 | } 128 | 129 | impl FromIterator for Path { 130 | fn from_iter>(iter: T) -> Self { 131 | Self(iter.into_iter().collect()) 132 | } 133 | } 134 | 135 | impl IntoIterator for Path { 136 | type Item = StringOrIdent; 137 | type IntoIter = std::vec::IntoIter; 138 | 139 | fn into_iter(self) -> Self::IntoIter { 140 | self.0.into_iter() 141 | } 142 | } 143 | 144 | /// A struct or enum variant field. 145 | struct Field { 146 | name: String, 147 | vis: Visibility, 148 | ty: String, 149 | attributes: Vec, 150 | } 151 | 152 | impl Field { 153 | fn new(name: impl Into, vis: Visibility, ty: impl Into) -> Self { 154 | Self { 155 | name: name.into(), 156 | vis, 157 | ty: ty.into(), 158 | attributes: Vec::new(), 159 | } 160 | } 161 | } 162 | 163 | /// A builder for struct or enum variant fields. 164 | pub struct FieldBuilder<'a, P> { 165 | fields: &'a mut Vec, 166 | _parent: PhantomData

, // Keep this to disallow `pub` on enum fields 167 | } 168 | 169 | impl

FieldBuilder<'_, P> { 170 | /// Add an attribute to the field. 171 | /// 172 | /// ``` 173 | /// # use virtue::prelude::Generator; 174 | /// # let mut generator = Generator::with_name("Fooz"); 175 | /// generator 176 | /// .generate_struct("Foo") 177 | /// .add_field("foo", "u16") 178 | /// .make_pub() 179 | /// .with_attribute("serde", |b| { 180 | /// b.push_parsed("(default)")?; 181 | /// Ok(()) 182 | /// })?; 183 | /// generator 184 | /// .generate_enum("Bar") 185 | /// .add_value("Baz") 186 | /// .add_field("baz", "bool") 187 | /// .with_attribute("serde", |b| { 188 | /// b.push_parsed("(default)")?; 189 | /// Ok(()) 190 | /// })?; 191 | /// # generator.assert_eq("struct Foo { # [serde (default)] pub foo : u16 , } \ 192 | /// enum Bar { Baz { # [serde (default)] baz : bool , } , }"); 193 | /// # Ok::<_, virtue::Error>(()) 194 | /// ``` 195 | /// 196 | /// Generates: 197 | /// ```ignore 198 | /// struct Foo { 199 | /// #[serde(default)] 200 | /// pub bar: u16 201 | /// } 202 | /// 203 | /// enum Bar { 204 | /// Baz { 205 | /// #[serde(default)] 206 | /// baz: bool 207 | /// } 208 | /// } 209 | /// ``` 210 | pub fn with_attribute( 211 | &mut self, 212 | name: impl AsRef, 213 | value: impl FnOnce(&mut StreamBuilder) -> crate::Result, 214 | ) -> crate::Result<&mut Self> { 215 | self.current().with_attribute(name, value)?; 216 | Ok(self) 217 | } 218 | 219 | /// Add a parsed attribute to the field. 220 | /// 221 | /// ``` 222 | /// # use virtue::prelude::Generator; 223 | /// # let mut generator = Generator::with_name("Fooz"); 224 | /// generator 225 | /// .generate_struct("Foo") 226 | /// .add_field("foo", "u16") 227 | /// .make_pub() 228 | /// .with_parsed_attribute("serde(default)")?; 229 | /// generator 230 | /// .generate_enum("Bar") 231 | /// .add_value("Baz") 232 | /// .add_field("baz", "bool") 233 | /// .with_parsed_attribute("serde(default)")?; 234 | /// # generator.assert_eq("struct Foo { # [serde (default)] pub foo : u16 , } \ 235 | /// enum Bar { Baz { # [serde (default)] baz : bool , } , }"); 236 | /// # Ok::<_, virtue::Error>(()) 237 | /// ``` 238 | /// 239 | /// Generates: 240 | /// ```ignore 241 | /// struct Foo { 242 | /// #[serde(default)] 243 | /// pub bar: u16 244 | /// } 245 | /// 246 | /// enum Bar { 247 | /// Baz { 248 | /// #[serde(default)] 249 | /// baz: bool 250 | /// } 251 | /// } 252 | /// ``` 253 | pub fn with_parsed_attribute( 254 | &mut self, 255 | attribute: impl AsRef, 256 | ) -> crate::Result<&mut Self> { 257 | self.current().with_parsed_attribute(attribute)?; 258 | Ok(self) 259 | } 260 | 261 | /// Add a token stream as an attribute to the field. 262 | /// 263 | /// ``` 264 | /// # use virtue::prelude::{Generator, TokenStream}; 265 | /// # let mut generator = Generator::with_name("Fooz"); 266 | /// let attribute = "serde(default)".parse::().unwrap(); 267 | /// generator 268 | /// .generate_struct("Foo") 269 | /// .add_field("foo", "u16") 270 | /// .make_pub() 271 | /// .with_attribute_stream(attribute); 272 | /// # generator.assert_eq("struct Foo { # [serde (default)] pub foo : u16 , }"); 273 | /// # Ok::<_, virtue::Error>(()) 274 | /// ``` 275 | /// 276 | /// Generates: 277 | /// ```ignore 278 | /// struct Foo { 279 | /// #[serde(default)] 280 | /// pub bar: u16 281 | /// } 282 | /// ``` 283 | pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { 284 | self.current().with_attribute_stream(attribute); 285 | self 286 | } 287 | 288 | /// Add a field to the parent type. 289 | /// 290 | /// ``` 291 | /// # use virtue::prelude::Generator; 292 | /// # let mut generator = Generator::with_name("Fooz"); 293 | /// generator 294 | /// .generate_struct("Foo") 295 | /// .add_field("foo", "u16") 296 | /// .add_field("bar", "bool"); 297 | /// # generator.assert_eq("struct Foo { foo : u16 , bar : bool , }"); 298 | /// # Ok::<_, virtue::Error>(()) 299 | /// ``` 300 | /// 301 | /// Generates: 302 | /// ``` 303 | /// struct Foo { 304 | /// foo: u16, 305 | /// bar: bool 306 | /// } 307 | /// ``` 308 | pub fn add_field(&mut self, name: impl Into, ty: impl Into) -> &mut Self { 309 | self.fields.push(Field::new(name, Visibility::Default, ty)); 310 | self 311 | } 312 | } 313 | 314 | // Only allow `pub` on struct fields 315 | impl<'a, P: Parent> FieldBuilder<'_, GenStruct<'a, P>> { 316 | /// Make the field public. 317 | pub fn make_pub(&mut self) -> &mut Self { 318 | self.current().vis = Visibility::Pub; 319 | self 320 | } 321 | } 322 | 323 | impl<'a, P> From<&'a mut Vec> for FieldBuilder<'a, P> { 324 | fn from(fields: &'a mut Vec) -> Self { 325 | Self { 326 | fields, 327 | _parent: PhantomData, 328 | } 329 | } 330 | } 331 | 332 | impl

FieldBuilder<'_, P> { 333 | fn current(&mut self) -> &mut Field { 334 | // A field is always added before this is called, so the unwrap doesn't fail. 335 | self.fields.last_mut().unwrap() 336 | } 337 | } 338 | 339 | /// A helper trait to share attribute code between struct and enum generators. 340 | trait AttributeContainer { 341 | fn derives(&mut self) -> &mut Vec; 342 | fn attributes(&mut self) -> &mut Vec; 343 | 344 | fn with_derive(&mut self, derive: impl Into) -> &mut Self { 345 | self.derives().push(derive.into()); 346 | self 347 | } 348 | 349 | fn with_derives>(&mut self, derives: impl IntoIterator) -> &mut Self { 350 | self.derives().extend(derives.into_iter().map(Into::into)); 351 | self 352 | } 353 | 354 | fn with_attribute( 355 | &mut self, 356 | name: impl AsRef, 357 | value: impl FnOnce(&mut StreamBuilder) -> crate::Result, 358 | ) -> crate::Result<&mut Self> { 359 | let mut stream = StreamBuilder::new(); 360 | value(stream.ident_str(name))?; 361 | self.attributes().push(stream); 362 | Ok(self) 363 | } 364 | 365 | fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> crate::Result<&mut Self> { 366 | let mut stream = StreamBuilder::new(); 367 | stream.push_parsed(attribute)?; 368 | self.attributes().push(stream); 369 | Ok(self) 370 | } 371 | 372 | fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { 373 | let stream = StreamBuilder { 374 | stream: attribute.into(), 375 | }; 376 | self.attributes().push(stream); 377 | self 378 | } 379 | 380 | fn build_derives(&mut self, b: &mut StreamBuilder) -> &mut Self { 381 | let derives = std::mem::take(self.derives()); 382 | if !derives.is_empty() { 383 | build_attribute(b, |b| { 384 | b.ident_str("derive").group(Delimiter::Parenthesis, |b| { 385 | for (idx, derive) in derives.into_iter().enumerate() { 386 | if idx > 0 { 387 | b.punct(','); 388 | } 389 | for (idx, component) in derive.into_iter().enumerate() { 390 | if idx > 0 { 391 | b.puncts("::"); 392 | } 393 | 394 | match component { 395 | StringOrIdent::String(s) => b.ident_str(s), 396 | StringOrIdent::Ident(i) => b.ident(i), 397 | }; 398 | } 399 | } 400 | Ok(()) 401 | }) 402 | }) 403 | .expect("could not build derives"); 404 | } 405 | self 406 | } 407 | 408 | fn build_attributes(&mut self, b: &mut StreamBuilder) -> &mut Self { 409 | for attr in std::mem::take(self.attributes()) { 410 | build_attribute(b, |b| Ok(b.extend(attr.stream))).expect("could not build attribute"); 411 | } 412 | self 413 | } 414 | } 415 | 416 | impl AttributeContainer for Field { 417 | fn derives(&mut self) -> &mut Vec { 418 | unreachable!("fields cannot have derives") 419 | } 420 | 421 | fn attributes(&mut self) -> &mut Vec { 422 | &mut self.attributes 423 | } 424 | } 425 | 426 | fn build_attribute(b: &mut StreamBuilder, build: T) -> crate::Result 427 | where 428 | T: FnOnce(&mut StreamBuilder) -> crate::Result<&mut StreamBuilder>, 429 | { 430 | b.punct('#').group(Delimiter::Bracket, |b| { 431 | build(b)?; 432 | Ok(()) 433 | })?; 434 | 435 | Ok(()) 436 | } 437 | -------------------------------------------------------------------------------- /src/generate/generator.rs: -------------------------------------------------------------------------------- 1 | use super::{GenEnum, GenStruct, GenerateMod, Impl, ImplFor, StreamBuilder, StringOrIdent}; 2 | use crate::parse::{GenericConstraints, Generics}; 3 | use crate::prelude::{Ident, TokenStream}; 4 | 5 | #[must_use] 6 | /// The generator is used to generate code. 7 | /// 8 | /// Often you will want to use [`impl_for`] to generate an `impl for `. 9 | /// 10 | /// [`impl_for`]: #method.impl_for 11 | pub struct Generator { 12 | name: Ident, 13 | generics: Option, 14 | generic_constraints: Option, 15 | stream: StreamBuilder, 16 | } 17 | 18 | impl Generator { 19 | pub(crate) fn new( 20 | name: Ident, 21 | generics: Option, 22 | generic_constraints: Option, 23 | ) -> Self { 24 | Self { 25 | name, 26 | generics, 27 | generic_constraints, 28 | stream: StreamBuilder::new(), 29 | } 30 | } 31 | 32 | /// Return the name for the struct or enum that this is going to be implemented on. 33 | pub fn target_name(&self) -> Ident { 34 | self.name.clone() 35 | } 36 | 37 | /// Generate an `impl ` implementation. See [`Impl`] for more information. 38 | /// 39 | /// 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` 40 | pub fn r#impl(&mut self) -> Impl { 41 | Impl::with_parent_name(self) 42 | } 43 | 44 | /// Generate an `impl ` implementation. See [`Impl`] for more information. 45 | /// 46 | /// Alias for [`impl`] which doesn't need a `r#` prefix. 47 | /// 48 | /// [`impl`]: #method.impl 49 | pub fn generate_impl(&mut self) -> Impl { 50 | Impl::with_parent_name(self) 51 | } 52 | 53 | /// Generate an `for for ` implementation. See [ImplFor] for more information. 54 | /// 55 | /// 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` 56 | pub fn impl_for(&mut self, trait_name: impl Into) -> ImplFor { 57 | ImplFor::new( 58 | self, 59 | self.name.clone().into(), 60 | Some(trait_name.into().into()), 61 | ) 62 | } 63 | 64 | /// Generate an `impl ` block. See [ImplFor] for more information. 65 | /// ``` 66 | /// # use virtue::prelude::*; 67 | /// # let mut generator = Generator::with_name("Baz"); 68 | /// generator.impl_for_other_type("Foo"); 69 | /// 70 | /// // will output: 71 | /// // impl Foo { } 72 | /// # generator.assert_eq("impl Foo { }"); 73 | /// ``` 74 | pub fn impl_for_other_type(&mut self, type_name: impl Into) -> ImplFor { 75 | ImplFor::new(self, type_name.into(), None) 76 | } 77 | 78 | /// Generate an `impl for ` block. See [ImplFor] for more information. 79 | /// ``` 80 | /// # use virtue::prelude::*; 81 | /// # let mut generator = Generator::with_name("Baz"); 82 | /// generator.impl_trait_for_other_type("Foo", "Bar"); 83 | /// 84 | /// // will output: 85 | /// // impl Foo for Bar { } 86 | /// # generator.assert_eq("impl Foo for Bar { }"); 87 | /// ``` 88 | pub fn impl_trait_for_other_type( 89 | &mut self, 90 | trait_name: impl Into, 91 | type_name: impl Into, 92 | ) -> ImplFor { 93 | ImplFor::new(self, type_name.into(), Some(trait_name.into())) 94 | } 95 | 96 | /// Generate an `for <..lifetimes> for ` implementation. See [ImplFor] for more information. 97 | /// 98 | /// Note: 99 | /// - Lifetimes should _not_ have the leading apostrophe. 100 | /// - `trait_name` should _not_ have custom lifetimes. These will be added automatically. 101 | /// 102 | /// ``` 103 | /// # use virtue::prelude::*; 104 | /// # let mut generator = Generator::with_name("Bar"); 105 | /// generator.impl_for_with_lifetimes("Foo", ["a", "b"]); 106 | /// 107 | /// // will output: 108 | /// // impl<'a, 'b> Foo<'a, 'b> for StructOrEnum { } 109 | /// # generator.assert_eq("impl < 'a , 'b > Foo < 'a , 'b > for Bar { }"); 110 | /// ``` 111 | /// 112 | /// 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()` 113 | /// 114 | /// ``` 115 | /// # use virtue::prelude::*; 116 | /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); 117 | /// // given a derive on `struct<'a> Bar<'a>` 118 | /// generator.impl_for_with_lifetimes("Foo", ["b"]).new_lifetimes_depend_on_existing(); 119 | /// 120 | /// // will output: 121 | /// // impl<'a, 'b> Foo<'b> for Bar<'a> where 'b: 'a { } 122 | /// # generator.assert_eq("impl < 'b , 'a > Foo < 'b > for Bar < 'a > where 'b : 'a { }"); 123 | /// ``` 124 | pub fn impl_for_with_lifetimes( 125 | &mut self, 126 | trait_name: T, 127 | lifetimes: ITER, 128 | ) -> ImplFor 129 | where 130 | ITER: IntoIterator, 131 | ITER::Item: Into, 132 | T: Into, 133 | { 134 | ImplFor::new(self, self.name.clone().into(), Some(trait_name.into())) 135 | .with_lifetimes(lifetimes) 136 | } 137 | 138 | /// Generate a struct with the given name. See [`GenStruct`] for more info. 139 | pub fn generate_struct(&mut self, name: impl Into) -> GenStruct { 140 | GenStruct::new(self, name) 141 | } 142 | 143 | /// Generate an enum with the given name. See [`GenEnum`] for more info. 144 | pub fn generate_enum(&mut self, name: impl Into) -> GenEnum { 145 | GenEnum::new(self, name) 146 | } 147 | 148 | /// Generate a `mod { ... }`. See [`GenerateMod`] for more info. 149 | pub fn generate_mod(&mut self, mod_name: impl Into) -> GenerateMod { 150 | GenerateMod::new(self, mod_name) 151 | } 152 | 153 | /// Export the current stream to a file, making it very easy to debug the output of a derive macro. 154 | /// This will try to find rust's `target` directory, and write `target/generated//_.rs`. 155 | /// 156 | /// Will return `true` if the file is written, `false` otherwise. 157 | /// 158 | /// The outputted file is unformatted. Use `cargo fmt -- target/generated//.rs` to format the file. 159 | pub fn export_to_file(&self, crate_name: &str, file_postfix: &str) -> bool { 160 | use std::io::Write; 161 | 162 | if let Ok(var) = std::env::var("CARGO_MANIFEST_DIR") { 163 | let mut path = std::path::PathBuf::from(var); 164 | loop { 165 | { 166 | let mut path = path.clone(); 167 | path.push("target"); 168 | if path.exists() { 169 | path.push("generated"); 170 | path.push(crate_name); 171 | if std::fs::create_dir_all(&path).is_err() { 172 | return false; 173 | } 174 | path.push(format!("{}_{}.rs", self.target_name(), file_postfix)); 175 | if let Ok(mut file) = std::fs::File::create(path) { 176 | let _ = file.write_all(self.stream.stream.to_string().as_bytes()); 177 | return true; 178 | } 179 | } 180 | } 181 | if let Some(parent) = path.parent() { 182 | path = parent.into(); 183 | } else { 184 | break; 185 | } 186 | } 187 | } 188 | false 189 | } 190 | 191 | /// Consume the contents of this generator. This *must* be called, or else the generator will panic on drop. 192 | pub fn finish(mut self) -> crate::prelude::Result { 193 | Ok(std::mem::take(&mut self.stream).stream) 194 | } 195 | } 196 | 197 | #[cfg(feature = "proc-macro2")] 198 | impl Generator { 199 | /// Create a new generator with the name `name`. This is useful for testing purposes in combination with the `assert_eq` function. 200 | pub fn with_name(name: &str) -> Self { 201 | Self::new( 202 | Ident::new(name, crate::prelude::Span::call_site()), 203 | None, 204 | None, 205 | ) 206 | } 207 | /// Add a lifetime to this generator. 208 | pub fn with_lifetime(mut self, lt: &str) -> Self { 209 | self.generics 210 | .get_or_insert_with(|| Generics(Vec::new())) 211 | .push(crate::parse::Generic::Lifetime(crate::parse::Lifetime { 212 | ident: crate::prelude::Ident::new(lt, crate::prelude::Span::call_site()), 213 | constraint: Vec::new(), 214 | })); 215 | self 216 | } 217 | /// 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. 218 | pub fn assert_eq(&self, expected: &str) { 219 | assert_eq!(expected, self.stream.stream.to_string()); 220 | } 221 | } 222 | 223 | impl Drop for Generator { 224 | fn drop(&mut self) { 225 | if !self.stream.stream.is_empty() && !std::thread::panicking() { 226 | eprintln!("WARNING: Generator dropped but the stream is not empty. Please call `.finish()` on the generator"); 227 | } 228 | } 229 | } 230 | 231 | impl super::Parent for Generator { 232 | fn append(&mut self, builder: StreamBuilder) { 233 | self.stream.append(builder); 234 | } 235 | 236 | fn name(&self) -> &Ident { 237 | &self.name 238 | } 239 | 240 | fn generics(&self) -> Option<&Generics> { 241 | self.generics.as_ref() 242 | } 243 | 244 | fn generic_constraints(&self) -> Option<&GenericConstraints> { 245 | self.generic_constraints.as_ref() 246 | } 247 | } 248 | 249 | #[cfg(test)] 250 | mod test { 251 | use proc_macro2::Span; 252 | 253 | use crate::token_stream; 254 | 255 | use super::*; 256 | 257 | #[test] 258 | fn impl_for_with_lifetimes() { 259 | // No generics 260 | let mut generator = 261 | Generator::new(Ident::new("StructOrEnum", Span::call_site()), None, None); 262 | let _ = generator.impl_for_with_lifetimes("Foo", ["a", "b"]); 263 | let output = generator.finish().unwrap(); 264 | assert_eq!( 265 | output 266 | .into_iter() 267 | .map(|v| v.to_string()) 268 | .collect::(), 269 | token_stream("impl<'a, 'b> Foo<'a, 'b> for StructOrEnum { }") 270 | .map(|v| v.to_string()) 271 | .collect::(), 272 | ); 273 | 274 | //with simple generics 275 | let mut generator = Generator::new( 276 | Ident::new("StructOrEnum", Span::call_site()), 277 | Generics::try_take(&mut token_stream("")).unwrap(), 278 | None, 279 | ); 280 | let _ = generator.impl_for_with_lifetimes("Foo", ["a", "b"]); 281 | let output = generator.finish().unwrap(); 282 | assert_eq!( 283 | output 284 | .into_iter() 285 | .map(|v| v.to_string()) 286 | .collect::(), 287 | token_stream("impl<'a, 'b, T1, T2> Foo<'a, 'b> for StructOrEnum { }") 288 | .map(|v| v.to_string()) 289 | .collect::() 290 | ); 291 | 292 | // with lifetimes 293 | let mut generator = Generator::new( 294 | Ident::new("StructOrEnum", Span::call_site()), 295 | Generics::try_take(&mut token_stream("<'alpha, 'beta>")).unwrap(), 296 | None, 297 | ); 298 | let _ = generator.impl_for_with_lifetimes("Foo", ["a", "b"]); 299 | let output = generator.finish().unwrap(); 300 | assert_eq!( 301 | output 302 | .into_iter() 303 | .map(|v| v.to_string()) 304 | .collect::(), 305 | token_stream( 306 | "impl<'a, 'b, 'alpha, 'beta> Foo<'a, 'b> for StructOrEnum<'alpha, 'beta> { }" 307 | ) 308 | .map(|v| v.to_string()) 309 | .collect::() 310 | ); 311 | } 312 | 313 | #[test] 314 | fn impl_for_with_trait_generics() { 315 | let mut generator = Generator::new( 316 | Ident::new("StructOrEnum", Span::call_site()), 317 | Generics::try_take(&mut token_stream("<'a>")).unwrap(), 318 | None, 319 | ); 320 | let _ = generator.impl_for("Foo").with_trait_generics(["&'a str"]); 321 | let output = generator.finish().unwrap(); 322 | assert_eq!( 323 | output 324 | .into_iter() 325 | .map(|v| v.to_string()) 326 | .collect::(), 327 | token_stream("impl<'a> Foo<&'a str> for StructOrEnum<'a> { }") 328 | .map(|v| v.to_string()) 329 | .collect::(), 330 | ); 331 | } 332 | 333 | #[test] 334 | fn impl_for_with_impl_generics() { 335 | //with simple generics 336 | let mut generator = Generator::new( 337 | Ident::new("StructOrEnum", Span::call_site()), 338 | Generics::try_take(&mut token_stream("")).unwrap(), 339 | None, 340 | ); 341 | let _ = generator.impl_for("Foo").with_impl_generics(["Bar"]); 342 | 343 | let output = generator.finish().unwrap(); 344 | assert_eq!( 345 | output 346 | .into_iter() 347 | .map(|v| v.to_string()) 348 | .collect::(), 349 | token_stream("impl Foo for StructOrEnum { }") 350 | .map(|v| v.to_string()) 351 | .collect::() 352 | ); 353 | // with lifetimes 354 | let mut generator = Generator::new( 355 | Ident::new("StructOrEnum", Span::call_site()), 356 | Generics::try_take(&mut token_stream("<'alpha, 'beta>")).unwrap(), 357 | None, 358 | ); 359 | let _ = generator.impl_for("Foo").with_impl_generics(["Bar"]); 360 | let output = generator.finish().unwrap(); 361 | assert_eq!( 362 | output 363 | .into_iter() 364 | .map(|v| v.to_string()) 365 | .collect::(), 366 | token_stream("impl<'alpha, 'beta, Bar> Foo for StructOrEnum<'alpha, 'beta> { }") 367 | .map(|v| v.to_string()) 368 | .collect::() 369 | ); 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /src/generate/gen_struct.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, Path, StreamBuilder, 3 | StringOrIdent, 4 | }; 5 | use crate::parse::{Generic, Generics, Visibility}; 6 | use crate::prelude::{Delimiter, Ident, Span, TokenStream}; 7 | use crate::Result; 8 | 9 | /// Builder to generate a struct. 10 | /// Defaults to a struct with named fields `struct { : , ... }` 11 | pub struct GenStruct<'a, P: Parent> { 12 | parent: &'a mut P, 13 | name: Ident, 14 | visibility: Visibility, 15 | generics: Option, 16 | fields: Vec, 17 | derives: Vec, 18 | attributes: Vec, 19 | additional: Vec, 20 | struct_type: StructType, 21 | } 22 | 23 | impl<'a, P: Parent> GenStruct<'a, P> { 24 | pub(crate) fn new(parent: &'a mut P, name: impl Into) -> Self { 25 | Self { 26 | parent, 27 | name: Ident::new(name.into().as_str(), Span::call_site()), 28 | visibility: Visibility::Default, 29 | generics: None, 30 | fields: Vec::new(), 31 | derives: Vec::new(), 32 | attributes: Vec::new(), 33 | additional: Vec::new(), 34 | struct_type: StructType::Named, 35 | } 36 | } 37 | 38 | /// Make the struct a zero-sized type (no fields) 39 | /// 40 | /// Any fields will be ignored 41 | /// 42 | /// ``` 43 | /// # use virtue::prelude::Generator; 44 | /// # let mut generator = Generator::with_name("Fooz"); 45 | /// generator 46 | /// .generate_struct("Foo") 47 | /// .make_zst() 48 | /// .add_field("bar", "u16") 49 | /// .add_field("baz", "String"); 50 | /// # generator.assert_eq("struct Foo ;"); 51 | /// # Ok::<_, virtue::Error>(()) 52 | /// ``` 53 | /// 54 | /// Generates: 55 | /// ``` 56 | /// struct Foo; 57 | /// ``` 58 | pub fn make_zst(&mut self) -> &mut Self { 59 | self.struct_type = StructType::Zst; 60 | self 61 | } 62 | 63 | /// Make the struct fields unnamed 64 | /// 65 | /// The names of any field will be ignored 66 | /// 67 | /// ``` 68 | /// # use virtue::prelude::Generator; 69 | /// # let mut generator = Generator::with_name("Fooz"); 70 | /// generator 71 | /// .generate_struct("Foo") 72 | /// .make_tuple() 73 | /// .add_field("bar", "u16") 74 | /// .add_field("baz", "String"); 75 | /// # generator.assert_eq("struct Foo (u16 , String ,) ;"); 76 | /// # Ok::<_, virtue::Error>(()) 77 | /// ``` 78 | /// 79 | /// Generates: 80 | /// ``` 81 | /// struct Foo(u16, String); 82 | /// ``` 83 | pub fn make_tuple(&mut self) -> &mut Self { 84 | self.struct_type = StructType::Unnamed; 85 | self 86 | } 87 | 88 | /// Make the struct `pub`. By default the struct will have no visibility modifier and will only be visible in the current scope. 89 | pub fn make_pub(&mut self) -> &mut Self { 90 | self.visibility = Visibility::Pub; 91 | self 92 | } 93 | 94 | /// Inherit the generic parameters of the parent type. 95 | /// 96 | /// ``` 97 | /// # use virtue::prelude::Generator; 98 | /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); 99 | /// // given a derive on struct Bar<'a> 100 | /// generator 101 | /// .generate_struct("Foo") 102 | /// .inherit_generics() 103 | /// .add_field("bar", "&'a str"); 104 | /// # generator.assert_eq("struct Foo < 'a > { bar : &'a str , }"); 105 | /// # Ok::<_, virtue::Error>(()) 106 | /// ``` 107 | /// 108 | /// Generates: 109 | /// ```ignore 110 | /// // given a derive on struct Bar<'a> 111 | /// struct Foo<'a> { 112 | /// bar: &'a str 113 | /// } 114 | /// ``` 115 | pub fn inherit_generics(&mut self) -> &mut Self { 116 | self.generics = self.parent.generics().cloned(); 117 | self 118 | } 119 | 120 | /// Append generic parameters to the type. 121 | /// 122 | /// ``` 123 | /// # use virtue::prelude::Generator; 124 | /// # use virtue::parse::{Generic, Lifetime}; 125 | /// # use proc_macro2::{Ident, Span}; 126 | /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); 127 | /// generator 128 | /// .generate_struct("Foo") 129 | /// .with_generics([Lifetime { ident: Ident::new("a", Span::call_site()), constraint: vec![] }.into()]) 130 | /// .add_field("bar", "&'a str"); 131 | /// # generator.assert_eq("struct Foo < 'a > { bar : &'a str , }"); 132 | /// # Ok::<_, virtue::Error>(()) 133 | /// ``` 134 | /// 135 | /// Generates: 136 | /// ```ignore 137 | /// struct Foo<'a> { 138 | /// bar: &'a str 139 | /// } 140 | /// ``` 141 | pub fn with_generics(&mut self, generics: impl IntoIterator) -> &mut Self { 142 | self.generics 143 | .get_or_insert_with(|| Generics(Vec::new())) 144 | .extend(generics); 145 | self 146 | } 147 | 148 | /// Add a generic parameter to the type. 149 | /// 150 | /// ``` 151 | /// # use virtue::prelude::Generator; 152 | /// # use virtue::parse::{Generic, Lifetime}; 153 | /// # use proc_macro2::{Ident, Span}; 154 | /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); 155 | /// generator 156 | /// .generate_struct("Foo") 157 | /// .with_generic(Lifetime { ident: Ident::new("a", Span::call_site()), constraint: vec![] }.into()) 158 | /// .add_field("bar", "&'a str"); 159 | /// # generator.assert_eq("struct Foo < 'a > { bar : &'a str , }"); 160 | /// # Ok::<_, virtue::Error>(()) 161 | /// ``` 162 | /// 163 | /// Generates: 164 | /// ```ignore 165 | /// struct Foo<'a> { 166 | /// bar: &'a str 167 | /// } 168 | /// ``` 169 | pub fn with_generic(&mut self, generic: Generic) -> &mut Self { 170 | self.generics 171 | .get_or_insert_with(|| Generics(Vec::new())) 172 | .push(generic); 173 | self 174 | } 175 | 176 | /// Add a derive macro to the struct. 177 | /// 178 | /// ``` 179 | /// # use virtue::prelude::Generator; 180 | /// # use virtue::generate::Path; 181 | /// # let mut generator = Generator::with_name("Bar"); 182 | /// generator 183 | /// .generate_struct("Foo") 184 | /// .with_derive("Clone") 185 | /// .with_derive("Default") 186 | /// .with_derive(Path::from_iter(vec!["serde", "Deserialize"])); 187 | /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] struct Foo { }"); 188 | /// # Ok::<_, virtue::Error>(()) 189 | /// ``` 190 | /// 191 | /// Generates: 192 | /// ```ignore 193 | /// #[derive(Clone, Default, serde::Deserialize)] 194 | /// struct Foo { } 195 | /// ``` 196 | pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { 197 | AttributeContainer::with_derive(self, derive) 198 | } 199 | 200 | /// Add derive macros to the struct. 201 | /// 202 | /// ``` 203 | /// # use virtue::prelude::Generator; 204 | /// # use virtue::generate::Path; 205 | /// # let mut generator = Generator::with_name("Bar"); 206 | /// generator 207 | /// .generate_struct("Foo") 208 | /// .with_derives([ 209 | /// "Clone".into(), 210 | /// "Default".into(), 211 | /// Path::from_iter(vec!["serde", "Deserialize"]), 212 | /// ]); 213 | /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] struct Foo { }"); 214 | /// # Ok::<_, virtue::Error>(()) 215 | /// ``` 216 | /// 217 | /// Generates: 218 | /// ```ignore 219 | /// #[derive(Clone, Default, serde::Deserialize)] 220 | /// struct Foo { } 221 | /// ``` 222 | pub fn with_derives>( 223 | &mut self, 224 | derives: impl IntoIterator, 225 | ) -> &mut Self { 226 | AttributeContainer::with_derives(self, derives) 227 | } 228 | 229 | /// Add an attribute to the struct. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) 230 | /// instead. 231 | /// 232 | /// ``` 233 | /// # use virtue::prelude::Generator; 234 | /// # let mut generator = Generator::with_name("Bar"); 235 | /// generator 236 | /// .generate_struct("Foo") 237 | /// .with_attribute("serde", |b| { 238 | /// b.push_parsed("(rename_all = \"camelCase\")")?; 239 | /// Ok(()) 240 | /// })?; 241 | /// # generator.assert_eq("# [serde (rename_all = \"camelCase\")] struct Foo { }"); 242 | /// # Ok::<_, virtue::Error>(()) 243 | /// ``` 244 | /// 245 | /// Generates: 246 | /// ```ignore 247 | /// #[serde(rename_all = "camelCase")] 248 | /// struct Foo { } 249 | /// ``` 250 | pub fn with_attribute( 251 | &mut self, 252 | name: impl AsRef, 253 | value: impl FnOnce(&mut StreamBuilder) -> Result, 254 | ) -> Result<&mut Self> { 255 | AttributeContainer::with_attribute(self, name, value) 256 | } 257 | 258 | /// Add a parsed attribute to the struct. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) 259 | /// instead. 260 | /// 261 | /// ``` 262 | /// # use virtue::prelude::Generator; 263 | /// # let mut generator = Generator::with_name("Bar"); 264 | /// generator 265 | /// .generate_struct("Foo") 266 | /// .with_parsed_attribute("serde(rename_all = \"camelCase\")")?; 267 | /// # generator.assert_eq("# [serde (rename_all = \"camelCase\")] struct Foo { }"); 268 | /// # Ok::<_, virtue::Error>(()) 269 | /// ``` 270 | /// 271 | /// Generates: 272 | /// ```ignore 273 | /// #[serde(rename_all = "camelCase")] 274 | /// struct Foo { } 275 | /// ``` 276 | pub fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> Result<&mut Self> { 277 | AttributeContainer::with_parsed_attribute(self, attribute) 278 | } 279 | 280 | /// Add a token stream as an attribute to the struct. For `#[derive(...)]`, use 281 | /// [`with_derive`](Self::with_derive) instead. 282 | /// 283 | /// ``` 284 | /// # use virtue::prelude::{Generator, TokenStream}; 285 | /// # use std::str::FromStr; 286 | /// # let mut generator = Generator::with_name("Bar"); 287 | /// 288 | /// let attribute = "serde(rename_all = \"camelCase\")".parse::().unwrap(); 289 | /// generator 290 | /// .generate_struct("Foo") 291 | /// .with_attribute_stream(attribute); 292 | /// # generator.assert_eq("# [serde (rename_all = \"camelCase\")] struct Foo { }"); 293 | /// # Ok::<_, virtue::Error>(()) 294 | /// ``` 295 | /// 296 | /// Generates: 297 | /// ```ignore 298 | /// #[serde(rename_all = "camelCase")] 299 | /// struct Foo { } 300 | /// ``` 301 | pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { 302 | AttributeContainer::with_attribute_stream(self, attribute) 303 | } 304 | 305 | /// Add a field to the struct. 306 | /// 307 | /// Names are ignored when the Struct's fields are unnamed 308 | /// 309 | /// ``` 310 | /// # use virtue::prelude::Generator; 311 | /// # let mut generator = Generator::with_name("Fooz"); 312 | /// generator 313 | /// .generate_struct("Foo") 314 | /// .add_field("bar", "u16") 315 | /// .add_field("baz", "String"); 316 | /// # generator.assert_eq("struct Foo { bar : u16 , baz : String , }"); 317 | /// # Ok::<_, virtue::Error>(()) 318 | /// ``` 319 | /// 320 | /// Generates: 321 | /// ``` 322 | /// struct Foo { 323 | /// bar: u16, 324 | /// baz: String, 325 | /// }; 326 | /// ``` 327 | pub fn add_field( 328 | &mut self, 329 | name: impl Into, 330 | ty: impl Into, 331 | ) -> FieldBuilder { 332 | let mut fields = FieldBuilder::from(&mut self.fields); 333 | fields.add_field(name, ty); 334 | fields 335 | } 336 | 337 | /// Add an `impl for ` 338 | pub fn impl_for(&mut self, name: impl Into) -> ImplFor { 339 | ImplFor::new(self, name.into(), None) 340 | } 341 | 342 | /// Generate an `impl ` implementation. See [`Impl`] for more information. 343 | pub fn r#impl(&mut self) -> Impl { 344 | Impl::with_parent_name(self) 345 | } 346 | 347 | /// Generate an `impl ` implementation. See [`Impl`] for more information. 348 | /// 349 | /// Alias for [`impl`] which doesn't need a `r#` prefix. 350 | /// 351 | /// [`impl`]: #method.impl 352 | pub fn generate_impl(&mut self) -> Impl { 353 | Impl::with_parent_name(self) 354 | } 355 | } 356 | 357 | impl AttributeContainer for GenStruct<'_, P> { 358 | fn derives(&mut self) -> &mut Vec { 359 | &mut self.derives 360 | } 361 | 362 | fn attributes(&mut self) -> &mut Vec { 363 | &mut self.attributes 364 | } 365 | } 366 | 367 | impl<'a, P: Parent> Parent for GenStruct<'a, P> { 368 | fn append(&mut self, builder: StreamBuilder) { 369 | self.additional.push(builder); 370 | } 371 | 372 | fn name(&self) -> &Ident { 373 | &self.name 374 | } 375 | 376 | fn generics(&self) -> Option<&Generics> { 377 | self.generics.as_ref() 378 | } 379 | 380 | fn generic_constraints(&self) -> Option<&crate::parse::GenericConstraints> { 381 | None 382 | } 383 | } 384 | 385 | impl<'a, P: Parent> Drop for GenStruct<'a, P> { 386 | fn drop(&mut self) { 387 | use std::mem::take; 388 | let mut builder = StreamBuilder::new(); 389 | 390 | self.build_derives(&mut builder) 391 | .build_attributes(&mut builder); 392 | 393 | if self.visibility == Visibility::Pub { 394 | builder.ident_str("pub"); 395 | } 396 | builder.ident_str("struct").ident(self.name.clone()).append( 397 | self.generics() 398 | .map(Generics::impl_generics) 399 | .unwrap_or_default(), 400 | ); 401 | 402 | match self.struct_type { 403 | StructType::Named => builder 404 | .group(Delimiter::Brace, |b| { 405 | for field in self.fields.iter_mut() { 406 | field.build_attributes(b); 407 | if field.vis == Visibility::Pub { 408 | b.ident_str("pub"); 409 | } 410 | b.ident_str(&field.name) 411 | .punct(':') 412 | .push_parsed(&field.ty)? 413 | .punct(','); 414 | } 415 | Ok(()) 416 | }) 417 | .expect("Could not build struct"), 418 | StructType::Unnamed => builder 419 | .group(Delimiter::Parenthesis, |b| { 420 | for field in self.fields.iter_mut() { 421 | field.build_attributes(b); 422 | if field.vis == Visibility::Pub { 423 | b.ident_str("pub"); 424 | } 425 | b.push_parsed(&field.ty)?.punct(','); 426 | } 427 | Ok(()) 428 | }) 429 | .expect("Could not build struct") 430 | .punct(';'), 431 | StructType::Zst => builder.punct(';'), 432 | }; 433 | 434 | for additional in take(&mut self.additional) { 435 | builder.append(additional); 436 | } 437 | self.parent.append(builder); 438 | } 439 | } 440 | 441 | enum StructType { 442 | Named, 443 | Unnamed, 444 | Zst, 445 | } 446 | -------------------------------------------------------------------------------- /src/generate/generate_item.rs: -------------------------------------------------------------------------------- 1 | use super::StreamBuilder; 2 | use crate::{ 3 | parse::Visibility, 4 | prelude::{Delimiter, Result}, 5 | }; 6 | 7 | /// A builder for constants. 8 | pub struct GenConst<'a> { 9 | consts: &'a mut Vec, 10 | attrs: Vec, 11 | name: String, 12 | ty: String, 13 | vis: Visibility, 14 | } 15 | 16 | impl<'a> GenConst<'a> { 17 | pub(crate) fn new( 18 | consts: &'a mut Vec, 19 | name: impl Into, 20 | ty: impl Into, 21 | ) -> Self { 22 | Self { 23 | consts, 24 | attrs: Vec::new(), 25 | name: name.into(), 26 | ty: ty.into(), 27 | vis: Visibility::Default, 28 | } 29 | } 30 | 31 | /// Make the const `pub`. By default the const will have no visibility modifier and will only be visible in the current scope. 32 | #[must_use] 33 | pub fn make_pub(mut self) -> Self { 34 | self.vis = Visibility::Pub; 35 | self 36 | } 37 | 38 | /// Add an outer attribute 39 | #[must_use] 40 | pub fn with_attr(mut self, attr: impl Into) -> Self { 41 | self.attrs.push(attr.into()); 42 | self 43 | } 44 | 45 | /// Complete the constant definition. This function takes a callback that will form the value of the constant. 46 | /// 47 | /// ``` 48 | /// # use virtue::prelude::Generator; 49 | /// # let mut generator = Generator::with_name("Bar"); 50 | /// generator.impl_for("Foo") 51 | /// .generate_const("BAR", "u8") 52 | /// .with_value(|b| { 53 | /// b.push_parsed("5")?; 54 | /// Ok(()) 55 | /// })?; 56 | /// # generator.assert_eq("impl Foo for Bar { const BAR : u8 = 5 ; }"); 57 | /// # Ok::<_, virtue::Error>(()) 58 | /// ``` 59 | /// 60 | /// Generates: 61 | /// ```ignore 62 | /// impl Foo for { 63 | /// const BAR: u8 = 5; 64 | /// } 65 | /// ``` 66 | pub fn with_value(self, f: F) -> Result 67 | where 68 | F: FnOnce(&mut StreamBuilder) -> Result, 69 | { 70 | let mut builder = StreamBuilder::new(); 71 | 72 | for attr in self.attrs { 73 | builder 74 | .punct('#') 75 | .punct('!') 76 | .group(Delimiter::Bracket, |builder| { 77 | builder.push_parsed(attr)?; 78 | Ok(()) 79 | })?; 80 | } 81 | 82 | if self.vis == Visibility::Pub { 83 | builder.ident_str("pub"); 84 | } 85 | 86 | builder 87 | .ident_str("const") 88 | .push_parsed(self.name)? 89 | .punct(':') 90 | .push_parsed(self.ty)? 91 | .punct('='); 92 | f(&mut builder)?; 93 | builder.punct(';'); 94 | 95 | self.consts.push(builder); 96 | Ok(()) 97 | } 98 | } 99 | 100 | /// A builder for functions. 101 | pub struct FnBuilder<'a, P> { 102 | parent: &'a mut P, 103 | name: String, 104 | 105 | attrs: Vec, 106 | is_async: bool, 107 | lifetimes: Vec<(String, Vec)>, 108 | generics: Vec<(String, Vec)>, 109 | self_arg: FnSelfArg, 110 | args: Vec<(String, String)>, 111 | return_type: Option, 112 | vis: Visibility, 113 | } 114 | 115 | impl<'a, P: FnParent> FnBuilder<'a, P> { 116 | pub(super) fn new(parent: &'a mut P, name: impl Into) -> Self { 117 | Self { 118 | parent, 119 | name: name.into(), 120 | attrs: Vec::new(), 121 | is_async: false, 122 | lifetimes: Vec::new(), 123 | generics: Vec::new(), 124 | self_arg: FnSelfArg::None, 125 | args: Vec::new(), 126 | return_type: None, 127 | vis: Visibility::Default, 128 | } 129 | } 130 | 131 | /// Add an outer attribute 132 | #[must_use] 133 | pub fn with_attr(mut self, attr: impl Into) -> Self { 134 | self.attrs.push(attr.into()); 135 | self 136 | } 137 | 138 | /// Add a lifetime parameter. 139 | /// 140 | /// ``` 141 | /// # use virtue::prelude::Generator; 142 | /// # let mut generator = Generator::with_name("Foo"); 143 | /// generator 144 | /// .r#impl() 145 | /// .generate_fn("foo") // fn foo() 146 | /// .with_lifetime("a") // fn foo<'a>() 147 | /// # .body(|_| Ok(())).unwrap(); 148 | /// # generator.assert_eq("impl Foo { fn foo < 'a > () { } }"); 149 | /// ``` 150 | #[must_use] 151 | pub fn with_lifetime(mut self, name: impl Into) -> Self { 152 | self.lifetimes.push((name.into(), Vec::new())); 153 | self 154 | } 155 | 156 | /// Make the function async 157 | /// 158 | /// ``` 159 | /// # use virtue::prelude::Generator; 160 | /// # let mut generator = Generator::with_name("Foo"); 161 | /// generator 162 | /// .r#impl() 163 | /// .generate_fn("foo") // fn foo() 164 | /// .as_async() // async fn foo() 165 | /// # .body(|_| Ok(())).unwrap(); 166 | /// # generator.assert_eq("impl Foo { async fn foo () { } }"); 167 | /// ``` 168 | #[must_use] 169 | pub fn as_async(mut self) -> Self { 170 | self.is_async = true; 171 | self 172 | } 173 | 174 | /// Add a lifetime parameter. 175 | /// 176 | /// `dependencies` are the lifetime dependencies of the given lifetime. 177 | /// 178 | /// ``` 179 | /// # use virtue::prelude::Generator; 180 | /// # let mut generator = Generator::with_name("Foo"); 181 | /// generator 182 | /// .r#impl() 183 | /// .generate_fn("foo") // fn foo() 184 | /// .with_lifetime("a") // fn foo<'a>() 185 | /// .with_lifetime_deps("b", ["a"]) // fn foo<'b: 'a>() 186 | /// # .body(|_| Ok(())).unwrap(); 187 | /// # generator.assert_eq("impl Foo { fn foo < 'a , 'b : 'a > () { } }"); 188 | /// ``` 189 | #[must_use] 190 | pub fn with_lifetime_deps( 191 | mut self, 192 | name: impl Into, 193 | dependencies: ITER, 194 | ) -> Self 195 | where 196 | ITER: IntoIterator, 197 | I: Into, 198 | { 199 | self.lifetimes.push(( 200 | name.into(), 201 | dependencies.into_iter().map(Into::into).collect(), 202 | )); 203 | self 204 | } 205 | 206 | /// Add a generic parameter. Keep in mind that will *not* work for lifetimes. 207 | /// 208 | /// ``` 209 | /// # use virtue::prelude::Generator; 210 | /// # let mut generator = Generator::with_name("Foo"); 211 | /// generator 212 | /// .r#impl() 213 | /// .generate_fn("foo") // fn foo() 214 | /// .with_generic("D") // fn foo() 215 | /// # .body(|_| Ok(())).unwrap(); 216 | /// # generator.assert_eq("impl Foo { fn foo < D > () { } }"); 217 | /// ``` 218 | #[must_use] 219 | pub fn with_generic(mut self, name: impl Into) -> Self { 220 | self.generics.push((name.into(), Vec::new())); 221 | self 222 | } 223 | 224 | /// Add a generic parameter. Keep in mind that will *not* work for lifetimes. 225 | /// 226 | /// `dependencies` are the dependencies of the parameter. 227 | /// 228 | /// ``` 229 | /// # use virtue::prelude::Generator; 230 | /// # let mut generator = Generator::with_name("Foo"); 231 | /// generator 232 | /// .r#impl() 233 | /// .generate_fn("foo") // fn foo() 234 | /// .with_generic("D") // fn foo() 235 | /// .with_generic_deps("E", ["Encodable"]) // fn foo(); 236 | /// # .body(|_| Ok(())).unwrap(); 237 | /// # generator.assert_eq("impl Foo { fn foo < D , E : Encodable > () { } }"); 238 | /// ``` 239 | #[must_use] 240 | pub fn with_generic_deps(mut self, name: impl Into, dependencies: DEP) -> Self 241 | where 242 | DEP: IntoIterator, 243 | I: Into, 244 | { 245 | self.generics.push(( 246 | name.into(), 247 | dependencies.into_iter().map(Into::into).collect(), 248 | )); 249 | self 250 | } 251 | 252 | /// Set the value for `self`. See [FnSelfArg] for more information. 253 | /// 254 | /// ``` 255 | /// # use virtue::prelude::{Generator, FnSelfArg}; 256 | /// # let mut generator = Generator::with_name("Foo"); 257 | /// generator 258 | /// .r#impl() 259 | /// .generate_fn("foo") // fn foo() 260 | /// .with_self_arg(FnSelfArg::RefSelf) // fn foo(&self) 261 | /// # .body(|_| Ok(())).unwrap(); 262 | /// # generator.assert_eq("impl Foo { fn foo (& self ,) { } }"); 263 | /// ``` 264 | #[must_use] 265 | pub fn with_self_arg(mut self, self_arg: FnSelfArg) -> Self { 266 | self.self_arg = self_arg; 267 | self 268 | } 269 | 270 | /// Add an argument with a `name` and a `ty`. 271 | /// 272 | /// ``` 273 | /// # use virtue::prelude::Generator; 274 | /// # let mut generator = Generator::with_name("Foo"); 275 | /// generator 276 | /// .r#impl() 277 | /// .generate_fn("foo") // fn foo() 278 | /// .with_arg("a", "u32") // fn foo(a: u32) 279 | /// .with_arg("b", "u32") // fn foo(a: u32, b: u32) 280 | /// # .body(|_| Ok(())).unwrap(); 281 | /// # generator.assert_eq("impl Foo { fn foo (a : u32 , b : u32) { } }"); 282 | /// ``` 283 | #[must_use] 284 | pub fn with_arg(mut self, name: impl Into, ty: impl Into) -> Self { 285 | self.args.push((name.into(), ty.into())); 286 | self 287 | } 288 | 289 | /// Set the return type for the function. By default the function will have no return type. 290 | /// 291 | /// ``` 292 | /// # use virtue::prelude::Generator; 293 | /// # let mut generator = Generator::with_name("Foo"); 294 | /// generator 295 | /// .r#impl() 296 | /// .generate_fn("foo") // fn foo() 297 | /// .with_return_type("u32") // fn foo() -> u32 298 | /// # .body(|_| Ok(())).unwrap(); 299 | /// # generator.assert_eq("impl Foo { fn foo () ->u32 { } }"); 300 | /// ``` 301 | #[must_use] 302 | pub fn with_return_type(mut self, ret_type: impl Into) -> Self { 303 | self.return_type = Some(ret_type.into()); 304 | self 305 | } 306 | 307 | /// Make the function `pub`. If this is not called, the function will have no visibility modifier. 308 | #[must_use] 309 | pub fn make_pub(mut self) -> Self { 310 | self.vis = Visibility::Pub; 311 | self 312 | } 313 | 314 | /// Complete the function definition. This function takes a callback that will form the body of the function. 315 | /// 316 | /// ``` 317 | /// # use virtue::prelude::Generator; 318 | /// # let mut generator = Generator::with_name("Foo"); 319 | /// generator 320 | /// .r#impl() 321 | /// .generate_fn("foo") // fn foo() 322 | /// .body(|b| { 323 | /// b.push_parsed("println!(\"hello world\");")?; 324 | /// Ok(()) 325 | /// }) 326 | /// .unwrap(); 327 | /// // fn foo() { 328 | /// // println!("Hello world"); 329 | /// // } 330 | /// # generator.assert_eq("impl Foo { fn foo () { println ! (\"hello world\") ; } }"); 331 | /// ``` 332 | pub fn body( 333 | self, 334 | body_builder: impl FnOnce(&mut StreamBuilder) -> crate::Result, 335 | ) -> crate::Result { 336 | let FnBuilder { 337 | parent, 338 | name, 339 | attrs, 340 | is_async, 341 | lifetimes, 342 | generics, 343 | self_arg, 344 | args, 345 | return_type, 346 | vis, 347 | } = self; 348 | 349 | let mut builder = StreamBuilder::new(); 350 | 351 | // attrs 352 | for attr in attrs { 353 | builder.punct('#').group(Delimiter::Bracket, |builder| { 354 | builder.push_parsed(attr)?; 355 | Ok(()) 356 | })?; 357 | } 358 | 359 | // function name; `fn name` 360 | if vis == Visibility::Pub { 361 | builder.ident_str("pub"); 362 | } 363 | if is_async { 364 | builder.ident_str("async"); 365 | } 366 | builder.ident_str("fn"); 367 | builder.ident_str(name); 368 | 369 | // lifetimes; `<'a: 'b, D: Display>` 370 | if !lifetimes.is_empty() || !generics.is_empty() { 371 | builder.punct('<'); 372 | let mut is_first = true; 373 | for (lifetime, dependencies) in lifetimes { 374 | if is_first { 375 | is_first = false; 376 | } else { 377 | builder.punct(','); 378 | } 379 | builder.lifetime_str(lifetime.as_ref()); 380 | if !dependencies.is_empty() { 381 | for (idx, dependency) in dependencies.into_iter().enumerate() { 382 | builder.punct(if idx == 0 { ':' } else { '+' }); 383 | builder.lifetime_str(dependency.as_ref()); 384 | } 385 | } 386 | } 387 | for (generic, dependencies) in generics { 388 | if is_first { 389 | is_first = false; 390 | } else { 391 | builder.punct(','); 392 | } 393 | builder.ident_str(&generic); 394 | if !dependencies.is_empty() { 395 | for (idx, dependency) in dependencies.into_iter().enumerate() { 396 | builder.punct(if idx == 0 { ':' } else { '+' }); 397 | builder.push_parsed(&dependency)?; 398 | } 399 | } 400 | } 401 | builder.punct('>'); 402 | } 403 | 404 | // Arguments; `(&self, foo: &Bar)` 405 | builder.group(Delimiter::Parenthesis, |arg_stream| { 406 | if let Some(self_arg) = self_arg.into_token_tree() { 407 | arg_stream.append(self_arg); 408 | arg_stream.punct(','); 409 | } 410 | for (idx, (arg_name, arg_ty)) in args.into_iter().enumerate() { 411 | if idx != 0 { 412 | arg_stream.punct(','); 413 | } 414 | arg_stream.push_parsed(&arg_name)?; 415 | arg_stream.punct(':'); 416 | arg_stream.push_parsed(&arg_ty)?; 417 | } 418 | Ok(()) 419 | })?; 420 | 421 | // Return type: `-> ResultType` 422 | if let Some(return_type) = return_type { 423 | builder.puncts("->"); 424 | builder.push_parsed(&return_type)?; 425 | } 426 | 427 | let mut body_stream = StreamBuilder::new(); 428 | body_builder(&mut body_stream)?; 429 | 430 | parent.append(builder, body_stream) 431 | } 432 | } 433 | 434 | pub trait FnParent { 435 | fn append(&mut self, fn_definition: StreamBuilder, fn_body: StreamBuilder) -> Result; 436 | } 437 | 438 | /// The `self` argument of a function 439 | #[allow(dead_code)] 440 | #[non_exhaustive] 441 | pub enum FnSelfArg { 442 | /// No `self` argument. The function will be a static function. 443 | None, 444 | 445 | /// `self`. The function will consume self. 446 | TakeSelf, 447 | 448 | /// `mut self`. The function will consume self. 449 | MutTakeSelf, 450 | 451 | /// `&self`. The function will take self by reference. 452 | RefSelf, 453 | 454 | /// `&mut self`. The function will take self by mutable reference. 455 | MutSelf, 456 | } 457 | 458 | impl FnSelfArg { 459 | fn into_token_tree(self) -> Option { 460 | let mut builder = StreamBuilder::new(); 461 | match self { 462 | Self::None => return None, 463 | Self::TakeSelf => { 464 | builder.ident_str("self"); 465 | } 466 | Self::MutTakeSelf => { 467 | builder.ident_str("mut"); 468 | builder.ident_str("self"); 469 | } 470 | Self::RefSelf => { 471 | builder.punct('&'); 472 | builder.ident_str("self"); 473 | } 474 | Self::MutSelf => { 475 | builder.punct('&'); 476 | builder.ident_str("mut"); 477 | builder.ident_str("self"); 478 | } 479 | } 480 | Some(builder) 481 | } 482 | } 483 | -------------------------------------------------------------------------------- /src/generate/gen_enum.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, Path, StreamBuilder, 3 | StringOrIdent, 4 | }; 5 | use crate::parse::{Generic, Generics, Visibility}; 6 | use crate::prelude::{Delimiter, Ident, Span, TokenStream}; 7 | use crate::Result; 8 | 9 | /// Builder to generate an `enum { { ... }, ... }` 10 | /// 11 | /// ``` 12 | /// # use virtue::prelude::Generator; 13 | /// # let mut generator = Generator::with_name("Fooz"); 14 | /// { 15 | /// let mut enumgen = generator.generate_enum("Foo"); 16 | /// enumgen 17 | /// .add_value("ZST") 18 | /// .make_zst(); 19 | /// enumgen 20 | /// .add_value("Named") 21 | /// .add_field("bar", "u16") 22 | /// .add_field("baz", "String"); 23 | /// enumgen 24 | /// .add_value("Unnamed") 25 | /// .make_tuple() 26 | /// .add_field("", "u16") 27 | /// .add_field("baz", "String"); 28 | /// } 29 | /// # generator.assert_eq("enum Foo { ZST , Named { bar : u16 , baz : String , } , Unnamed (u16 , String ,) , }"); 30 | /// # Ok::<_, virtue::Error>(()) 31 | /// ``` 32 | /// 33 | /// Generates: 34 | /// ``` 35 | /// enum Foo { 36 | /// ZST, 37 | /// Named { 38 | /// bar: u16, 39 | /// baz: String, 40 | /// }, 41 | /// Unnamed(u16, String), 42 | /// }; 43 | /// ``` 44 | pub struct GenEnum<'a, P: Parent> { 45 | parent: &'a mut P, 46 | name: Ident, 47 | visibility: Visibility, 48 | generics: Option, 49 | values: Vec, 50 | derives: Vec, 51 | attributes: Vec, 52 | additional: Vec, 53 | } 54 | 55 | impl<'a, P: Parent> GenEnum<'a, P> { 56 | pub(crate) fn new(parent: &'a mut P, name: impl Into) -> Self { 57 | Self { 58 | parent, 59 | name: Ident::new(name.into().as_str(), Span::call_site()), 60 | visibility: Visibility::Default, 61 | generics: None, 62 | values: Vec::new(), 63 | derives: Vec::new(), 64 | attributes: Vec::new(), 65 | additional: Vec::new(), 66 | } 67 | } 68 | 69 | /// Make the enum `pub`. By default the struct will have no visibility modifier and will only be visible in the current scope. 70 | pub fn make_pub(&mut self) -> &mut Self { 71 | self.visibility = Visibility::Pub; 72 | self 73 | } 74 | 75 | /// Add a derive macro to the enum. 76 | /// 77 | /// ``` 78 | /// # use virtue::prelude::Generator; 79 | /// # use virtue::generate::Path; 80 | /// # let mut generator = Generator::with_name("Bar"); 81 | /// generator 82 | /// .generate_enum("Foo") 83 | /// .with_derive("Clone") 84 | /// .with_derive("Default") 85 | /// .with_derive(Path::from_iter(vec!["serde", "Deserialize"])); 86 | /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] enum Foo { }"); 87 | /// # Ok::<_, virtue::Error>(()) 88 | /// ``` 89 | /// 90 | /// Generates: 91 | /// ```ignore 92 | /// #[derive(Clone, Default, serde::Deserialize)] 93 | /// enum Foo { } 94 | pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { 95 | AttributeContainer::with_derive(self, derive) 96 | } 97 | 98 | /// Add derive macros to the enum. 99 | /// 100 | /// ``` 101 | /// # use virtue::prelude::Generator; 102 | /// # use virtue::generate::Path; 103 | /// # let mut generator = Generator::with_name("Bar"); 104 | /// generator 105 | /// .generate_enum("Foo") 106 | /// .with_derives([ 107 | /// "Clone".into(), 108 | /// "Default".into(), 109 | /// Path::from_iter(vec!["serde", "Deserialize"]), 110 | /// ]); 111 | /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] enum Foo { }"); 112 | /// # Ok::<_, virtue::Error>(()) 113 | /// ``` 114 | /// 115 | /// Generates: 116 | /// ```ignore 117 | /// #[derive(Clone, Default, serde::Deserialize)] 118 | /// enum Foo { } 119 | pub fn with_derives>( 120 | &mut self, 121 | derives: impl IntoIterator, 122 | ) -> &mut Self { 123 | AttributeContainer::with_derives(self, derives) 124 | } 125 | 126 | /// Add an attribute to the enum. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) 127 | /// instead. 128 | /// 129 | /// ``` 130 | /// # use virtue::prelude::Generator; 131 | /// # let mut generator = Generator::with_name("Bar"); 132 | /// generator 133 | /// .generate_enum("Foo") 134 | /// .with_attribute("serde", |b| { 135 | /// b.push_parsed("(untagged)")?; 136 | /// Ok(()) 137 | /// })?; 138 | /// # generator.assert_eq("# [serde (untagged)] enum Foo { }"); 139 | /// # Ok::<_, virtue::Error>(()) 140 | /// ``` 141 | /// 142 | /// Generates: 143 | /// ```ignore 144 | /// #[serde(untagged)] 145 | /// enum Foo { } 146 | /// ``` 147 | pub fn with_attribute( 148 | &mut self, 149 | name: impl AsRef, 150 | value: impl FnOnce(&mut StreamBuilder) -> Result, 151 | ) -> Result<&mut Self> { 152 | AttributeContainer::with_attribute(self, name, value) 153 | } 154 | 155 | /// Add a parsed attribute to the enum. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) 156 | /// instead. 157 | /// 158 | /// ``` 159 | /// # use virtue::prelude::Generator; 160 | /// # let mut generator = Generator::with_name("Bar"); 161 | /// 162 | /// generator 163 | /// .generate_enum("Foo") 164 | /// .with_parsed_attribute("serde(untagged)")?; 165 | /// # generator.assert_eq("# [serde (untagged)] enum Foo { }"); 166 | /// # Ok::<_, virtue::Error>(()) 167 | /// ``` 168 | /// 169 | /// Generates: 170 | /// ```ignore 171 | /// #[serde(untagged)] 172 | /// enum Foo { } 173 | /// ``` 174 | pub fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> Result<&mut Self> { 175 | AttributeContainer::with_parsed_attribute(self, attribute) 176 | } 177 | 178 | /// Add a token stream as an attribute to the enum. For `#[derive(...)]`, use 179 | /// [`with_derive`](Self::with_derive) instead. 180 | /// 181 | /// ``` 182 | /// # use virtue::prelude::{Generator, TokenStream}; 183 | /// # let mut generator = Generator::with_name("Bar"); 184 | /// 185 | /// let attribute = "serde(untagged)".parse::().unwrap(); 186 | /// generator 187 | /// .generate_enum("Foo") 188 | /// .with_attribute_stream(attribute); 189 | /// # generator.assert_eq("# [serde (untagged)] enum Foo { }"); 190 | /// # Ok::<_, virtue::Error>(()) 191 | /// ``` 192 | /// 193 | /// Generates: 194 | /// ```ignore 195 | /// #[serde(untagged)] 196 | /// enum Foo { } 197 | /// ``` 198 | pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { 199 | AttributeContainer::with_attribute_stream(self, attribute) 200 | } 201 | 202 | /// Inherit the generic parameters of the parent type. 203 | /// 204 | /// ``` 205 | /// # use virtue::prelude::Generator; 206 | /// # use virtue::parse::{Generic, Lifetime}; 207 | /// # use proc_macro2::{Ident, Span}; 208 | /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); 209 | /// // given a derive on enum Bar<'a> 210 | /// generator 211 | /// .generate_enum("Foo") 212 | /// .inherit_generics() 213 | /// .add_value("Bar") 214 | /// .make_tuple() 215 | /// .add_field("bar", "&'a str"); 216 | /// # generator.assert_eq("enum Foo < 'a > { Bar (&'a str ,) , }"); 217 | /// # Ok::<_, virtue::Error>(()) 218 | /// ``` 219 | /// 220 | /// Generates: 221 | /// ```ignore 222 | /// // given a derive on enum Bar<'a> 223 | /// enum Foo<'a> { 224 | /// Bar(&'a str) 225 | /// } 226 | /// ``` 227 | pub fn inherit_generics(&mut self) -> &mut Self { 228 | self.generics = self.parent.generics().cloned(); 229 | self 230 | } 231 | 232 | /// Append generic parameters to the type. 233 | /// 234 | /// ``` 235 | /// # use virtue::prelude::Generator; 236 | /// # use virtue::parse::{Generic, Lifetime}; 237 | /// # use proc_macro2::{Ident, Span}; 238 | /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); 239 | /// generator 240 | /// .generate_enum("Foo") 241 | /// .with_generics([Lifetime { ident: Ident::new("a", Span::call_site()), constraint: vec![] }.into()]) 242 | /// .add_value("Bar") 243 | /// .make_tuple() 244 | /// .add_field("bar", "&'a str"); 245 | /// # generator.assert_eq("enum Foo < 'a > { Bar (&'a str ,) , }"); 246 | /// # Ok::<_, virtue::Error>(()) 247 | /// ``` 248 | /// 249 | /// Generates: 250 | /// ```ignore 251 | /// enum Foo<'a> { 252 | /// Bar(&'a str) 253 | /// } 254 | /// ``` 255 | pub fn with_generics(&mut self, generics: impl IntoIterator) -> &mut Self { 256 | self.generics 257 | .get_or_insert_with(|| Generics(Vec::new())) 258 | .extend(generics); 259 | self 260 | } 261 | 262 | /// Add a generic parameter to the type. 263 | /// 264 | /// ``` 265 | /// # use virtue::prelude::Generator; 266 | /// # use virtue::parse::{Generic, Lifetime}; 267 | /// # use proc_macro2::{Ident, Span}; 268 | /// # let mut generator = Generator::with_name("Bar").with_lifetime("a"); 269 | /// generator 270 | /// .generate_enum("Foo") 271 | /// .with_generic(Lifetime { ident: Ident::new("a", Span::call_site()), constraint: vec![] }.into()) 272 | /// .add_value("Bar") 273 | /// .make_tuple() 274 | /// .add_field("bar", "&'a str"); 275 | /// # generator.assert_eq("enum Foo < 'a > { Bar (&'a str ,) , }"); 276 | /// # Ok::<_, virtue::Error>(()) 277 | /// ``` 278 | /// 279 | /// Generates: 280 | /// ```ignore 281 | /// enum Foo<'a> { 282 | /// Bar(&'a str) 283 | /// } 284 | /// ``` 285 | pub fn with_generic(&mut self, generic: Generic) -> &mut Self { 286 | self.generics 287 | .get_or_insert_with(|| Generics(Vec::new())) 288 | .push(generic); 289 | self 290 | } 291 | 292 | /// Add an enum value 293 | /// 294 | /// Returns a builder for the value that's similar to GenStruct 295 | pub fn add_value(&mut self, name: impl Into) -> &mut EnumValue { 296 | self.values.push(EnumValue::new(name)); 297 | self.values.last_mut().unwrap() 298 | } 299 | 300 | /// Add an `impl for ` 301 | pub fn impl_for(&mut self, name: impl Into) -> ImplFor { 302 | ImplFor::new(self, name.into(), None) 303 | } 304 | 305 | /// Generate an `impl ` implementation. See [`Impl`] for more information. 306 | pub fn r#impl(&mut self) -> Impl { 307 | Impl::with_parent_name(self) 308 | } 309 | 310 | /// Generate an `impl ` implementation. See [`Impl`] for more information. 311 | /// 312 | /// Alias for [`impl`] which doesn't need a `r#` prefix. 313 | /// 314 | /// [`impl`]: #method.impl 315 | pub fn generate_impl(&mut self) -> Impl { 316 | Impl::with_parent_name(self) 317 | } 318 | } 319 | 320 | impl AttributeContainer for GenEnum<'_, P> { 321 | fn derives(&mut self) -> &mut Vec { 322 | &mut self.derives 323 | } 324 | 325 | fn attributes(&mut self) -> &mut Vec { 326 | &mut self.attributes 327 | } 328 | } 329 | 330 | impl<'a, P: Parent> Parent for GenEnum<'a, P> { 331 | fn append(&mut self, builder: StreamBuilder) { 332 | self.additional.push(builder); 333 | } 334 | 335 | fn name(&self) -> &Ident { 336 | &self.name 337 | } 338 | 339 | fn generics(&self) -> Option<&Generics> { 340 | self.generics.as_ref() 341 | } 342 | 343 | fn generic_constraints(&self) -> Option<&crate::parse::GenericConstraints> { 344 | None 345 | } 346 | } 347 | 348 | impl<'a, P: Parent> Drop for GenEnum<'a, P> { 349 | fn drop(&mut self) { 350 | let mut builder = StreamBuilder::new(); 351 | self.build_derives(&mut builder) 352 | .build_attributes(&mut builder); 353 | if self.visibility == Visibility::Pub { 354 | builder.ident_str("pub"); 355 | } 356 | builder 357 | .ident_str("enum") 358 | .ident(self.name.clone()) 359 | .append( 360 | self.generics() 361 | .map(Generics::impl_generics) 362 | .unwrap_or_default(), 363 | ) 364 | .group(Delimiter::Brace, |b| { 365 | for value in self.values.iter_mut() { 366 | build_value(b, value)?; 367 | } 368 | 369 | Ok(()) 370 | }) 371 | .expect("Could not build enum"); 372 | 373 | for additional in std::mem::take(&mut self.additional) { 374 | builder.append(additional); 375 | } 376 | self.parent.append(builder); 377 | } 378 | } 379 | 380 | fn build_value(builder: &mut StreamBuilder, value: &mut EnumValue) -> Result { 381 | value.build_attributes(builder); 382 | builder.ident(value.name.clone()); 383 | 384 | match value.value_type { 385 | ValueType::Named => builder.group(Delimiter::Brace, |b| { 386 | for field in value.fields.iter_mut() { 387 | field.build_attributes(b); 388 | if field.vis == Visibility::Pub { 389 | b.ident_str("pub"); 390 | } 391 | b.ident_str(&field.name) 392 | .punct(':') 393 | .push_parsed(&field.ty)? 394 | .punct(','); 395 | } 396 | Ok(()) 397 | })?, 398 | ValueType::Unnamed => builder.group(Delimiter::Parenthesis, |b| { 399 | for field in value.fields.iter_mut() { 400 | field.build_attributes(b); 401 | if field.vis == Visibility::Pub { 402 | b.ident_str("pub"); 403 | } 404 | b.push_parsed(&field.ty)?.punct(','); 405 | } 406 | Ok(()) 407 | })?, 408 | ValueType::Zst => builder, 409 | }; 410 | 411 | builder.punct(','); 412 | 413 | Ok(()) 414 | } 415 | 416 | pub struct EnumValue { 417 | name: Ident, 418 | fields: Vec, 419 | value_type: ValueType, 420 | attributes: Vec, 421 | } 422 | 423 | impl EnumValue { 424 | fn new(name: impl Into) -> Self { 425 | Self { 426 | name: Ident::new(name.into().as_str(), Span::call_site()), 427 | fields: Vec::new(), 428 | value_type: ValueType::Named, 429 | attributes: Vec::new(), 430 | } 431 | } 432 | 433 | /// Make the struct a zero-sized type (no fields) 434 | /// 435 | /// Any fields will be ignored 436 | pub fn make_zst(&mut self) -> &mut Self { 437 | self.value_type = ValueType::Zst; 438 | self 439 | } 440 | 441 | /// Make the struct fields unnamed 442 | /// 443 | /// The names of any field will be ignored 444 | pub fn make_tuple(&mut self) -> &mut Self { 445 | self.value_type = ValueType::Unnamed; 446 | self 447 | } 448 | 449 | /// Add an attribute to the variant. 450 | /// 451 | /// ``` 452 | /// # use virtue::prelude::Generator; 453 | /// # let mut generator = Generator::with_name("Bar"); 454 | /// generator 455 | /// .generate_enum("Foo") 456 | /// .add_value("Bar") 457 | /// .with_attribute("serde", |b| { 458 | /// b.push_parsed("(rename_all = \"camelCase\")")?; 459 | /// Ok(()) 460 | /// })?; 461 | /// # generator.assert_eq("enum Foo { # [serde (rename_all = \"camelCase\")] Bar { } , }"); 462 | /// # Ok::<_, virtue::Error>(()) 463 | /// ``` 464 | /// 465 | /// Generates: 466 | /// ```ignore 467 | /// enum Foo { 468 | /// #[serde(rename_all = "camelCase")] 469 | /// Bar { } 470 | /// } 471 | /// ``` 472 | pub fn with_attribute( 473 | &mut self, 474 | name: impl AsRef, 475 | value: impl FnOnce(&mut StreamBuilder) -> Result, 476 | ) -> Result<&mut Self> { 477 | AttributeContainer::with_attribute(self, name, value) 478 | } 479 | 480 | /// Add a parsed attribute to the variant. 481 | /// 482 | /// ``` 483 | /// # use virtue::prelude::Generator; 484 | /// # let mut generator = Generator::with_name("Bar"); 485 | /// generator 486 | /// .generate_enum("Foo") 487 | /// .add_value("Bar") 488 | /// .with_parsed_attribute("serde(rename_all = \"camelCase\")")?; 489 | /// # generator.assert_eq("enum Foo { # [serde (rename_all = \"camelCase\")] Bar { } , }"); 490 | /// # Ok::<_, virtue::Error>(()) 491 | /// ``` 492 | /// 493 | /// Generates: 494 | /// ```ignore 495 | /// enum Foo { 496 | /// #[serde(rename_all = "camelCase")] 497 | /// Bar { } 498 | /// } 499 | /// ``` 500 | pub fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> Result<&mut Self> { 501 | AttributeContainer::with_parsed_attribute(self, attribute) 502 | } 503 | 504 | /// Add a token stream as an attribute to the variant. 505 | /// 506 | /// ``` 507 | /// # use virtue::prelude::{Generator, TokenStream}; 508 | /// # let mut generator = Generator::with_name("Bar"); 509 | /// let attribute = "serde(rename_all = \"camelCase\")".parse::().unwrap(); 510 | /// generator 511 | /// .generate_enum("Foo") 512 | /// .add_value("Bar") 513 | /// .with_attribute_stream(attribute); 514 | /// # generator.assert_eq("enum Foo { # [serde (rename_all = \"camelCase\")] Bar { } , }"); 515 | /// # Ok::<_, virtue::Error>(()) 516 | /// ``` 517 | /// 518 | /// Generates: 519 | /// ```ignore 520 | /// enum Foo { 521 | /// #[serde(rename_all = "camelCase")] 522 | /// Bar { } 523 | /// } 524 | /// ``` 525 | pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { 526 | AttributeContainer::with_attribute_stream(self, attribute) 527 | } 528 | 529 | /// Add a field to the enum value. 530 | /// 531 | /// Names are ignored when the enum value's fields are unnamed 532 | /// 533 | /// ``` 534 | /// # use virtue::prelude::Generator; 535 | /// # let mut generator = Generator::with_name("Fooz"); 536 | /// generator 537 | /// .generate_enum("Foo") 538 | /// .add_value("Bar") 539 | /// .add_field("bar", "u16") 540 | /// .add_field("baz", "String"); 541 | /// # generator.assert_eq("enum Foo { Bar { bar : u16 , baz : String , } , }"); 542 | /// # Ok::<_, virtue::Error>(()) 543 | /// ``` 544 | /// 545 | /// Generates: 546 | /// ``` 547 | /// enum Foo { 548 | /// Bar { 549 | /// bar: u16, 550 | /// baz: String 551 | /// } 552 | /// }; 553 | /// ``` 554 | pub fn add_field( 555 | &mut self, 556 | name: impl Into, 557 | ty: impl Into, 558 | ) -> FieldBuilder { 559 | let mut fields = FieldBuilder::from(&mut self.fields); 560 | fields.add_field(name, ty); 561 | fields 562 | } 563 | } 564 | 565 | impl AttributeContainer for EnumValue { 566 | fn derives(&mut self) -> &mut Vec { 567 | unreachable!("enum variants cannot have derives") 568 | } 569 | 570 | fn attributes(&mut self) -> &mut Vec { 571 | &mut self.attributes 572 | } 573 | } 574 | 575 | enum ValueType { 576 | Named, 577 | Unnamed, 578 | Zst, 579 | } 580 | -------------------------------------------------------------------------------- /src/parse/generics.rs: -------------------------------------------------------------------------------- 1 | use super::utils::*; 2 | use crate::generate::StreamBuilder; 3 | use crate::prelude::{Ident, TokenTree}; 4 | use crate::{Error, Result}; 5 | use std::iter::Peekable; 6 | use std::ops::{Deref, DerefMut}; 7 | 8 | /// A generic parameter for a struct or enum. 9 | /// 10 | /// ``` 11 | /// use std::marker::PhantomData; 12 | /// use std::fmt::Display; 13 | /// 14 | /// // Generic will be `Generic::Generic("F")` 15 | /// struct Foo { 16 | /// f: PhantomData 17 | /// } 18 | /// // Generics will be `Generic::Generic("F: Display")` 19 | /// struct Bar { 20 | /// f: PhantomData 21 | /// } 22 | /// // Generics will be `[Generic::Lifetime("a"), Generic::Generic("F: Display")]` 23 | /// struct Baz<'a, F> { 24 | /// f: PhantomData<&'a F> 25 | /// } 26 | /// ``` 27 | #[derive(Debug, Clone)] 28 | pub struct Generics(pub Vec); 29 | 30 | impl Generics { 31 | pub(crate) fn try_take( 32 | input: &mut Peekable>, 33 | ) -> Result> { 34 | let maybe_punct = input.peek(); 35 | if let Some(TokenTree::Punct(punct)) = maybe_punct { 36 | if punct.as_char() == '<' { 37 | let punct = assume_punct(input.next(), '<'); 38 | let mut result = Generics(Vec::new()); 39 | loop { 40 | match input.peek() { 41 | Some(TokenTree::Punct(punct)) if punct.as_char() == '\'' => { 42 | result.push(Lifetime::take(input)?.into()); 43 | consume_punct_if(input, ','); 44 | } 45 | Some(TokenTree::Punct(punct)) if punct.as_char() == '>' => { 46 | assume_punct(input.next(), '>'); 47 | break; 48 | } 49 | Some(TokenTree::Ident(ident)) if ident_eq(ident, "const") => { 50 | result.push(ConstGeneric::take(input)?.into()); 51 | consume_punct_if(input, ','); 52 | } 53 | Some(TokenTree::Ident(_)) => { 54 | result.push(SimpleGeneric::take(input)?.into()); 55 | consume_punct_if(input, ','); 56 | } 57 | x => { 58 | return Err(Error::InvalidRustSyntax { 59 | span: x.map(|x| x.span()).unwrap_or_else(|| punct.span()), 60 | expected: format!("', > or an ident, got {:?}", x), 61 | }); 62 | } 63 | } 64 | } 65 | return Ok(Some(result)); 66 | } 67 | } 68 | Ok(None) 69 | } 70 | 71 | /// Returns `true` if any of the generics is a [`Generic::Lifetime`] 72 | pub fn has_lifetime(&self) -> bool { 73 | self.iter().any(|lt| lt.is_lifetime()) 74 | } 75 | 76 | /// Returns an iterator which contains only the simple type generics 77 | pub fn iter_generics(&self) -> impl Iterator { 78 | self.iter().filter_map(|g| match g { 79 | Generic::Generic(s) => Some(s), 80 | _ => None, 81 | }) 82 | } 83 | 84 | /// Returns an iterator which contains only the lifetimes 85 | pub fn iter_lifetimes(&self) -> impl Iterator { 86 | self.iter().filter_map(|g| match g { 87 | Generic::Lifetime(s) => Some(s), 88 | _ => None, 89 | }) 90 | } 91 | 92 | /// Returns an iterator which contains only the const generics 93 | pub fn iter_consts(&self) -> impl Iterator { 94 | self.iter().filter_map(|g| match g { 95 | Generic::Const(s) => Some(s), 96 | _ => None, 97 | }) 98 | } 99 | 100 | pub(crate) fn impl_generics(&self) -> StreamBuilder { 101 | let mut result = StreamBuilder::new(); 102 | result.punct('<'); 103 | 104 | for (idx, generic) in self.iter().enumerate() { 105 | if idx > 0 { 106 | result.punct(','); 107 | } 108 | 109 | generic.append_to_result_with_constraints(&mut result); 110 | } 111 | 112 | result.punct('>'); 113 | 114 | result 115 | } 116 | 117 | pub(crate) fn impl_generics_with_additional( 118 | &self, 119 | lifetimes: &[String], 120 | types: &[String], 121 | ) -> StreamBuilder { 122 | let mut result = StreamBuilder::new(); 123 | result.punct('<'); 124 | let mut is_first = true; 125 | for lt in lifetimes.iter() { 126 | if !is_first { 127 | result.punct(','); 128 | } else { 129 | is_first = false; 130 | } 131 | result.lifetime_str(lt); 132 | } 133 | 134 | for generic in self.iter() { 135 | if !is_first { 136 | result.punct(','); 137 | } else { 138 | is_first = false; 139 | } 140 | generic.append_to_result_with_constraints(&mut result); 141 | } 142 | for ty in types { 143 | if !is_first { 144 | result.punct(','); 145 | } else { 146 | is_first = false; 147 | } 148 | result.ident_str(ty); 149 | } 150 | 151 | result.punct('>'); 152 | 153 | result 154 | } 155 | 156 | pub(crate) fn type_generics(&self) -> StreamBuilder { 157 | let mut result = StreamBuilder::new(); 158 | result.punct('<'); 159 | 160 | for (idx, generic) in self.iter().enumerate() { 161 | if idx > 0 { 162 | result.punct(','); 163 | } 164 | if generic.is_lifetime() { 165 | result.lifetime(generic.ident().clone()); 166 | } else { 167 | result.ident(generic.ident().clone()); 168 | } 169 | } 170 | 171 | result.punct('>'); 172 | result 173 | } 174 | } 175 | 176 | impl Deref for Generics { 177 | type Target = Vec; 178 | 179 | fn deref(&self) -> &Self::Target { 180 | &self.0 181 | } 182 | } 183 | 184 | impl DerefMut for Generics { 185 | fn deref_mut(&mut self) -> &mut Self::Target { 186 | &mut self.0 187 | } 188 | } 189 | 190 | /// A single generic argument on a type 191 | #[derive(Debug, Clone)] 192 | #[allow(clippy::enum_variant_names)] 193 | #[non_exhaustive] 194 | pub enum Generic { 195 | /// A lifetime generic 196 | /// 197 | /// ``` 198 | /// # use std::marker::PhantomData; 199 | /// struct Foo<'a> { // will be Generic::Lifetime("a") 200 | /// # a: PhantomData<&'a ()>, 201 | /// } 202 | /// ``` 203 | Lifetime(Lifetime), 204 | /// A simple generic 205 | /// 206 | /// ``` 207 | /// # use std::marker::PhantomData; 208 | /// struct Foo { // will be Generic::Generic("F") 209 | /// # a: PhantomData, 210 | /// } 211 | /// ``` 212 | Generic(SimpleGeneric), 213 | /// A const generic 214 | /// 215 | /// ``` 216 | /// struct Foo { // will be Generic::Const("N") 217 | /// # a: [u8; N], 218 | /// } 219 | /// ``` 220 | Const(ConstGeneric), 221 | } 222 | 223 | impl Generic { 224 | fn is_lifetime(&self) -> bool { 225 | matches!(self, Generic::Lifetime(_)) 226 | } 227 | 228 | /// The ident of this generic 229 | pub fn ident(&self) -> &Ident { 230 | match self { 231 | Self::Lifetime(lt) => <.ident, 232 | Self::Generic(gen) => &gen.ident, 233 | Self::Const(gen) => &gen.ident, 234 | } 235 | } 236 | 237 | fn has_constraints(&self) -> bool { 238 | match self { 239 | Self::Lifetime(lt) => !lt.constraint.is_empty(), 240 | Self::Generic(gen) => !gen.constraints.is_empty(), 241 | Self::Const(_) => true, // const generics always have a constraint 242 | } 243 | } 244 | 245 | fn constraints(&self) -> Vec { 246 | match self { 247 | Self::Lifetime(lt) => lt.constraint.clone(), 248 | Self::Generic(gen) => gen.constraints.clone(), 249 | Self::Const(gen) => gen.constraints.clone(), 250 | } 251 | } 252 | 253 | fn append_to_result_with_constraints(&self, builder: &mut StreamBuilder) { 254 | match self { 255 | Self::Lifetime(lt) => builder.lifetime(lt.ident.clone()), 256 | Self::Generic(gen) => builder.ident(gen.ident.clone()), 257 | Self::Const(gen) => { 258 | builder.ident(gen.const_token.clone()); 259 | builder.ident(gen.ident.clone()) 260 | } 261 | }; 262 | if self.has_constraints() { 263 | builder.punct(':'); 264 | builder.extend(self.constraints()); 265 | } 266 | } 267 | } 268 | 269 | impl From for Generic { 270 | fn from(lt: Lifetime) -> Self { 271 | Self::Lifetime(lt) 272 | } 273 | } 274 | 275 | impl From for Generic { 276 | fn from(gen: SimpleGeneric) -> Self { 277 | Self::Generic(gen) 278 | } 279 | } 280 | 281 | impl From for Generic { 282 | fn from(gen: ConstGeneric) -> Self { 283 | Self::Const(gen) 284 | } 285 | } 286 | 287 | #[test] 288 | fn test_generics_try_take() { 289 | use crate::token_stream; 290 | 291 | assert!(Generics::try_take(&mut token_stream("")).unwrap().is_none()); 292 | assert!(Generics::try_take(&mut token_stream("foo")) 293 | .unwrap() 294 | .is_none()); 295 | assert!(Generics::try_take(&mut token_stream("()")) 296 | .unwrap() 297 | .is_none()); 298 | 299 | let stream = &mut token_stream("struct Foo<'a, T>()"); 300 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 301 | assert_eq!(data_type, super::DataType::Struct); 302 | assert_eq!(ident, "Foo"); 303 | let generics = Generics::try_take(stream).unwrap().unwrap(); 304 | assert_eq!(generics.len(), 2); 305 | assert_eq!(generics[0].ident(), "a"); 306 | assert_eq!(generics[1].ident(), "T"); 307 | 308 | let stream = &mut token_stream("struct Foo()"); 309 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 310 | assert_eq!(data_type, super::DataType::Struct); 311 | assert_eq!(ident, "Foo"); 312 | let generics = Generics::try_take(stream).unwrap().unwrap(); 313 | assert_eq!(generics.len(), 2); 314 | assert_eq!(generics[0].ident(), "A"); 315 | assert_eq!(generics[1].ident(), "B"); 316 | 317 | let stream = &mut token_stream("struct Foo<'a, T: Display>()"); 318 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 319 | assert_eq!(data_type, super::DataType::Struct); 320 | assert_eq!(ident, "Foo"); 321 | let generics = Generics::try_take(stream).unwrap().unwrap(); 322 | dbg!(&generics); 323 | assert_eq!(generics.len(), 2); 324 | assert_eq!(generics[0].ident(), "a"); 325 | assert_eq!(generics[1].ident(), "T"); 326 | 327 | let stream = &mut token_stream("struct Foo<'a, T: for<'a> Bar<'a> + 'static>()"); 328 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 329 | assert_eq!(data_type, super::DataType::Struct); 330 | assert_eq!(ident, "Foo"); 331 | dbg!(&generics); 332 | assert_eq!(generics.len(), 2); 333 | assert_eq!(generics[0].ident(), "a"); 334 | assert_eq!(generics[1].ident(), "T"); 335 | 336 | let stream = &mut token_stream( 337 | "struct Baz Bar<'a, for<'b> Bar<'b, for<'c> Bar<'c, u32>>>> {}", 338 | ); 339 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 340 | assert_eq!(data_type, super::DataType::Struct); 341 | assert_eq!(ident, "Baz"); 342 | let generics = Generics::try_take(stream).unwrap().unwrap(); 343 | dbg!(&generics); 344 | assert_eq!(generics.len(), 1); 345 | assert_eq!(generics[0].ident(), "T"); 346 | 347 | let stream = &mut token_stream("struct Baz<()> {}"); 348 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 349 | assert_eq!(data_type, super::DataType::Struct); 350 | assert_eq!(ident, "Baz"); 351 | assert!(Generics::try_take(stream) 352 | .unwrap_err() 353 | .is_invalid_rust_syntax()); 354 | 355 | let stream = &mut token_stream("struct Bar SomeStruct, B>"); 356 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 357 | assert_eq!(data_type, super::DataType::Struct); 358 | assert_eq!(ident, "Bar"); 359 | let generics = Generics::try_take(stream).unwrap().unwrap(); 360 | dbg!(&generics); 361 | assert_eq!(generics.len(), 2); 362 | assert_eq!(generics[0].ident(), "A"); 363 | assert_eq!(generics[1].ident(), "B"); 364 | 365 | let stream = &mut token_stream("struct Bar"); 366 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 367 | assert_eq!(data_type, super::DataType::Struct); 368 | assert_eq!(ident, "Bar"); 369 | let generics = Generics::try_take(stream).unwrap().unwrap(); 370 | dbg!(&generics); 371 | assert_eq!(generics.len(), 1); 372 | if let Generic::Generic(generic) = &generics[0] { 373 | assert_eq!(generic.ident, "A"); 374 | assert_eq!(generic.default_value.len(), 1); 375 | assert_eq!(generic.default_value[0].to_string(), "()"); 376 | } else { 377 | panic!("Expected simple generic, got {:?}", generics[0]); 378 | } 379 | } 380 | 381 | /// a lifetime generic parameter, e.g. `struct Foo<'a> { ... }` 382 | #[derive(Debug, Clone)] 383 | pub struct Lifetime { 384 | /// The ident of this lifetime 385 | pub ident: Ident, 386 | /// Any constraints that this lifetime may have 387 | pub constraint: Vec, 388 | } 389 | 390 | impl Lifetime { 391 | pub(crate) fn take(input: &mut Peekable>) -> Result { 392 | let start = assume_punct(input.next(), '\''); 393 | let ident = match input.peek() { 394 | Some(TokenTree::Ident(_)) => assume_ident(input.next()), 395 | Some(t) => return Err(Error::ExpectedIdent(t.span())), 396 | None => return Err(Error::ExpectedIdent(start.span())), 397 | }; 398 | 399 | let mut constraint = Vec::new(); 400 | if let Some(TokenTree::Punct(p)) = input.peek() { 401 | if p.as_char() == ':' { 402 | assume_punct(input.next(), ':'); 403 | constraint = read_tokens_until_punct(input, &[',', '>'])?; 404 | } 405 | } 406 | 407 | Ok(Self { ident, constraint }) 408 | } 409 | 410 | #[cfg(test)] 411 | fn is_ident(&self, s: &str) -> bool { 412 | self.ident == s 413 | } 414 | } 415 | 416 | #[test] 417 | fn test_lifetime_take() { 418 | use crate::token_stream; 419 | use std::panic::catch_unwind; 420 | assert!(Lifetime::take(&mut token_stream("'a")) 421 | .unwrap() 422 | .is_ident("a")); 423 | assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'0"))).is_err()); 424 | assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'("))).is_err()); 425 | assert!(catch_unwind(|| Lifetime::take(&mut token_stream("')"))).is_err()); 426 | assert!(catch_unwind(|| Lifetime::take(&mut token_stream("'0'"))).is_err()); 427 | 428 | let stream = &mut token_stream("'a: 'b>"); 429 | let lifetime = Lifetime::take(stream).unwrap(); 430 | assert_eq!(lifetime.ident, "a"); 431 | assert_eq!(lifetime.constraint.len(), 2); 432 | assume_punct(stream.next(), '>'); 433 | assert!(stream.next().is_none()); 434 | } 435 | 436 | /// a simple generic parameter, e.g. `struct Foo { .. }` 437 | #[derive(Debug, Clone)] 438 | #[non_exhaustive] 439 | pub struct SimpleGeneric { 440 | /// The ident of this generic 441 | pub ident: Ident, 442 | /// The constraints of this generic, e.g. `F: SomeTrait` 443 | pub constraints: Vec, 444 | /// The default value of this generic, e.g. `F = ()` 445 | pub default_value: Vec, 446 | } 447 | 448 | impl SimpleGeneric { 449 | pub(crate) fn take(input: &mut Peekable>) -> Result { 450 | let ident = assume_ident(input.next()); 451 | let mut constraints = Vec::new(); 452 | let mut default_value = Vec::new(); 453 | if let Some(TokenTree::Punct(punct)) = input.peek() { 454 | let punct_char = punct.as_char(); 455 | if punct_char == ':' { 456 | assume_punct(input.next(), ':'); 457 | constraints = read_tokens_until_punct(input, &['>', ','])?; 458 | } 459 | if punct_char == '=' { 460 | assume_punct(input.next(), '='); 461 | default_value = read_tokens_until_punct(input, &['>', ','])?; 462 | } 463 | } 464 | Ok(Self { 465 | ident, 466 | constraints, 467 | default_value, 468 | }) 469 | } 470 | 471 | /// The name of this generic, e.g. `T` 472 | pub fn name(&self) -> Ident { 473 | self.ident.clone() 474 | } 475 | } 476 | 477 | /// a const generic parameter, e.g. `struct Foo { .. }` 478 | #[derive(Debug, Clone)] 479 | pub struct ConstGeneric { 480 | /// The `const` token for this generic 481 | pub const_token: Ident, 482 | /// The ident of this generic 483 | pub ident: Ident, 484 | /// The "constraints" (type) of this generic, e.g. the `usize` from `const N: usize` 485 | pub constraints: Vec, 486 | } 487 | 488 | impl ConstGeneric { 489 | pub(crate) fn take(input: &mut Peekable>) -> Result { 490 | let const_token = assume_ident(input.next()); 491 | let ident = assume_ident(input.next()); 492 | let mut constraints = Vec::new(); 493 | if let Some(TokenTree::Punct(punct)) = input.peek() { 494 | if punct.as_char() == ':' { 495 | assume_punct(input.next(), ':'); 496 | constraints = read_tokens_until_punct(input, &['>', ','])?; 497 | } 498 | } 499 | Ok(Self { 500 | const_token, 501 | ident, 502 | constraints, 503 | }) 504 | } 505 | } 506 | 507 | /// Constraints on generic types. 508 | /// 509 | /// ``` 510 | /// # use std::marker::PhantomData; 511 | /// # use std::fmt::Display; 512 | /// 513 | /// struct Foo 514 | /// where F: Display // These are `GenericConstraints` 515 | /// { 516 | /// f: PhantomData 517 | /// } 518 | #[derive(Debug, Clone, Default)] 519 | pub struct GenericConstraints { 520 | constraints: Vec, 521 | } 522 | 523 | impl GenericConstraints { 524 | pub(crate) fn try_take( 525 | input: &mut Peekable>, 526 | ) -> Result> { 527 | match input.peek() { 528 | Some(TokenTree::Ident(ident)) => { 529 | if !ident_eq(ident, "where") { 530 | return Ok(None); 531 | } 532 | } 533 | _ => { 534 | return Ok(None); 535 | } 536 | } 537 | input.next(); 538 | let constraints = read_tokens_until_punct(input, &['{', '('])?; 539 | Ok(Some(Self { constraints })) 540 | } 541 | 542 | pub(crate) fn where_clause(&self) -> StreamBuilder { 543 | let mut result = StreamBuilder::new(); 544 | result.ident_str("where"); 545 | result.extend(self.constraints.clone()); 546 | result 547 | } 548 | 549 | /// Push the given constraint onto this stream. 550 | /// 551 | /// ```ignore 552 | /// let mut generic_constraints = GenericConstraints::parse("T: Foo"); // imaginary function 553 | /// let mut generic = SimpleGeneric::new("U"); // imaginary function 554 | /// 555 | /// generic_constraints.push_constraint(&generic, "Bar"); 556 | /// 557 | /// // generic_constraints is now: 558 | /// // `T: Foo, U: Bar` 559 | /// ``` 560 | pub fn push_constraint( 561 | &mut self, 562 | generic: &SimpleGeneric, 563 | constraint: impl AsRef, 564 | ) -> Result<()> { 565 | let mut builder = StreamBuilder::new(); 566 | let last_constraint_was_comma = self.constraints.last().map_or( 567 | false, 568 | |l| matches!(l, TokenTree::Punct(c) if c.as_char() == ','), 569 | ); 570 | if !self.constraints.is_empty() && !last_constraint_was_comma { 571 | builder.punct(','); 572 | } 573 | builder.ident(generic.ident.clone()); 574 | builder.punct(':'); 575 | builder.push_parsed(constraint)?; 576 | self.constraints.extend(builder.stream); 577 | 578 | Ok(()) 579 | } 580 | 581 | /// Push the given constraint onto this stream. 582 | /// 583 | /// ```ignore 584 | /// let mut generic_constraints = GenericConstraints::parse("T: Foo"); // imaginary function 585 | /// 586 | /// generic_constraints.push_parsed_constraint("u32: SomeTrait"); 587 | /// 588 | /// // generic_constraints is now: 589 | /// // `T: Foo, u32: SomeTrait` 590 | /// ``` 591 | pub fn push_parsed_constraint(&mut self, constraint: impl AsRef) -> Result<()> { 592 | let mut builder = StreamBuilder::new(); 593 | if !self.constraints.is_empty() { 594 | builder.punct(','); 595 | } 596 | builder.push_parsed(constraint)?; 597 | self.constraints.extend(builder.stream); 598 | 599 | Ok(()) 600 | } 601 | 602 | /// Clear the constraints 603 | pub fn clear(&mut self) { 604 | self.constraints.clear(); 605 | } 606 | } 607 | 608 | #[test] 609 | fn test_generic_constraints_try_take() { 610 | use super::{DataType, StructBody, Visibility}; 611 | use crate::parse::body::Fields; 612 | use crate::token_stream; 613 | 614 | let stream = &mut token_stream("struct Foo where Foo: Bar { }"); 615 | DataType::take(stream).unwrap(); 616 | assert!(GenericConstraints::try_take(stream).unwrap().is_some()); 617 | 618 | let stream = &mut token_stream("struct Foo { }"); 619 | DataType::take(stream).unwrap(); 620 | assert!(GenericConstraints::try_take(stream).unwrap().is_none()); 621 | 622 | let stream = &mut token_stream("struct Foo where Foo: Bar(Foo)"); 623 | DataType::take(stream).unwrap(); 624 | assert!(GenericConstraints::try_take(stream).unwrap().is_some()); 625 | 626 | let stream = &mut token_stream("struct Foo()"); 627 | DataType::take(stream).unwrap(); 628 | assert!(GenericConstraints::try_take(stream).unwrap().is_none()); 629 | 630 | let stream = &mut token_stream("struct Foo()"); 631 | assert!(GenericConstraints::try_take(stream).unwrap().is_none()); 632 | 633 | let stream = &mut token_stream("{}"); 634 | assert!(GenericConstraints::try_take(stream).unwrap().is_none()); 635 | 636 | let stream = &mut token_stream(""); 637 | assert!(GenericConstraints::try_take(stream).unwrap().is_none()); 638 | 639 | let stream = &mut token_stream("pub(crate) struct Test {}"); 640 | assert_eq!(Visibility::Pub, Visibility::try_take(stream).unwrap()); 641 | let (data_type, ident) = DataType::take(stream).unwrap(); 642 | assert_eq!(data_type, DataType::Struct); 643 | assert_eq!(ident, "Test"); 644 | let constraints = Generics::try_take(stream).unwrap().unwrap(); 645 | assert_eq!(constraints.len(), 1); 646 | assert_eq!(constraints[0].ident(), "T"); 647 | let body = StructBody::take(stream).unwrap(); 648 | if let Some(Fields::Struct(v)) = body.fields { 649 | assert!(v.is_empty()); 650 | } else { 651 | panic!("wrong fields {:?}", body.fields); 652 | } 653 | } 654 | 655 | #[test] 656 | fn test_generic_constraints_trailing_comma() { 657 | use crate::parse::{ 658 | Attribute, AttributeLocation, DataType, GenericConstraints, Generics, StructBody, 659 | Visibility, 660 | }; 661 | use crate::token_stream; 662 | let source = &mut token_stream("pub struct MyStruct where T: Clone, { }"); 663 | 664 | Attribute::try_take(AttributeLocation::Container, source).unwrap(); 665 | Visibility::try_take(source).unwrap(); 666 | DataType::take(source).unwrap(); 667 | Generics::try_take(source).unwrap().unwrap(); 668 | GenericConstraints::try_take(source).unwrap().unwrap(); 669 | StructBody::take(source).unwrap(); 670 | } 671 | -------------------------------------------------------------------------------- /src/parse/body.rs: -------------------------------------------------------------------------------- 1 | use super::attributes::AttributeLocation; 2 | use super::{utils::*, Attribute, Visibility}; 3 | use crate::prelude::{Delimiter, Ident, Literal, Span, TokenTree}; 4 | use crate::{Error, Result}; 5 | use std::iter::Peekable; 6 | 7 | /// The body of a struct 8 | #[derive(Debug)] 9 | pub struct StructBody { 10 | /// The fields of this struct, `None` if this struct has no fields 11 | pub fields: Option, 12 | } 13 | 14 | impl StructBody { 15 | pub(crate) fn take(input: &mut Peekable>) -> Result { 16 | match input.peek() { 17 | Some(TokenTree::Group(_)) => {} 18 | Some(TokenTree::Punct(p)) if p.as_char() == ';' => { 19 | return Ok(StructBody { fields: None }) 20 | } 21 | token => return Error::wrong_token(token, "group or punct"), 22 | } 23 | let group = assume_group(input.next()); 24 | let mut stream = group.stream().into_iter().peekable(); 25 | let fields = match group.delimiter() { 26 | Delimiter::Brace => { 27 | let fields = UnnamedField::parse_with_name(&mut stream)?; 28 | Some(Fields::Struct(fields)) 29 | } 30 | Delimiter::Parenthesis => { 31 | let fields = UnnamedField::parse(&mut stream)?; 32 | Some(Fields::Tuple(fields)) 33 | } 34 | found => { 35 | return Err(Error::InvalidRustSyntax { 36 | span: group.span(), 37 | expected: format!("brace or parenthesis, found {:?}", found), 38 | }) 39 | } 40 | }; 41 | Ok(StructBody { fields }) 42 | } 43 | } 44 | 45 | #[test] 46 | fn test_struct_body_take() { 47 | use crate::token_stream; 48 | 49 | let stream = &mut token_stream( 50 | "struct Foo { pub bar: u8, pub(crate) baz: u32, bla: Vec>> }", 51 | ); 52 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 53 | assert_eq!(data_type, super::DataType::Struct); 54 | assert_eq!(ident, "Foo"); 55 | let body = StructBody::take(stream).unwrap(); 56 | let fields = body.fields.as_ref().unwrap(); 57 | 58 | assert_eq!(fields.len(), 3); 59 | let (ident, field) = fields.get(0).unwrap(); 60 | assert_eq!(ident.unwrap(), "bar"); 61 | assert_eq!(field.vis, Visibility::Pub); 62 | assert_eq!(field.type_string(), "u8"); 63 | 64 | let (ident, field) = fields.get(1).unwrap(); 65 | assert_eq!(ident.unwrap(), "baz"); 66 | assert_eq!(field.vis, Visibility::Pub); 67 | assert_eq!(field.type_string(), "u32"); 68 | 69 | let (ident, field) = fields.get(2).unwrap(); 70 | assert_eq!(ident.unwrap(), "bla"); 71 | assert_eq!(field.vis, Visibility::Default); 72 | assert_eq!(field.type_string(), "Vec>>"); 73 | 74 | let stream = &mut token_stream( 75 | "struct Foo ( pub u8, pub(crate) u32, Vec>> )", 76 | ); 77 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 78 | assert_eq!(data_type, super::DataType::Struct); 79 | assert_eq!(ident, "Foo"); 80 | let body = StructBody::take(stream).unwrap(); 81 | let fields = body.fields.as_ref().unwrap(); 82 | 83 | assert_eq!(fields.len(), 3); 84 | 85 | let (ident, field) = fields.get(0).unwrap(); 86 | assert!(ident.is_none()); 87 | assert_eq!(field.vis, Visibility::Pub); 88 | assert_eq!(field.type_string(), "u8"); 89 | 90 | let (ident, field) = fields.get(1).unwrap(); 91 | assert!(ident.is_none()); 92 | assert_eq!(field.vis, Visibility::Pub); 93 | assert_eq!(field.type_string(), "u32"); 94 | 95 | let (ident, field) = fields.get(2).unwrap(); 96 | assert!(ident.is_none()); 97 | assert_eq!(field.vis, Visibility::Default); 98 | assert_eq!(field.type_string(), "Vec>>"); 99 | 100 | let stream = &mut token_stream("struct Foo;"); 101 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 102 | assert_eq!(data_type, super::DataType::Struct); 103 | assert_eq!(ident, "Foo"); 104 | let body = StructBody::take(stream).unwrap(); 105 | assert!(body.fields.is_none()); 106 | 107 | let stream = &mut token_stream("struct Foo {}"); 108 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 109 | assert_eq!(data_type, super::DataType::Struct); 110 | assert_eq!(ident, "Foo"); 111 | let body = StructBody::take(stream).unwrap(); 112 | if let Some(Fields::Struct(v)) = body.fields { 113 | assert!(v.is_empty()); 114 | } else { 115 | panic!("wrong fields {:?}", body.fields); 116 | } 117 | 118 | let stream = &mut token_stream("struct Foo ()"); 119 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 120 | assert_eq!(data_type, super::DataType::Struct); 121 | assert_eq!(ident, "Foo"); 122 | let body = StructBody::take(stream).unwrap(); 123 | if let Some(Fields::Tuple(v)) = body.fields { 124 | assert!(v.is_empty()); 125 | } else { 126 | panic!("wrong fields {:?}", body.fields); 127 | } 128 | } 129 | 130 | #[test] 131 | fn issue_77() { 132 | // https://github.com/bincode-org/virtue/issues/77 133 | use crate::token_stream; 134 | 135 | let stream = &mut token_stream("struct Test(pub [u8; 32])"); 136 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 137 | assert_eq!(data_type, super::DataType::Struct); 138 | assert_eq!(ident, "Test"); 139 | let body = StructBody::take(stream).unwrap(); 140 | let fields = body.fields.unwrap(); 141 | let Fields::Tuple(t) = fields else { 142 | panic!("Fields is not a tuple") 143 | }; 144 | assert_eq!(t.len(), 1); 145 | assert_eq!(t[0].r#type[0].to_string(), "[u8 ; 32]"); 146 | 147 | let stream = &mut token_stream("struct Foo(pub (u8, ))"); 148 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 149 | assert_eq!(data_type, super::DataType::Struct); 150 | assert_eq!(ident, "Foo"); 151 | let body = StructBody::take(stream).unwrap(); 152 | let fields = body.fields.unwrap(); 153 | let Fields::Tuple(t) = fields else { 154 | panic!("Fields is not a tuple") 155 | }; 156 | assert_eq!(t.len(), 1); 157 | assert_eq!(t[0].r#type[0].to_string(), "(u8 ,)"); 158 | } 159 | 160 | /// The body of an enum 161 | #[derive(Debug)] 162 | pub struct EnumBody { 163 | /// The enum's variants 164 | pub variants: Vec, 165 | } 166 | 167 | impl EnumBody { 168 | pub(crate) fn take(input: &mut Peekable>) -> Result { 169 | match input.peek() { 170 | Some(TokenTree::Group(_)) => {} 171 | Some(TokenTree::Punct(p)) if p.as_char() == ';' => { 172 | return Ok(EnumBody { 173 | variants: Vec::new(), 174 | }) 175 | } 176 | token => return Error::wrong_token(token, "group or ;"), 177 | } 178 | let group = assume_group(input.next()); 179 | let mut variants = Vec::new(); 180 | let stream = &mut group.stream().into_iter().peekable(); 181 | while stream.peek().is_some() { 182 | let attributes = Attribute::try_take(AttributeLocation::Variant, stream)?; 183 | let ident = match super::utils::consume_ident(stream) { 184 | Some(ident) => ident, 185 | None => Error::wrong_token(stream.peek(), "ident")?, 186 | }; 187 | 188 | let mut fields = None; 189 | let mut value = None; 190 | 191 | if let Some(TokenTree::Group(_)) = stream.peek() { 192 | let group = assume_group(stream.next()); 193 | let stream = &mut group.stream().into_iter().peekable(); 194 | match group.delimiter() { 195 | Delimiter::Brace => { 196 | fields = Some(Fields::Struct(UnnamedField::parse_with_name(stream)?)); 197 | } 198 | Delimiter::Parenthesis => { 199 | fields = Some(Fields::Tuple(UnnamedField::parse(stream)?)); 200 | } 201 | delim => { 202 | return Err(Error::InvalidRustSyntax { 203 | span: group.span(), 204 | expected: format!("Brace or parenthesis, found {:?}", delim), 205 | }) 206 | } 207 | } 208 | } 209 | match stream.peek() { 210 | Some(TokenTree::Punct(p)) if p.as_char() == '=' => { 211 | assume_punct(stream.next(), '='); 212 | match stream.next() { 213 | Some(TokenTree::Literal(lit)) => { 214 | value = Some(lit); 215 | } 216 | Some(TokenTree::Punct(p)) if p.as_char() == '-' => match stream.next() { 217 | Some(TokenTree::Literal(lit)) => { 218 | match lit.to_string().parse::() { 219 | Ok(val) => value = Some(Literal::i64_unsuffixed(-val)), 220 | Err(_) => { 221 | return Err(Error::custom_at( 222 | "parse:: failed", 223 | lit.span(), 224 | )) 225 | } 226 | }; 227 | } 228 | token => return Error::wrong_token(token.as_ref(), "literal"), 229 | }, 230 | token => return Error::wrong_token(token.as_ref(), "literal"), 231 | } 232 | } 233 | Some(TokenTree::Punct(p)) if p.as_char() == ',' => { 234 | // next field 235 | } 236 | None => { 237 | // group done 238 | } 239 | token => return Error::wrong_token(token, "group, comma or ="), 240 | } 241 | 242 | consume_punct_if(stream, ','); 243 | 244 | variants.push(EnumVariant { 245 | name: ident, 246 | fields, 247 | value, 248 | attributes, 249 | }); 250 | } 251 | 252 | Ok(EnumBody { variants }) 253 | } 254 | } 255 | 256 | #[test] 257 | fn test_enum_body_take() { 258 | use crate::token_stream; 259 | 260 | let stream = &mut token_stream("enum Foo { }"); 261 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 262 | assert_eq!(data_type, super::DataType::Enum); 263 | assert_eq!(ident, "Foo"); 264 | let body = EnumBody::take(stream).unwrap(); 265 | assert!(body.variants.is_empty()); 266 | 267 | let stream = &mut token_stream("enum Foo { Bar, Baz(u8), Blah { a: u32, b: u128 } }"); 268 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 269 | assert_eq!(data_type, super::DataType::Enum); 270 | assert_eq!(ident, "Foo"); 271 | let body = EnumBody::take(stream).unwrap(); 272 | assert_eq!(3, body.variants.len()); 273 | 274 | assert_eq!(body.variants[0].name, "Bar"); 275 | assert!(body.variants[0].fields.is_none()); 276 | 277 | assert_eq!(body.variants[1].name, "Baz"); 278 | assert!(body.variants[1].fields.is_some()); 279 | let fields = body.variants[1].fields.as_ref().unwrap(); 280 | assert_eq!(1, fields.len()); 281 | let (ident, field) = fields.get(0).unwrap(); 282 | assert!(ident.is_none()); 283 | assert_eq!(field.type_string(), "u8"); 284 | 285 | assert_eq!(body.variants[2].name, "Blah"); 286 | assert!(body.variants[2].fields.is_some()); 287 | let fields = body.variants[2].fields.as_ref().unwrap(); 288 | assert_eq!(2, fields.len()); 289 | let (ident, field) = fields.get(0).unwrap(); 290 | assert_eq!(ident.unwrap(), "a"); 291 | assert_eq!(field.type_string(), "u32"); 292 | let (ident, field) = fields.get(1).unwrap(); 293 | assert_eq!(ident.unwrap(), "b"); 294 | assert_eq!(field.type_string(), "u128"); 295 | 296 | let stream = &mut token_stream("enum Foo { Bar = -1, Baz = 2 }"); 297 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 298 | assert_eq!(data_type, super::DataType::Enum); 299 | assert_eq!(ident, "Foo"); 300 | let body = EnumBody::take(stream).unwrap(); 301 | assert_eq!(2, body.variants.len()); 302 | 303 | assert_eq!(body.variants[0].name, "Bar"); 304 | assert!(body.variants[0].fields.is_none()); 305 | assert_eq!(body.variants[0].get_integer(), -1); 306 | 307 | assert_eq!(body.variants[1].name, "Baz"); 308 | assert!(body.variants[1].fields.is_none()); 309 | assert_eq!(body.variants[1].get_integer(), 2); 310 | 311 | let stream = &mut token_stream("enum Foo { Bar(i32) = -1, Baz { a: i32 } = 2 }"); 312 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 313 | assert_eq!(data_type, super::DataType::Enum); 314 | assert_eq!(ident, "Foo"); 315 | let body = EnumBody::take(stream).unwrap(); 316 | assert_eq!(2, body.variants.len()); 317 | 318 | assert_eq!(body.variants[0].name, "Bar"); 319 | assert!(body.variants[0].fields.is_some()); 320 | let fields = body.variants[0].fields.as_ref().unwrap(); 321 | assert_eq!(fields.len(), 1); 322 | assert!(matches!(fields.names()[0], IdentOrIndex::Index { index, .. } if index == 0)); 323 | assert_eq!(body.variants[0].get_integer(), -1); 324 | 325 | assert_eq!(body.variants[1].name, "Baz"); 326 | assert!(body.variants[1].fields.is_some()); 327 | let fields = body.variants[1].fields.as_ref().unwrap(); 328 | assert_eq!(fields.len(), 1); 329 | assert_eq!(fields.names().len(), 1); 330 | assert!(matches!(&fields.names()[0], IdentOrIndex::Ident { ident, .. } if *ident == "a")); 331 | assert_eq!(body.variants[1].get_integer(), 2); 332 | 333 | let stream = &mut token_stream("enum Foo { Round(), Curly{}, Without }"); 334 | let (data_type, ident) = super::DataType::take(stream).unwrap(); 335 | assert_eq!(data_type, super::DataType::Enum); 336 | assert_eq!(ident, "Foo"); 337 | let body = EnumBody::take(stream).unwrap(); 338 | assert_eq!(3, body.variants.len()); 339 | 340 | assert_eq!(body.variants[0].name, "Round"); 341 | assert!(body.variants[0].fields.is_some()); 342 | let fields = body.variants[0].fields.as_ref().unwrap(); 343 | assert!(fields.names().is_empty()); 344 | assert_eq!(fields.len(), 0); 345 | 346 | assert_eq!(body.variants[1].name, "Curly"); 347 | assert!(body.variants[1].fields.is_some()); 348 | let fields = body.variants[1].fields.as_ref().unwrap(); 349 | assert!(fields.names().is_empty()); 350 | assert_eq!(fields.len(), 0); 351 | 352 | assert_eq!(body.variants[2].name, "Without"); 353 | assert!(body.variants[2].fields.is_none()); 354 | } 355 | 356 | /// A variant of an enum 357 | #[derive(Debug)] 358 | pub struct EnumVariant { 359 | /// The name of the variant 360 | pub name: Ident, 361 | /// The field of the variant. See [`Fields`] for more info 362 | pub fields: Option, 363 | /// The value of this variant. This can be one of: 364 | /// - `Baz = 5` 365 | /// - `Baz(i32) = 5` 366 | /// - `Baz { a: i32} = 5` 367 | /// 368 | /// In either case this value will be `Some(Literal::i32(5))` 369 | pub value: Option, 370 | /// The attributes of this variant 371 | pub attributes: Vec, 372 | } 373 | 374 | #[cfg(test)] 375 | impl EnumVariant { 376 | fn get_integer(&self) -> i64 { 377 | let value = self.value.as_ref().expect("Variant has no value"); 378 | value 379 | .to_string() 380 | .parse() 381 | .expect("Value is not a valid integer") 382 | } 383 | } 384 | 385 | /// The different field types an enum variant can have. 386 | #[derive(Debug)] 387 | pub enum Fields { 388 | /// Tuple-like variant 389 | /// ```rs 390 | /// enum Foo { 391 | /// Baz(u32) 392 | /// } 393 | /// struct Bar(u32); 394 | /// ``` 395 | Tuple(Vec), 396 | 397 | /// Struct-like variant 398 | /// ```rs 399 | /// enum Foo { 400 | /// Baz { 401 | /// baz: u32 402 | /// } 403 | /// } 404 | /// struct Bar { 405 | /// baz: u32 406 | /// } 407 | /// ``` 408 | Struct(Vec<(Ident, UnnamedField)>), 409 | } 410 | 411 | impl Fields { 412 | /// Returns a list of names for the variant. 413 | /// 414 | /// ``` 415 | /// enum Foo { 416 | /// C(u32, u32), // will return `vec[Index { index: 0 }, Index { index: 1 }]` 417 | /// D { a: u32, b: u32 }, // will return `vec[Ident { ident: "a" }, Ident { ident: "b" }]` 418 | /// } 419 | pub fn names(&self) -> Vec { 420 | let result: Vec = match self { 421 | Self::Tuple(fields) => fields 422 | .iter() 423 | .enumerate() 424 | .map(|(index, field)| IdentOrIndex::Index { 425 | index, 426 | span: field.span(), 427 | attributes: field.attributes.clone(), 428 | }) 429 | .collect(), 430 | Self::Struct(fields) => fields 431 | .iter() 432 | .map(|(ident, field)| IdentOrIndex::Ident { 433 | ident: ident.clone(), 434 | attributes: field.attributes.clone(), 435 | }) 436 | .collect(), 437 | }; 438 | result 439 | } 440 | 441 | /// Return the delimiter of the group for this variant 442 | /// 443 | /// ``` 444 | /// enum Foo { 445 | /// C(u32, u32), // will return `Delimiter::Paranthesis` 446 | /// D { a: u32, b: u32 }, // will return `Delimiter::Brace` 447 | /// } 448 | /// ``` 449 | pub fn delimiter(&self) -> Delimiter { 450 | match self { 451 | Self::Tuple(_) => Delimiter::Parenthesis, 452 | Self::Struct(_) => Delimiter::Brace, 453 | } 454 | } 455 | } 456 | 457 | #[cfg(test)] 458 | impl Fields { 459 | fn len(&self) -> usize { 460 | match self { 461 | Self::Tuple(fields) => fields.len(), 462 | Self::Struct(fields) => fields.len(), 463 | } 464 | } 465 | 466 | fn get(&self, index: usize) -> Option<(Option<&Ident>, &UnnamedField)> { 467 | match self { 468 | Self::Tuple(fields) => fields.get(index).map(|f| (None, f)), 469 | Self::Struct(fields) => fields.get(index).map(|(ident, field)| (Some(ident), field)), 470 | } 471 | } 472 | } 473 | 474 | /// An unnamed field 475 | #[derive(Debug)] 476 | pub struct UnnamedField { 477 | /// The visibility of the field 478 | pub vis: Visibility, 479 | /// The type of the field 480 | pub r#type: Vec, 481 | /// The attributes of the field 482 | pub attributes: Vec, 483 | } 484 | 485 | impl UnnamedField { 486 | pub(crate) fn parse_with_name( 487 | input: &mut Peekable>, 488 | ) -> Result> { 489 | let mut result = Vec::new(); 490 | loop { 491 | let attributes = Attribute::try_take(AttributeLocation::Field, input)?; 492 | let vis = Visibility::try_take(input)?; 493 | 494 | let ident = match input.peek() { 495 | Some(TokenTree::Ident(_)) => assume_ident(input.next()), 496 | Some(x) => { 497 | return Err(Error::InvalidRustSyntax { 498 | span: x.span(), 499 | expected: format!("ident or end of group, got {:?}", x), 500 | }) 501 | } 502 | None => break, 503 | }; 504 | match input.peek() { 505 | Some(TokenTree::Punct(p)) if p.as_char() == ':' => { 506 | input.next(); 507 | } 508 | token => return Error::wrong_token(token, ":"), 509 | } 510 | let r#type = read_tokens_until_punct(input, &[','])?; 511 | consume_punct_if(input, ','); 512 | result.push(( 513 | ident, 514 | Self { 515 | vis, 516 | r#type, 517 | attributes, 518 | }, 519 | )); 520 | } 521 | Ok(result) 522 | } 523 | 524 | pub(crate) fn parse( 525 | input: &mut Peekable>, 526 | ) -> Result> { 527 | let mut result = Vec::new(); 528 | while input.peek().is_some() { 529 | let attributes = Attribute::try_take(AttributeLocation::Field, input)?; 530 | let vis = Visibility::try_take(input)?; 531 | 532 | let r#type = read_tokens_until_punct(input, &[','])?; 533 | consume_punct_if(input, ','); 534 | result.push(Self { 535 | vis, 536 | r#type, 537 | attributes, 538 | }); 539 | } 540 | Ok(result) 541 | } 542 | 543 | /// Return [`type`] as a string. Useful for comparing it for known values. 544 | /// 545 | /// [`type`]: #structfield.type 546 | pub fn type_string(&self) -> String { 547 | self.r#type.iter().map(|t| t.to_string()).collect() 548 | } 549 | 550 | /// Return the span of [`type`]. 551 | /// 552 | /// **note**: Until is stable, this will return the first span of the type instead 553 | /// 554 | /// [`type`]: #structfield.type 555 | pub fn span(&self) -> Span { 556 | // BlockedTODO: https://github.com/rust-lang/rust/issues/54725 557 | // Span::join is unstable 558 | // if let Some(first) = self.r#type.first() { 559 | // let mut span = first.span(); 560 | // for token in self.r#type.iter().skip(1) { 561 | // span = span.join(span).unwrap(); 562 | // } 563 | // span 564 | // } else { 565 | // Span::call_site() 566 | // } 567 | 568 | match self.r#type.first() { 569 | Some(first) => first.span(), 570 | None => Span::call_site(), 571 | } 572 | } 573 | } 574 | 575 | /// Reference to an enum variant's field. Either by index or by ident. 576 | /// 577 | /// ``` 578 | /// enum Foo { 579 | /// Bar(u32), // will be IdentOrIndex::Index { index: 0, .. } 580 | /// Baz { 581 | /// a: u32, // will be IdentOrIndex::Ident { ident: "a", .. } 582 | /// }, 583 | /// } 584 | #[derive(Debug, Clone)] 585 | pub enum IdentOrIndex { 586 | /// The variant is a named field 587 | Ident { 588 | /// The name of the field 589 | ident: Ident, 590 | /// The attributes of the field 591 | attributes: Vec, 592 | }, 593 | /// The variant is an unnamed field 594 | Index { 595 | /// The field index 596 | index: usize, 597 | /// The span of the field type 598 | span: Span, 599 | /// The attributes of this field 600 | attributes: Vec, 601 | }, 602 | } 603 | 604 | impl IdentOrIndex { 605 | /// Get the ident. Will panic if this is an `IdentOrIndex::Index` 606 | pub fn unwrap_ident(&self) -> Ident { 607 | match self { 608 | Self::Ident { ident, .. } => ident.clone(), 609 | x => panic!("Expected ident, found {:?}", x), 610 | } 611 | } 612 | 613 | /// Convert this ident into a TokenTree. If this is an `Index`, will return `prefix + index` instead. 614 | pub fn to_token_tree_with_prefix(&self, prefix: &str) -> TokenTree { 615 | TokenTree::Ident(match self { 616 | IdentOrIndex::Ident { ident, .. } => (*ident).clone(), 617 | IdentOrIndex::Index { index, span, .. } => { 618 | let name = format!("{}{}", prefix, index); 619 | Ident::new(&name, *span) 620 | } 621 | }) 622 | } 623 | 624 | /// Return either the index or the ident of this field with a fixed prefix. The prefix will always be added. 625 | pub fn to_string_with_prefix(&self, prefix: &str) -> String { 626 | match self { 627 | IdentOrIndex::Ident { ident, .. } => ident.to_string(), 628 | IdentOrIndex::Index { index, .. } => { 629 | format!("{}{}", prefix, index) 630 | } 631 | } 632 | } 633 | 634 | /// Returns the attributes of this field. 635 | pub fn attributes(&self) -> &Vec { 636 | match self { 637 | Self::Ident { attributes, .. } => attributes, 638 | Self::Index { attributes, .. } => attributes, 639 | } 640 | } 641 | } 642 | 643 | impl std::fmt::Display for IdentOrIndex { 644 | fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { 645 | match self { 646 | IdentOrIndex::Ident { ident, .. } => write!(fmt, "{}", ident), 647 | IdentOrIndex::Index { index, .. } => write!(fmt, "{}", index), 648 | } 649 | } 650 | } 651 | 652 | #[test] 653 | fn enum_explicit_variants() { 654 | use crate::token_stream; 655 | let stream = &mut token_stream("{ A = 1, B = 2 }"); 656 | let body = EnumBody::take(stream).unwrap(); 657 | assert_eq!(body.variants.len(), 2); 658 | } 659 | --------------------------------------------------------------------------------