diff --git a/src/generate/gen_enum.rs b/src/generate/gen_enum.rs index 0ec2aa9..a230263 100644 --- a/src/generate/gen_enum.rs +++ b/src/generate/gen_enum.rs @@ -1,5 +1,5 @@ use super::{ - AttributeContainer, Field, FieldContainer, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, + AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, }; use crate::parse::{Generic, Generics, Visibility}; use crate::prelude::{Delimiter, Ident, Span}; @@ -21,9 +21,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>(()) @@ -47,7 +47,7 @@ pub struct GenEnum<'a, P: Parent> { generics: Option, values: Vec, derives: Vec, - attributes: Vec<(String, StreamBuilder)>, + attributes: Vec, additional: Vec, } @@ -133,10 +133,11 @@ impl<'a, P: Parent> GenEnum<'a, P> { /// #[serde(untagged)] /// enum Foo { } /// ``` - pub fn with_attribute(&mut self, name: impl Into, value: T) -> Result<&mut Self> - where - T: FnOnce(&mut StreamBuilder) -> Result, - { + pub fn with_attribute( + &mut self, + name: impl AsRef, + value: impl FnOnce(&mut StreamBuilder) -> Result, + ) -> Result<&mut Self> { AttributeContainer::with_attribute(self, name, value) } @@ -263,7 +264,7 @@ impl AttributeContainer for GenEnum<'_, P> { &mut self.derives } - fn attributes(&mut self) -> &mut Vec<(String, StreamBuilder)> { + fn attributes(&mut self) -> &mut Vec { &mut self.attributes } } @@ -358,7 +359,7 @@ pub struct EnumValue { name: Ident, fields: Vec, value_type: ValueType, - attributes: Vec<(String, StreamBuilder)>, + attributes: Vec, } impl EnumValue { @@ -386,6 +387,7 @@ impl EnumValue { self.value_type = ValueType::Unnamed; self } + /// Add an attribute to the variant. /// /// ``` @@ -409,76 +411,71 @@ impl EnumValue { /// Bar { } /// } /// ``` - pub fn with_attribute(&mut self, name: impl Into, value: T) -> Result<&mut Self> - where - T: FnOnce(&mut StreamBuilder) -> Result, - { + 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 *private* field to the struct. For adding a public field, see `add_pub_field` + /// Add a parsed 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(Field::new(name.into(), Visibility::Default, 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 *private* field with an attribute to the variant. + /// Add a field to the enum value. /// - /// Names are ignored when the variant's fields are unnamed + /// Names are ignored when the enum value's fields are unnamed /// /// ``` /// # use virtue::prelude::Generator; - /// # let mut generator = Generator::with_name("Bar"); + /// # let mut generator = Generator::with_name("Fooz"); /// generator /// .generate_enum("Foo") /// .add_value("Bar") - /// .add_field_with_attribute("bar", "u16", "serde", |b| { - /// b.push_parsed("(default)")?; - /// Ok(()) - /// })?; - /// # generator.assert_eq("enum Foo { Bar { # [serde (default)] bar : u16 , } , }"); + /// .add_field("bar", "u16") + /// .add_field("baz", "String"); + /// # generator.assert_eq("enum Foo { Bar { bar : u16 , baz : String , } , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: - /// ```ignore + /// ``` /// enum Foo { /// Bar { - /// #[serde(default)] - /// bar: u16 + /// bar: u16, + /// baz: String /// } - /// } + /// }; /// ``` - pub fn add_field_with_attribute( + pub fn add_field( &mut self, name: impl Into, ty: impl Into, - attribute_name: impl Into, - attribute_value: T, - ) -> Result<&mut Self> - where - T: FnOnce(&mut StreamBuilder) -> Result, - { - FieldContainer::add_field_with_attribute( - self, - name, - ty, - Visibility::Default, - attribute_name, - attribute_value, - ) - } - - /// 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(Field::new(name.into(), Visibility::Pub, ty.into())); - self + ) -> FieldBuilder { + let mut fields = FieldBuilder::from(&mut self.fields); + fields.add_field(name, ty); + fields } } @@ -487,17 +484,11 @@ impl AttributeContainer for EnumValue { unreachable!("enum variants cannot have derives") } - fn attributes(&mut self) -> &mut Vec<(String, StreamBuilder)> { + fn attributes(&mut self) -> &mut Vec { &mut self.attributes } } -impl FieldContainer for EnumValue { - fn fields(&mut self) -> &mut Vec { - &mut self.fields - } -} - enum ValueType { Named, Unnamed, diff --git a/src/generate/gen_struct.rs b/src/generate/gen_struct.rs index 76a5e8d..8763fb7 100644 --- a/src/generate/gen_struct.rs +++ b/src/generate/gen_struct.rs @@ -1,8 +1,9 @@ use super::{ - AttributeContainer, Field, FieldContainer, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, + AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, }; use crate::parse::{Generic, Generics, Visibility}; use crate::prelude::{Delimiter, Ident, Span}; +use crate::Result; /// Builder to generate a struct. /// Defaults to a struct with named fields `struct { : , ... }` @@ -13,7 +14,7 @@ pub struct GenStruct<'a, P: Parent> { generics: Option, fields: Vec, derives: Vec, - attributes: Vec<(String, StreamBuilder)>, + attributes: Vec, additional: Vec, struct_type: StructType, } @@ -233,138 +234,66 @@ impl<'a, P: Parent> GenStruct<'a, P> { /// #[serde(rename_all = "camelCase")] /// struct Foo { } /// ``` - pub fn with_attribute( + pub fn with_attribute( &mut self, - name: impl Into, - value: T, - ) -> crate::Result<&mut Self> - where - T: FnOnce(&mut StreamBuilder) -> crate::Result, - { + name: impl AsRef, + value: impl FnOnce(&mut StreamBuilder) -> Result, + ) -> Result<&mut Self> { AttributeContainer::with_attribute(self, name, value) } - /// Add a *private* field to the struct. For adding a public field, see `add_pub_field` - /// - /// Names are ignored when the Struct's fields are unnamed - /// - /// ``` - /// # use virtue::prelude::Generator; - /// # let mut generator = Generator::with_name("Fooz"); - /// generator - /// .generate_struct("Foo") - /// .add_field("bar", "u16") - /// .add_field("baz", "String"); - /// # generator.assert_eq("struct Foo { bar : u16 , baz : String , }"); - /// # Ok::<_, virtue::Error>(()) - /// ``` - /// - /// Generates: - /// ``` - /// struct Foo { - /// bar: u16, - /// baz: String, - /// }; - /// ``` - pub fn add_field(&mut self, name: impl Into, ty: impl Into) -> &mut Self { - self.fields.push(Field::new(name, Visibility::Default, ty)); - self - } - - /// Add a *private* field with an attribute to the struct. For adding a public field, see `add_pub_field_with_attribute` - /// - /// Names are ignored when the Struct's fields are unnamed + /// 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") - /// .add_field_with_attribute("bar", "u16", "serde", |b| { - /// b.push_parsed("(default)")?; - /// Ok(()) - /// })?; - /// # generator.assert_eq("struct Foo { # [serde (default)] bar : u16 , }"); + /// .with_parsed_attribute("serde(rename_all = \"camelCase\")")?; + /// # generator.assert_eq("# [serde (rename_all = \"camelCase\")] struct Foo { }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: /// ```ignore - /// struct Foo { - /// #[serde(default)] - /// bar: u16 - /// } + /// #[serde(rename_all = "camelCase")] + /// struct Foo { } /// ``` - pub fn add_field_with_attribute( - &mut self, - name: impl Into, - ty: impl Into, - attribute_name: impl Into, - attribute_value: T, - ) -> crate::Result<&mut Self> - where - T: FnOnce(&mut StreamBuilder) -> crate::Result, - { - FieldContainer::add_field_with_attribute( - self, - name, - ty, - Visibility::Default, - attribute_name, - attribute_value, - ) + pub fn with_parsed_attribute(&mut self, attribute: impl AsRef) -> Result<&mut Self> { + AttributeContainer::with_parsed_attribute(self, attribute) } - /// Add a *public* field to the struct. For adding a private 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(Field::new(name, Visibility::Pub, ty)); - self - } - - /// Add a *public* field with an attribute to the struct. For adding a private field, see `add_field_with_attribute` + /// Add a field to the struct. /// /// Names are ignored when the Struct's fields are unnamed /// /// ``` /// # use virtue::prelude::Generator; - /// # let mut generator = Generator::with_name("Bar"); + /// # let mut generator = Generator::with_name("Fooz"); /// generator /// .generate_struct("Foo") - /// .add_pub_field_with_attribute("bar", "u16", "serde", |b| { - /// b.push_parsed("(default)")?; - /// Ok(()) - /// })?; - /// # generator.assert_eq("struct Foo { # [serde (default)] pub bar : u16 , }"); + /// .add_field("bar", "u16") + /// .add_field("baz", "String"); + /// # generator.assert_eq("struct Foo { bar : u16 , baz : String , }"); /// # Ok::<_, virtue::Error>(()) /// ``` /// /// Generates: - /// ```ignore + /// ``` /// struct Foo { - /// #[serde(default)] - /// pub bar: u16 - /// } + /// bar: u16, + /// baz: String, + /// }; /// ``` - pub fn add_pub_field_with_attribute( + pub fn add_field( &mut self, name: impl Into, ty: impl Into, - attribute_name: impl Into, - attribute_value: T, - ) -> crate::Result<&mut Self> - where - T: FnOnce(&mut StreamBuilder) -> crate::Result, - { - FieldContainer::add_field_with_attribute( - self, - name, - ty, - Visibility::Pub, - attribute_name, - attribute_value, - ) + ) -> FieldBuilder { + let mut fields = FieldBuilder::from(&mut self.fields); + fields.add_field(name, ty); + fields } /// Add an `impl for ` @@ -392,17 +321,11 @@ impl AttributeContainer for GenStruct<'_, P> { &mut self.derives } - fn attributes(&mut self) -> &mut Vec<(String, StreamBuilder)> { + fn attributes(&mut self) -> &mut Vec { &mut self.attributes } } -impl FieldContainer for GenStruct<'_, P> { - fn fields(&mut self) -> &mut Vec { - &mut self.fields - } -} - impl<'a, P: Parent> Parent for GenStruct<'a, P> { fn append(&mut self, builder: StreamBuilder) { self.additional.push(builder); diff --git a/src/generate/mod.rs b/src/generate/mod.rs index 8af1bd5..070739e 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -27,6 +27,7 @@ use crate::{ prelude::{Delimiter, Ident}, }; use std::fmt; +use std::marker::PhantomData; pub use self::gen_enum::GenEnum; pub use self::gen_struct::GenStruct; @@ -81,11 +82,11 @@ impl<'a> From<&'a str> for StringOrIdent { } /// A struct or enum variant field. -pub struct Field { +struct Field { name: String, vis: Visibility, ty: String, - attributes: Vec<(String, StreamBuilder)>, + attributes: Vec, } impl Field { @@ -99,10 +100,159 @@ impl Field { } } +/// 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 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<(String, StreamBuilder)>; + fn attributes(&mut self) -> &mut Vec; fn with_derive(&mut self, derive: impl Into) -> &mut Self { self.derives().push(derive.into()); @@ -114,23 +264,29 @@ trait AttributeContainer { self } - fn with_attribute(&mut self, name: impl Into, value: T) -> crate::Result<&mut Self> - where - T: FnOnce(&mut StreamBuilder) -> crate::Result, - { - self.attributes().push((name.into(), { - let mut b = StreamBuilder::new(); - value(&mut b)?; - b - })); + 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 build_derives(&mut self, b: &mut StreamBuilder) -> &mut Self { let derives = std::mem::take(self.derives()); if !derives.is_empty() { - build_attribute(b, "derive", |b| { - b.group(Delimiter::Parenthesis, |b| { + 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(','); @@ -149,9 +305,8 @@ trait AttributeContainer { } fn build_attributes(&mut self, b: &mut StreamBuilder) -> &mut Self { - for (name, value) in std::mem::take(self.attributes()) { - build_attribute(b, name, |b| Ok(b.extend(value.stream))) - .expect("could not build attribute"); + for attr in std::mem::take(self.attributes()) { + build_attribute(b, |b| Ok(b.extend(attr.stream))).expect("could not build attribute"); } self } @@ -162,39 +317,17 @@ impl AttributeContainer for Field { unreachable!("fields cannot have derives") } - fn attributes(&mut self) -> &mut Vec<(String, StreamBuilder)> { + fn attributes(&mut self) -> &mut Vec { &mut self.attributes } } -/// A helper trait to share field attribute code between struct and enum generators. -trait FieldContainer { - fn fields(&mut self) -> &mut Vec; - - fn add_field_with_attribute( - &mut self, - name: impl Into, - ty: impl Into, - vis: Visibility, - attribute_name: impl Into, - attribute_value: T, - ) -> crate::Result<&mut Self> - where - T: FnOnce(&mut StreamBuilder) -> crate::Result, - { - let mut field = Field::new(name, vis, ty); - field.with_attribute(attribute_name, attribute_value)?; - self.fields().push(field); - Ok(self) - } -} - -fn build_attribute(b: &mut StreamBuilder, name: impl AsRef, build: T) -> crate::Result +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.ident_str(name))?; + build(b)?; Ok(()) })?;