From 89645027b00fae4de15501f9d8b2617ca9ea176a Mon Sep 17 00:00:00 2001 From: Strixpyrr <24661563+NightEule5@users.noreply.github.com> Date: Fri, 14 Jun 2024 00:37:16 -0600 Subject: [PATCH] Implement attributes for `GenStruct` and `GenEnum` (#87) * Implement attributes for GenStruct and GenEnum * Factor out repeated code * Tweak API * Allow paths in derives * Add method to add attributes as TokenStreams and missing with_parsed_attribute --- src/generate/gen_enum.rs | 308 +++++++++++++++++++++++++++---- src/generate/gen_struct.rs | 201 +++++++++++++++++---- src/generate/mod.rs | 358 ++++++++++++++++++++++++++++++++++++- 3 files changed, 803 insertions(+), 64 deletions(-) diff --git a/src/generate/gen_enum.rs b/src/generate/gen_enum.rs index 1bfbc3a..0a25886 100644 --- a/src/generate/gen_enum.rs +++ b/src/generate/gen_enum.rs @@ -1,6 +1,9 @@ -use super::{Impl, ImplFor, Parent, StreamBuilder, StringOrIdent}; +use super::{ + AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, Path, StreamBuilder, + StringOrIdent, +}; use crate::parse::{Generic, Generics, Visibility}; -use crate::prelude::{Delimiter, Ident, Span}; +use crate::prelude::{Delimiter, Ident, Span, TokenStream}; use crate::Result; /// Builder to generate an `enum { { ... }, ... }` @@ -19,9 +22,9 @@ use crate::Result; /// .add_field("baz", "String"); /// enumgen /// .add_value("Unnamed") +/// .make_tuple() /// .add_field("", "u16") -/// .add_field("baz", "String") -/// .make_tuple(); +/// .add_field("baz", "String"); /// } /// # generator.assert_eq("enum Foo { ZST , Named { bar : u16 , baz : String , } , Unnamed (u16 , String ,) , }"); /// # Ok::<_, virtue::Error>(()) @@ -44,6 +47,8 @@ pub struct GenEnum<'a, P: Parent> { visibility: Visibility, generics: Option, values: Vec, + derives: Vec, + attributes: Vec, additional: Vec, } @@ -55,6 +60,8 @@ impl<'a, P: Parent> GenEnum<'a, P> { visibility: Visibility::Default, generics: None, values: Vec::new(), + derives: Vec::new(), + attributes: Vec::new(), additional: Vec::new(), } } @@ -65,6 +72,133 @@ impl<'a, P: Parent> GenEnum<'a, P> { self } + /// Add a derive macro to the enum. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # use virtue::generate::Path; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_enum("Foo") + /// .with_derive("Clone") + /// .with_derive("Default") + /// .with_derive(Path::from_iter(vec!["serde", "Deserialize"])); + /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] enum Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[derive(Clone, Default, serde::Deserialize)] + /// enum Foo { } + pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { + AttributeContainer::with_derive(self, derive) + } + + /// Add derive macros to the enum. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # use virtue::generate::Path; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_enum("Foo") + /// .with_derives([ + /// "Clone".into(), + /// "Default".into(), + /// Path::from_iter(vec!["serde", "Deserialize"]), + /// ]); + /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] enum Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[derive(Clone, Default, serde::Deserialize)] + /// enum Foo { } + pub fn with_derives>( + &mut self, + derives: impl IntoIterator, + ) -> &mut Self { + AttributeContainer::with_derives(self, derives) + } + + /// Add an attribute to the enum. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) + /// instead. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_enum("Foo") + /// .with_attribute("serde", |b| { + /// b.push_parsed("(untagged)")?; + /// Ok(()) + /// })?; + /// # generator.assert_eq("# [serde (untagged)] enum Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[serde(untagged)] + /// enum Foo { } + /// ``` + pub fn with_attribute( + &mut self, + name: impl AsRef, + value: impl FnOnce(&mut StreamBuilder) -> Result, + ) -> Result<&mut Self> { + AttributeContainer::with_attribute(self, name, value) + } + + /// Add a parsed attribute to the enum. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) + /// instead. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// + /// generator + /// .generate_enum("Foo") + /// .with_parsed_attribute("serde(untagged)")?; + /// # generator.assert_eq("# [serde (untagged)] enum Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[serde(untagged)] + /// enum Foo { } + /// ``` + pub fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> Result<&mut Self> { + AttributeContainer::with_parsed_attribute(self, attribute) + } + + /// Add a token stream as an attribute to the enum. For `#[derive(...)]`, use + /// [`with_derive`](Self::with_derive) instead. + /// + /// ``` + /// # use virtue::prelude::{Generator, TokenStream}; + /// # let mut generator = Generator::with_name("Bar"); + /// + /// let attribute = "serde(untagged)".parse::().unwrap(); + /// generator + /// .generate_enum("Foo") + /// .with_attribute_stream(attribute); + /// # generator.assert_eq("# [serde (untagged)] enum Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[serde(untagged)] + /// enum Foo { } + /// ``` + pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { + AttributeContainer::with_attribute_stream(self, attribute) + } + /// Inherit the generic parameters of the parent type. /// /// ``` @@ -183,6 +317,16 @@ impl<'a, P: Parent> GenEnum<'a, P> { } } +impl AttributeContainer for GenEnum<'_, P> { + fn derives(&mut self) -> &mut Vec { + &mut self.derives + } + + fn attributes(&mut self) -> &mut Vec { + &mut self.attributes + } +} + impl<'a, P: Parent> Parent for GenEnum<'a, P> { fn append(&mut self, builder: StreamBuilder) { self.additional.push(builder); @@ -204,6 +348,8 @@ impl<'a, P: Parent> Parent for GenEnum<'a, P> { impl<'a, P: Parent> Drop for GenEnum<'a, P> { fn drop(&mut self) { let mut builder = StreamBuilder::new(); + self.build_derives(&mut builder) + .build_attributes(&mut builder); if self.visibility == Visibility::Pub { builder.ident_str("pub"); } @@ -216,7 +362,7 @@ impl<'a, P: Parent> Drop for GenEnum<'a, P> { .unwrap_or_default(), ) .group(Delimiter::Brace, |b| { - for value in &self.values { + for value in self.values.iter_mut() { build_value(b, value)?; } @@ -231,12 +377,14 @@ impl<'a, P: Parent> Drop for GenEnum<'a, P> { } } -fn build_value(builder: &mut StreamBuilder, value: &EnumValue) -> Result { +fn build_value(builder: &mut StreamBuilder, value: &mut EnumValue) -> Result { + value.build_attributes(builder); builder.ident(value.name.clone()); match value.value_type { ValueType::Named => builder.group(Delimiter::Brace, |b| { - for field in &value.fields { + for field in value.fields.iter_mut() { + field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -248,7 +396,8 @@ fn build_value(builder: &mut StreamBuilder, value: &EnumValue) -> Result { Ok(()) })?, ValueType::Unnamed => builder.group(Delimiter::Parenthesis, |b| { - for field in &value.fields { + for field in value.fields.iter_mut() { + field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -266,8 +415,9 @@ fn build_value(builder: &mut StreamBuilder, value: &EnumValue) -> Result { pub struct EnumValue { name: Ident, - fields: Vec, + fields: Vec, value_type: ValueType, + attributes: Vec, } impl EnumValue { @@ -276,6 +426,7 @@ impl EnumValue { name: Ident::new(name.into().as_str(), Span::call_site()), fields: Vec::new(), value_type: ValueType::Named, + attributes: Vec::new(), } } @@ -295,35 +446,130 @@ impl EnumValue { self } - /// Add a *private* field to the struct. For adding a public field, see `add_pub_field` + /// Add an attribute to the variant. /// - /// Names are ignored when the Struct's fields are unnamed - pub fn add_field(&mut self, name: impl Into, ty: impl Into) -> &mut Self { - self.fields.push(EnumField { - name: name.into(), - vis: Visibility::Default, - ty: ty.into(), - }); - self + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_enum("Foo") + /// .add_value("Bar") + /// .with_attribute("serde", |b| { + /// b.push_parsed("(rename_all = \"camelCase\")")?; + /// Ok(()) + /// })?; + /// # generator.assert_eq("enum Foo { # [serde (rename_all = \"camelCase\")] Bar { } , }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// enum Foo { + /// #[serde(rename_all = "camelCase")] + /// Bar { } + /// } + /// ``` + pub fn with_attribute( + &mut self, + name: impl AsRef, + value: impl FnOnce(&mut StreamBuilder) -> Result, + ) -> Result<&mut Self> { + AttributeContainer::with_attribute(self, name, value) } - /// Add a *public* field to the struct. For adding a public field, see `add_field` + /// Add a parsed attribute to the variant. /// - /// Names are ignored when the Struct's fields are unnamed - pub fn add_pub_field(&mut self, name: impl Into, ty: impl Into) -> &mut Self { - self.fields.push(EnumField { - name: name.into(), - vis: Visibility::Pub, - ty: ty.into(), - }); - self + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_enum("Foo") + /// .add_value("Bar") + /// .with_parsed_attribute("serde(rename_all = \"camelCase\")")?; + /// # generator.assert_eq("enum Foo { # [serde (rename_all = \"camelCase\")] Bar { } , }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// enum Foo { + /// #[serde(rename_all = "camelCase")] + /// Bar { } + /// } + /// ``` + pub fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> Result<&mut Self> { + AttributeContainer::with_parsed_attribute(self, attribute) + } + + /// Add a token stream as an attribute to the variant. + /// + /// ``` + /// # use virtue::prelude::{Generator, TokenStream}; + /// # let mut generator = Generator::with_name("Bar"); + /// let attribute = "serde(rename_all = \"camelCase\")".parse::().unwrap(); + /// generator + /// .generate_enum("Foo") + /// .add_value("Bar") + /// .with_attribute_stream(attribute); + /// # generator.assert_eq("enum Foo { # [serde (rename_all = \"camelCase\")] Bar { } , }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// enum Foo { + /// #[serde(rename_all = "camelCase")] + /// Bar { } + /// } + /// ``` + pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { + AttributeContainer::with_attribute_stream(self, attribute) + } + + /// Add a field to the enum value. + /// + /// Names are ignored when the enum value's fields are unnamed + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Fooz"); + /// generator + /// .generate_enum("Foo") + /// .add_value("Bar") + /// .add_field("bar", "u16") + /// .add_field("baz", "String"); + /// # generator.assert_eq("enum Foo { Bar { bar : u16 , baz : String , } , }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ``` + /// enum Foo { + /// Bar { + /// bar: u16, + /// baz: String + /// } + /// }; + /// ``` + pub fn add_field( + &mut self, + name: impl Into, + ty: impl Into, + ) -> FieldBuilder { + let mut fields = FieldBuilder::from(&mut self.fields); + fields.add_field(name, ty); + fields } } -struct EnumField { - name: String, - vis: Visibility, - ty: String, +impl AttributeContainer for EnumValue { + fn derives(&mut self) -> &mut Vec { + unreachable!("enum variants cannot have derives") + } + + fn attributes(&mut self) -> &mut Vec { + &mut self.attributes + } } enum ValueType { diff --git a/src/generate/gen_struct.rs b/src/generate/gen_struct.rs index b3f43e0..5e431b2 100644 --- a/src/generate/gen_struct.rs +++ b/src/generate/gen_struct.rs @@ -1,6 +1,10 @@ -use super::{Impl, ImplFor, Parent, StreamBuilder, StringOrIdent}; +use super::{ + AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, Path, StreamBuilder, + StringOrIdent, +}; use crate::parse::{Generic, Generics, Visibility}; -use crate::prelude::{Delimiter, Ident, Span}; +use crate::prelude::{Delimiter, Ident, Span, TokenStream}; +use crate::Result; /// Builder to generate a struct. /// Defaults to a struct with named fields `struct { : , ... }` @@ -9,7 +13,9 @@ pub struct GenStruct<'a, P: Parent> { name: Ident, visibility: Visibility, generics: Option, - fields: Vec, + fields: Vec, + derives: Vec, + attributes: Vec, additional: Vec, struct_type: StructType, } @@ -22,6 +28,8 @@ impl<'a, P: Parent> GenStruct<'a, P> { visibility: Visibility::Default, generics: None, fields: Vec::new(), + derives: Vec::new(), + attributes: Vec::new(), additional: Vec::new(), struct_type: StructType::Named, } @@ -165,7 +173,136 @@ impl<'a, P: Parent> GenStruct<'a, P> { self } - /// Add a *private* field to the struct. For adding a public field, see `add_pub_field` + /// Add a derive macro to the struct. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # use virtue::generate::Path; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_struct("Foo") + /// .with_derive("Clone") + /// .with_derive("Default") + /// .with_derive(Path::from_iter(vec!["serde", "Deserialize"])); + /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] struct Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[derive(Clone, Default, serde::Deserialize)] + /// struct Foo { } + /// ``` + pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { + AttributeContainer::with_derive(self, derive) + } + + /// Add derive macros to the struct. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # use virtue::generate::Path; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_struct("Foo") + /// .with_derives([ + /// "Clone".into(), + /// "Default".into(), + /// Path::from_iter(vec!["serde", "Deserialize"]), + /// ]); + /// # generator.assert_eq("# [derive (Clone , Default , serde ::Deserialize)] struct Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[derive(Clone, Default, serde::Deserialize)] + /// struct Foo { } + /// ``` + pub fn with_derives>( + &mut self, + derives: impl IntoIterator, + ) -> &mut Self { + AttributeContainer::with_derives(self, derives) + } + + /// Add an attribute to the struct. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) + /// instead. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_struct("Foo") + /// .with_attribute("serde", |b| { + /// b.push_parsed("(rename_all = \"camelCase\")")?; + /// Ok(()) + /// })?; + /// # generator.assert_eq("# [serde (rename_all = \"camelCase\")] struct Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[serde(rename_all = "camelCase")] + /// struct Foo { } + /// ``` + pub fn with_attribute( + &mut self, + name: impl AsRef, + value: impl FnOnce(&mut StreamBuilder) -> Result, + ) -> Result<&mut Self> { + AttributeContainer::with_attribute(self, name, value) + } + + /// Add a parsed attribute to the struct. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) + /// instead. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_struct("Foo") + /// .with_parsed_attribute("serde(rename_all = \"camelCase\")")?; + /// # generator.assert_eq("# [serde (rename_all = \"camelCase\")] struct Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[serde(rename_all = "camelCase")] + /// struct Foo { } + /// ``` + pub fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> Result<&mut Self> { + AttributeContainer::with_parsed_attribute(self, attribute) + } + + /// Add a token stream as an attribute to the struct. For `#[derive(...)]`, use + /// [`with_derive`](Self::with_derive) instead. + /// + /// ``` + /// # use virtue::prelude::{Generator, TokenStream}; + /// # use std::str::FromStr; + /// # let mut generator = Generator::with_name("Bar"); + /// + /// let attribute = "serde(rename_all = \"camelCase\")".parse::().unwrap(); + /// generator + /// .generate_struct("Foo") + /// .with_attribute_stream(attribute); + /// # generator.assert_eq("# [serde (rename_all = \"camelCase\")] struct Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[serde(rename_all = "camelCase")] + /// struct Foo { } + /// ``` + pub fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { + AttributeContainer::with_attribute_stream(self, attribute) + } + + /// Add a field to the struct. /// /// Names are ignored when the Struct's fields are unnamed /// @@ -187,25 +324,14 @@ impl<'a, P: Parent> GenStruct<'a, P> { /// baz: String, /// }; /// ``` - pub fn add_field(&mut self, name: impl Into, ty: impl Into) -> &mut Self { - self.fields.push(StructField { - name: name.into(), - vis: Visibility::Default, - ty: ty.into(), - }); - self - } - - /// Add a *public* field to the struct. For adding a public field, see `add_field` - /// - /// Names are ignored when the Struct's fields are unnamed - pub fn add_pub_field(&mut self, name: impl Into, ty: impl Into) -> &mut Self { - self.fields.push(StructField { - name: name.into(), - vis: Visibility::Pub, - ty: ty.into(), - }); - self + pub fn add_field( + &mut self, + name: impl Into, + ty: impl Into, + ) -> FieldBuilder { + let mut fields = FieldBuilder::from(&mut self.fields); + fields.add_field(name, ty); + fields } /// Add an `impl for ` @@ -228,6 +354,16 @@ impl<'a, P: Parent> GenStruct<'a, P> { } } +impl AttributeContainer for GenStruct<'_, P> { + fn derives(&mut self) -> &mut Vec { + &mut self.derives + } + + fn attributes(&mut self) -> &mut Vec { + &mut self.attributes + } +} + impl<'a, P: Parent> Parent for GenStruct<'a, P> { fn append(&mut self, builder: StreamBuilder) { self.additional.push(builder); @@ -248,7 +384,12 @@ impl<'a, P: Parent> Parent for GenStruct<'a, P> { impl<'a, P: Parent> Drop for GenStruct<'a, P> { fn drop(&mut self) { + use std::mem::take; let mut builder = StreamBuilder::new(); + + self.build_derives(&mut builder) + .build_attributes(&mut builder); + if self.visibility == Visibility::Pub { builder.ident_str("pub"); } @@ -261,7 +402,8 @@ impl<'a, P: Parent> Drop for GenStruct<'a, P> { match self.struct_type { StructType::Named => builder .group(Delimiter::Brace, |b| { - for field in &self.fields { + for field in self.fields.iter_mut() { + field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -275,7 +417,8 @@ impl<'a, P: Parent> Drop for GenStruct<'a, P> { .expect("Could not build struct"), StructType::Unnamed => builder .group(Delimiter::Parenthesis, |b| { - for field in &self.fields { + for field in self.fields.iter_mut() { + field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -288,7 +431,7 @@ impl<'a, P: Parent> Drop for GenStruct<'a, P> { StructType::Zst => builder.punct(';'), }; - for additional in std::mem::take(&mut self.additional) { + for additional in take(&mut self.additional) { builder.append(additional); } self.parent.append(builder); @@ -300,9 +443,3 @@ enum StructType { Unnamed, Zst, } - -struct StructField { - name: String, - vis: Visibility, - ty: String, -} diff --git a/src/generate/mod.rs b/src/generate/mod.rs index 260c289..e9d3406 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -21,11 +21,13 @@ mod r#impl; mod impl_for; mod stream_builder; +use crate::parse::Visibility; use crate::{ parse::{GenericConstraints, Generics}, - prelude::Ident, + prelude::{Delimiter, Ident, TokenStream}, }; use std::fmt; +use std::marker::PhantomData; pub use self::gen_enum::GenEnum; pub use self::gen_struct::GenStruct; @@ -78,3 +80,357 @@ impl<'a> From<&'a str> for StringOrIdent { Self::String(s.to_owned()) } } + +/// A path of identifiers, like `mod::Type`. +pub struct Path(Vec); + +impl From for Path { + fn from(s: String) -> Self { + StringOrIdent::from(s).into() + } +} + +impl From for Path { + fn from(i: Ident) -> Self { + StringOrIdent::from(i).into() + } +} + +impl From<&str> for Path { + fn from(s: &str) -> Self { + StringOrIdent::from(s).into() + } +} + +impl From for Path { + fn from(value: StringOrIdent) -> Self { + Self(vec![value]) + } +} + +impl FromIterator for Path { + fn from_iter>(iter: T) -> Self { + iter.into_iter().map(StringOrIdent::from).collect() + } +} + +impl FromIterator for Path { + fn from_iter>(iter: T) -> Self { + iter.into_iter().map(StringOrIdent::from).collect() + } +} + +impl<'a> FromIterator<&'a str> for Path { + fn from_iter>(iter: T) -> Self { + iter.into_iter().map(StringOrIdent::from).collect() + } +} + +impl FromIterator for Path { + fn from_iter>(iter: T) -> Self { + Self(iter.into_iter().collect()) + } +} + +impl IntoIterator for Path { + type Item = StringOrIdent; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +/// A struct or enum variant field. +struct Field { + name: String, + vis: Visibility, + ty: String, + attributes: Vec, +} + +impl Field { + fn new(name: impl Into, vis: Visibility, ty: impl Into) -> Self { + Self { + name: name.into(), + vis, + ty: ty.into(), + attributes: Vec::new(), + } + } +} + +/// A builder for struct or enum variant fields. +pub struct FieldBuilder<'a, P> { + fields: &'a mut Vec, + _parent: PhantomData

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

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

FieldBuilder<'_, P> { + fn current(&mut self) -> &mut Field { + // A field is always added before this is called, so the unwrap doesn't fail. + self.fields.last_mut().unwrap() + } +} + +/// A helper trait to share attribute code between struct and enum generators. +trait AttributeContainer { + fn derives(&mut self) -> &mut Vec; + fn attributes(&mut self) -> &mut Vec; + + fn with_derive(&mut self, derive: impl Into) -> &mut Self { + self.derives().push(derive.into()); + self + } + + fn with_derives>(&mut self, derives: impl IntoIterator) -> &mut Self { + self.derives().extend(derives.into_iter().map(Into::into)); + self + } + + fn with_attribute( + &mut self, + name: impl AsRef, + value: impl FnOnce(&mut StreamBuilder) -> crate::Result, + ) -> crate::Result<&mut Self> { + let mut stream = StreamBuilder::new(); + value(stream.ident_str(name))?; + self.attributes().push(stream); + Ok(self) + } + + fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> crate::Result<&mut Self> { + let mut stream = StreamBuilder::new(); + stream.push_parsed(attribute)?; + self.attributes().push(stream); + Ok(self) + } + + fn with_attribute_stream(&mut self, attribute: impl Into) -> &mut Self { + let stream = StreamBuilder { + stream: attribute.into(), + }; + self.attributes().push(stream); + self + } + + fn build_derives(&mut self, b: &mut StreamBuilder) -> &mut Self { + let derives = std::mem::take(self.derives()); + if !derives.is_empty() { + build_attribute(b, |b| { + b.ident_str("derive").group(Delimiter::Parenthesis, |b| { + for (idx, derive) in derives.into_iter().enumerate() { + if idx > 0 { + b.punct(','); + } + for (idx, component) in derive.into_iter().enumerate() { + if idx > 0 { + b.puncts("::"); + } + + match component { + StringOrIdent::String(s) => b.ident_str(s), + StringOrIdent::Ident(i) => b.ident(i), + }; + } + } + Ok(()) + }) + }) + .expect("could not build derives"); + } + self + } + + fn build_attributes(&mut self, b: &mut StreamBuilder) -> &mut Self { + for attr in std::mem::take(self.attributes()) { + build_attribute(b, |b| Ok(b.extend(attr.stream))).expect("could not build attribute"); + } + self + } +} + +impl AttributeContainer for Field { + fn derives(&mut self) -> &mut Vec { + unreachable!("fields cannot have derives") + } + + fn attributes(&mut self) -> &mut Vec { + &mut self.attributes + } +} + +fn build_attribute(b: &mut StreamBuilder, build: T) -> crate::Result +where + T: FnOnce(&mut StreamBuilder) -> crate::Result<&mut StreamBuilder>, +{ + b.punct('#').group(Delimiter::Bracket, |b| { + build(b)?; + Ok(()) + })?; + + Ok(()) +}