From eb4f8c9ffd5dd56d342fb9d49afc87c5f125bff4 Mon Sep 17 00:00:00 2001 From: NightEule5 <24661563+NightEule5@users.noreply.github.com> Date: Sun, 9 Jun 2024 20:51:53 -0600 Subject: [PATCH 1/5] Implement attributes for GenStruct and GenEnum --- src/generate/gen_enum.rs | 188 ++++++++++++++++++++++++++++++----- src/generate/gen_struct.rs | 196 ++++++++++++++++++++++++++++++++----- src/generate/mod.rs | 111 ++++++++++++++++++++- 3 files changed, 450 insertions(+), 45 deletions(-) diff --git a/src/generate/gen_enum.rs b/src/generate/gen_enum.rs index de9a9ee..828faec 100644 --- a/src/generate/gen_enum.rs +++ b/src/generate/gen_enum.rs @@ -1,4 +1,6 @@ -use super::{Impl, ImplFor, Parent, StreamBuilder, StringOrIdent}; +use super::{ + build_attributes, Attributes, Field, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, +}; use crate::parse::Visibility; use crate::prelude::{Delimiter, Ident, Span}; use crate::Result; @@ -43,6 +45,7 @@ pub struct GenEnum<'a, P: Parent> { name: Ident, visibility: Visibility, values: Vec, + attributes: Attributes, additional: Vec, } @@ -53,6 +56,7 @@ impl<'a, P: Parent> GenEnum<'a, P> { name: Ident::new(name.into().as_str(), Span::call_site()), visibility: Visibility::Default, values: Vec::new(), + attributes: Attributes::default(), additional: Vec::new(), } } @@ -63,6 +67,78 @@ impl<'a, P: Parent> GenEnum<'a, P> { self } + /// Add a derive macro to the enum. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_enum("Foo") + /// .with_derive("Clone") + /// .with_derive("Default"); + /// # generator.assert_eq("# [derive (Clone , Default)] enum Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[derive(Clone, Default)] + /// enum Foo { } + pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { + self.attributes.push_derive(derive); + self + } + + /// Add derive macros to the enum. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_enum("Foo") + /// .with_derives(["Clone".into(), "Default".into()]); + /// # generator.assert_eq("# [derive (Clone , Default)] enum Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[derive(Clone, Default)] + /// enum Foo { } + pub fn with_derives(&mut self, derives: impl IntoIterator) -> &mut Self { + self.attributes.append_derives(derives); + self + } + + /// 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 Into, value: T) -> Result<&mut Self> + where + T: FnOnce(&mut StreamBuilder) -> Result, + { + self.attributes.push(name.into(), value)?; + Ok(self) + } + /// Add an enum value /// /// Returns a builder for the value that's similar to GenStruct @@ -112,6 +188,7 @@ 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.attributes.build(&mut builder); if self.visibility == Visibility::Pub { builder.ident_str("pub"); } @@ -119,7 +196,7 @@ impl<'a, P: Parent> Drop for GenEnum<'a, P> { .ident_str("enum") .ident(self.name.clone()) .group(Delimiter::Brace, |b| { - for value in &self.values { + for value in self.values.iter_mut() { build_value(b, value)?; } @@ -134,12 +211,15 @@ 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 { + use std::mem::take; + value.attributes.build(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() { + build_attributes(b, take(&mut field.attributes)); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -151,7 +231,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() { + build_attributes(b, take(&mut field.attributes)); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -169,8 +250,9 @@ fn build_value(builder: &mut StreamBuilder, value: &EnumValue) -> Result { pub struct EnumValue { name: Ident, - fields: Vec, + fields: Vec, value_type: ValueType, + attributes: Attributes, } impl EnumValue { @@ -179,6 +261,7 @@ impl EnumValue { name: Ident::new(name.into().as_str(), Span::call_site()), fields: Vec::new(), value_type: ValueType::Named, + attributes: Attributes::default(), } } @@ -197,38 +280,99 @@ impl EnumValue { self.value_type = ValueType::Unnamed; self } + /// Add an attribute to the variant. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_enum("Foo") + /// .add_value("Bar") + /// .with_attribute("serde", |b| { + /// b.push_parsed("(rename_all = \"camelCase\")")?; + /// Ok(()) + /// })?; + /// # generator.assert_eq("enum Foo { # [serde (rename_all = \"camelCase\")] Bar { } , }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// enum Foo { + /// #[serde(rename_all = "camelCase")] + /// Bar { } + /// } + /// ``` + pub fn with_attribute(&mut self, name: impl Into, value: T) -> Result<&mut Self> + where + T: FnOnce(&mut StreamBuilder) -> Result, + { + self.attributes.push(name.into(), value)?; + Ok(self) + } /// 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 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.fields + .push(Field::new(name.into(), Visibility::Default, ty.into())); self } + /// Add a *private* field with an attribute to the variant. + /// + /// Names are ignored when the variant's fields are unnamed + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// 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 , } , }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// enum Foo { + /// Bar { + /// #[serde(default)] + /// bar: u16 + /// } + /// } + /// ``` + pub fn add_field_with_attribute( + &mut self, + name: impl Into, + ty: impl Into, + attribute_name: impl Into, + attribute_value: T, + ) -> Result<&mut Self> + where + T: FnOnce(&mut StreamBuilder) -> Result, + { + let field = Field::new(name, Visibility::Default, ty) + .with_attribute(attribute_name, attribute_value)?; + self.fields.push(field); + Ok(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(EnumField { - name: name.into(), - vis: Visibility::Pub, - ty: ty.into(), - }); + self.fields + .push(Field::new(name.into(), Visibility::Pub, ty.into())); self } } -struct EnumField { - name: String, - vis: Visibility, - ty: String, -} - enum ValueType { Named, Unnamed, diff --git a/src/generate/gen_struct.rs b/src/generate/gen_struct.rs index 40f1ae3..bfb13da 100644 --- a/src/generate/gen_struct.rs +++ b/src/generate/gen_struct.rs @@ -1,4 +1,6 @@ -use super::{Impl, ImplFor, Parent, StreamBuilder, StringOrIdent}; +use super::{ + build_attributes, Attributes, Field, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, +}; use crate::parse::Visibility; use crate::prelude::{Delimiter, Ident, Span}; @@ -8,7 +10,8 @@ pub struct GenStruct<'a, P: Parent> { parent: &'a mut P, name: Ident, visibility: Visibility, - fields: Vec, + fields: Vec, + attributes: Attributes, additional: Vec, struct_type: StructType, } @@ -20,6 +23,7 @@ impl<'a, P: Parent> GenStruct<'a, P> { name: Ident::new(name.into().as_str(), Span::call_site()), visibility: Visibility::Default, fields: Vec::new(), + attributes: Attributes::default(), additional: Vec::new(), struct_type: StructType::Named, } @@ -81,6 +85,82 @@ impl<'a, P: Parent> GenStruct<'a, P> { self } + /// Add a derive macro to the struct. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_struct("Foo") + /// .with_derive("Clone") + /// .with_derive("Default"); + /// # generator.assert_eq("# [derive (Clone , Default)] struct Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[derive(Clone, Default)] + /// struct Foo { } + pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { + self.attributes.push_derive(derive); + self + } + + /// Add derive macros to the struct. + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// generator + /// .generate_struct("Foo") + /// .with_derives(["Clone".into(), "Default".into()]); + /// # generator.assert_eq("# [derive (Clone , Default)] struct Foo { }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// #[derive(Clone, Default)] + /// struct Foo { } + pub fn with_derives(&mut self, derives: impl IntoIterator) -> &mut Self { + self.attributes.append_derives(derives); + self + } + + /// 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 Into, + value: T, + ) -> crate::Result<&mut Self> + where + T: FnOnce(&mut StreamBuilder) -> crate::Result, + { + self.attributes.push(name.into(), value)?; + Ok(self) + } + /// 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 @@ -104,26 +184,98 @@ impl<'a, P: Parent> GenStruct<'a, P> { /// }; /// ``` 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.fields.push(Field::new(name, Visibility::Default, ty)); self } - /// Add a *public* field to the struct. For adding a public field, see `add_field` + /// 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 + /// + /// ``` + /// # 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 , }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// struct Foo { + /// #[serde(default)] + /// bar: u16 + /// } + /// ``` + 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, + { + let field = Field::new(name, Visibility::Default, ty) + .with_attribute(attribute_name, attribute_value)?; + self.fields.push(field); + Ok(self) + } + + /// 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(StructField { - name: name.into(), - vis: Visibility::Pub, - ty: ty.into(), - }); + 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` + /// + /// Names are ignored when the Struct's fields are unnamed + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Bar"); + /// 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 , }"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ```ignore + /// struct Foo { + /// #[serde(default)] + /// pub bar: u16 + /// } + /// ``` + pub fn add_pub_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, + { + let field = Field::new(name, Visibility::Pub, ty) + .with_attribute(attribute_name, attribute_value)?; + self.fields.push(field); + Ok(self) + } + /// Add an `impl for ` pub fn impl_for(&mut self, name: impl Into) -> ImplFor { ImplFor::new(self, name.into(), None) @@ -164,7 +316,11 @@ 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.attributes.build(&mut builder); + if self.visibility == Visibility::Pub { builder.ident_str("pub"); } @@ -173,7 +329,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() { + build_attributes(b, take(&mut field.attributes)); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -187,7 +344,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() { + build_attributes(b, take(&mut field.attributes)); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -200,7 +358,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); @@ -212,9 +370,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..ead54d4 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -21,9 +21,10 @@ mod r#impl; mod impl_for; mod stream_builder; +use crate::parse::Visibility; use crate::{ parse::{GenericConstraints, Generics}, - prelude::Ident, + prelude::{Delimiter, Ident}, }; use std::fmt; @@ -78,3 +79,111 @@ impl<'a> From<&'a str> for StringOrIdent { Self::String(s.to_owned()) } } + +/// A struct or enum variant field. +pub struct Field { + name: String, + vis: Visibility, + ty: String, + attributes: Vec<(String, StreamBuilder)>, +} + +impl Field { + fn new(name: impl Into, vis: Visibility, ty: impl Into) -> Self { + Self { + name: name.into(), + vis, + ty: ty.into(), + attributes: Vec::new(), + } + } + + fn with_attribute( + mut self, + attribute_name: impl Into, + attribute_value: F, + ) -> crate::Result + where + F: FnOnce(&mut StreamBuilder) -> crate::Result, + { + let mut b = StreamBuilder::new(); + attribute_value(&mut b)?; + self.attributes.push((attribute_name.into(), b)); + Ok(self) + } +} + +/// A set of attributes for a struct or enum. +#[derive(Default)] +struct Attributes { + derives: Vec, + attributes: Vec<(String, StreamBuilder)>, +} + +impl Attributes { + fn push_derive(&mut self, derive: impl Into) { + self.derives.push(derive.into()); + } + + fn push( + &mut self, + name: impl Into, + build: impl FnOnce(&mut StreamBuilder) -> crate::Result, + ) -> crate::Result { + self.attributes.push((name.into(), { + let mut b = StreamBuilder::new(); + build(&mut b)?; + b + })); + Ok(()) + } + + fn append_derives(&mut self, derives: impl IntoIterator) { + self.derives.extend(derives); + } + + fn build(&mut self, b: &mut StreamBuilder) { + let Self { + derives, + attributes, + } = std::mem::take(self); + if !derives.is_empty() { + build_attribute(b, "derive", |b| { + b.group(Delimiter::Parenthesis, |b| { + for (idx, derive) in derives.into_iter().enumerate() { + if idx > 0 { + b.punct(','); + } + match derive { + StringOrIdent::String(s) => b.ident_str(s), + StringOrIdent::Ident(i) => b.ident(i), + }; + } + Ok(()) + }) + }) + .expect("could not build derives"); + } + + build_attributes(b, attributes); + } +} + +fn build_attributes(b: &mut StreamBuilder, attributes: Vec<(String, StreamBuilder)>) { + for (name, value) in attributes { + build_attribute(b, name, |b| Ok(b.extend(value.stream))) + .expect("could not build attribute"); + } +} + +fn build_attribute(b: &mut StreamBuilder, name: impl AsRef, build: T) -> crate::Result +where + T: FnOnce(&mut StreamBuilder) -> crate::Result<&mut StreamBuilder>, +{ + b.punct('#').group(Delimiter::Bracket, |b| { + build(b.ident_str(name))?; + Ok(()) + })?; + + Ok(()) +} From 1254ef65bc22081e57d886b65642796fc2d8a2ee Mon Sep 17 00:00:00 2001 From: NightEule5 <24661563+NightEule5@users.noreply.github.com> Date: Mon, 10 Jun 2024 17:32:07 -0600 Subject: [PATCH 2/5] Factor out repeated code --- src/generate/gen_enum.rs | 72 ++++++++++++++++++-------- src/generate/gen_struct.rs | 64 ++++++++++++++++------- src/generate/mod.rs | 103 +++++++++++++++++++++---------------- 3 files changed, 152 insertions(+), 87 deletions(-) diff --git a/src/generate/gen_enum.rs b/src/generate/gen_enum.rs index 828faec..c04d073 100644 --- a/src/generate/gen_enum.rs +++ b/src/generate/gen_enum.rs @@ -1,5 +1,5 @@ use super::{ - build_attributes, Attributes, Field, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, + AttributeContainer, Field, FieldContainer, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, }; use crate::parse::Visibility; use crate::prelude::{Delimiter, Ident, Span}; @@ -45,7 +45,8 @@ pub struct GenEnum<'a, P: Parent> { name: Ident, visibility: Visibility, values: Vec, - attributes: Attributes, + derives: Vec, + attributes: Vec<(String, StreamBuilder)>, additional: Vec, } @@ -56,7 +57,8 @@ impl<'a, P: Parent> GenEnum<'a, P> { name: Ident::new(name.into().as_str(), Span::call_site()), visibility: Visibility::Default, values: Vec::new(), - attributes: Attributes::default(), + derives: Vec::new(), + attributes: Vec::new(), additional: Vec::new(), } } @@ -85,8 +87,7 @@ impl<'a, P: Parent> GenEnum<'a, P> { /// #[derive(Clone, Default)] /// enum Foo { } pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { - self.attributes.push_derive(derive); - self + AttributeContainer::with_derive(self, derive) } /// Add derive macros to the enum. @@ -106,8 +107,7 @@ impl<'a, P: Parent> GenEnum<'a, P> { /// #[derive(Clone, Default)] /// enum Foo { } pub fn with_derives(&mut self, derives: impl IntoIterator) -> &mut Self { - self.attributes.append_derives(derives); - self + AttributeContainer::with_derives(self, derives) } /// Add an attribute to the enum. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) @@ -135,8 +135,7 @@ impl<'a, P: Parent> GenEnum<'a, P> { where T: FnOnce(&mut StreamBuilder) -> Result, { - self.attributes.push(name.into(), value)?; - Ok(self) + AttributeContainer::with_attribute(self, name, value) } /// Add an enum value @@ -167,6 +166,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<(String, StreamBuilder)> { + &mut self.attributes + } +} + impl<'a, P: Parent> Parent for GenEnum<'a, P> { fn append(&mut self, builder: StreamBuilder) { self.additional.push(builder); @@ -188,7 +197,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.attributes.build(&mut builder); + self.build_derives(&mut builder) + .build_attributes(&mut builder); if self.visibility == Visibility::Pub { builder.ident_str("pub"); } @@ -212,14 +222,13 @@ impl<'a, P: Parent> Drop for GenEnum<'a, P> { } fn build_value(builder: &mut StreamBuilder, value: &mut EnumValue) -> Result { - use std::mem::take; - value.attributes.build(builder); + value.build_attributes(builder); builder.ident(value.name.clone()); match value.value_type { ValueType::Named => builder.group(Delimiter::Brace, |b| { for field in value.fields.iter_mut() { - build_attributes(b, take(&mut field.attributes)); + field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -232,7 +241,7 @@ fn build_value(builder: &mut StreamBuilder, value: &mut EnumValue) -> Result { })?, ValueType::Unnamed => builder.group(Delimiter::Parenthesis, |b| { for field in value.fields.iter_mut() { - build_attributes(b, take(&mut field.attributes)); + field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -252,7 +261,7 @@ pub struct EnumValue { name: Ident, fields: Vec, value_type: ValueType, - attributes: Attributes, + attributes: Vec<(String, StreamBuilder)>, } impl EnumValue { @@ -261,7 +270,7 @@ impl EnumValue { name: Ident::new(name.into().as_str(), Span::call_site()), fields: Vec::new(), value_type: ValueType::Named, - attributes: Attributes::default(), + attributes: Vec::new(), } } @@ -307,8 +316,7 @@ impl EnumValue { where T: FnOnce(&mut StreamBuilder) -> Result, { - self.attributes.push(name.into(), value)?; - Ok(self) + AttributeContainer::with_attribute(self, name, value) } /// Add a *private* field to the struct. For adding a public field, see `add_pub_field` @@ -357,10 +365,14 @@ impl EnumValue { where T: FnOnce(&mut StreamBuilder) -> Result, { - let field = Field::new(name, Visibility::Default, ty) - .with_attribute(attribute_name, attribute_value)?; - self.fields.push(field); - Ok(self) + 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` @@ -373,6 +385,22 @@ impl EnumValue { } } +impl AttributeContainer for EnumValue { + fn derives(&mut self) -> &mut Vec { + unreachable!("enum variants cannot have derives") + } + + fn attributes(&mut self) -> &mut Vec<(String, StreamBuilder)> { + &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 bfb13da..74ecfa5 100644 --- a/src/generate/gen_struct.rs +++ b/src/generate/gen_struct.rs @@ -1,5 +1,5 @@ use super::{ - build_attributes, Attributes, Field, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, + AttributeContainer, Field, FieldContainer, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, }; use crate::parse::Visibility; use crate::prelude::{Delimiter, Ident, Span}; @@ -11,7 +11,8 @@ pub struct GenStruct<'a, P: Parent> { name: Ident, visibility: Visibility, fields: Vec, - attributes: Attributes, + derives: Vec, + attributes: Vec<(String, StreamBuilder)>, additional: Vec, struct_type: StructType, } @@ -23,7 +24,8 @@ impl<'a, P: Parent> GenStruct<'a, P> { name: Ident::new(name.into().as_str(), Span::call_site()), visibility: Visibility::Default, fields: Vec::new(), - attributes: Attributes::default(), + derives: Vec::new(), + attributes: Vec::new(), additional: Vec::new(), struct_type: StructType::Named, } @@ -103,8 +105,7 @@ impl<'a, P: Parent> GenStruct<'a, P> { /// #[derive(Clone, Default)] /// struct Foo { } pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { - self.attributes.push_derive(derive); - self + AttributeContainer::with_derive(self, derive) } /// Add derive macros to the struct. @@ -124,8 +125,7 @@ impl<'a, P: Parent> GenStruct<'a, P> { /// #[derive(Clone, Default)] /// struct Foo { } pub fn with_derives(&mut self, derives: impl IntoIterator) -> &mut Self { - self.attributes.append_derives(derives); - self + AttributeContainer::with_derives(self, derives) } /// Add an attribute to the struct. For `#[derive(...)]`, use [`with_derive`](Self::with_derive) @@ -157,8 +157,7 @@ impl<'a, P: Parent> GenStruct<'a, P> { where T: FnOnce(&mut StreamBuilder) -> crate::Result, { - self.attributes.push(name.into(), value)?; - Ok(self) + AttributeContainer::with_attribute(self, name, value) } /// Add a *private* field to the struct. For adding a public field, see `add_pub_field` @@ -222,10 +221,14 @@ impl<'a, P: Parent> GenStruct<'a, P> { where T: FnOnce(&mut StreamBuilder) -> crate::Result, { - let field = Field::new(name, Visibility::Default, ty) - .with_attribute(attribute_name, attribute_value)?; - self.fields.push(field); - Ok(self) + FieldContainer::add_field_with_attribute( + self, + name, + ty, + Visibility::Default, + attribute_name, + attribute_value, + ) } /// Add a *public* field to the struct. For adding a private field, see `add_field` @@ -270,10 +273,14 @@ impl<'a, P: Parent> GenStruct<'a, P> { where T: FnOnce(&mut StreamBuilder) -> crate::Result, { - let field = Field::new(name, Visibility::Pub, ty) - .with_attribute(attribute_name, attribute_value)?; - self.fields.push(field); - Ok(self) + FieldContainer::add_field_with_attribute( + self, + name, + ty, + Visibility::Pub, + attribute_name, + attribute_value, + ) } /// Add an `impl for ` @@ -296,6 +303,22 @@ 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<(String, StreamBuilder)> { + &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); @@ -319,7 +342,8 @@ impl<'a, P: Parent> Drop for GenStruct<'a, P> { use std::mem::take; let mut builder = StreamBuilder::new(); - self.attributes.build(&mut builder); + self.build_derives(&mut builder) + .build_attributes(&mut builder); if self.visibility == Visibility::Pub { builder.ident_str("pub"); @@ -330,7 +354,7 @@ impl<'a, P: Parent> Drop for GenStruct<'a, P> { StructType::Named => builder .group(Delimiter::Brace, |b| { for field in self.fields.iter_mut() { - build_attributes(b, take(&mut field.attributes)); + field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } @@ -345,7 +369,7 @@ impl<'a, P: Parent> Drop for GenStruct<'a, P> { StructType::Unnamed => builder .group(Delimiter::Parenthesis, |b| { for field in self.fields.iter_mut() { - build_attributes(b, take(&mut field.attributes)); + field.build_attributes(b); if field.vis == Visibility::Pub { b.ident_str("pub"); } diff --git a/src/generate/mod.rs b/src/generate/mod.rs index ead54d4..8af1bd5 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -97,56 +97,37 @@ impl Field { attributes: Vec::new(), } } - - fn with_attribute( - mut self, - attribute_name: impl Into, - attribute_value: F, - ) -> crate::Result - where - F: FnOnce(&mut StreamBuilder) -> crate::Result, - { - let mut b = StreamBuilder::new(); - attribute_value(&mut b)?; - self.attributes.push((attribute_name.into(), b)); - Ok(self) - } } -/// A set of attributes for a struct or enum. -#[derive(Default)] -struct Attributes { - derives: Vec, - attributes: Vec<(String, StreamBuilder)>, -} +/// 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)>; -impl Attributes { - fn push_derive(&mut self, derive: impl Into) { - self.derives.push(derive.into()); + fn with_derive(&mut self, derive: impl Into) -> &mut Self { + self.derives().push(derive.into()); + self } - fn push( - &mut self, - name: impl Into, - build: impl FnOnce(&mut StreamBuilder) -> crate::Result, - ) -> crate::Result { - self.attributes.push((name.into(), { + fn with_derives(&mut self, derives: impl IntoIterator) -> &mut Self { + self.derives().extend(derives); + 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(); - build(&mut b)?; + value(&mut b)?; b })); - Ok(()) - } - - fn append_derives(&mut self, derives: impl IntoIterator) { - self.derives.extend(derives); + Ok(self) } - fn build(&mut self, b: &mut StreamBuilder) { - let Self { - derives, - attributes, - } = std::mem::take(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| { @@ -164,15 +145,47 @@ impl Attributes { }) .expect("could not build derives"); } + self + } + + 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"); + } + self + } +} + +impl AttributeContainer for Field { + fn derives(&mut self) -> &mut Vec { + unreachable!("fields cannot have derives") + } - build_attributes(b, attributes); + fn attributes(&mut self) -> &mut Vec<(String, StreamBuilder)> { + &mut self.attributes } } -fn build_attributes(b: &mut StreamBuilder, attributes: Vec<(String, StreamBuilder)>) { - for (name, value) in attributes { - build_attribute(b, name, |b| Ok(b.extend(value.stream))) - .expect("could not build attribute"); +/// 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) } } From 0f29d1faaed24ebe7b719d1c819337df7531824d Mon Sep 17 00:00:00 2001 From: NightEule5 <24661563+NightEule5@users.noreply.github.com> Date: Wed, 12 Jun 2024 03:27:42 -0600 Subject: [PATCH 3/5] Tweak API --- src/generate/gen_enum.rs | 117 +++++++++----------- src/generate/gen_struct.rs | 137 +++++------------------ src/generate/mod.rs | 217 ++++++++++++++++++++++++++++++------- 3 files changed, 259 insertions(+), 212 deletions(-) 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(()) })?; From b0e17f1653a07e6ccc4b80a95d909b7a0c71a407 Mon Sep 17 00:00:00 2001 From: NightEule5 <24661563+NightEule5@users.noreply.github.com> Date: Wed, 12 Jun 2024 17:40:26 -0600 Subject: [PATCH 4/5] Allow paths in derives --- src/generate/gen_enum.rs | 35 ++++++++++------ src/generate/gen_struct.rs | 35 +++++++++++----- src/generate/mod.rs | 84 ++++++++++++++++++++++++++++++++++---- 3 files changed, 122 insertions(+), 32 deletions(-) diff --git a/src/generate/gen_enum.rs b/src/generate/gen_enum.rs index a230263..1f682d9 100644 --- a/src/generate/gen_enum.rs +++ b/src/generate/gen_enum.rs @@ -1,5 +1,6 @@ use super::{ - AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, + AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, Path, StreamBuilder, + StringOrIdent, }; use crate::parse::{Generic, Generics, Visibility}; use crate::prelude::{Delimiter, Ident, Span}; @@ -46,7 +47,7 @@ pub struct GenEnum<'a, P: Parent> { visibility: Visibility, generics: Option, values: Vec, - derives: Vec, + derives: Vec, attributes: Vec, additional: Vec, } @@ -75,20 +76,22 @@ impl<'a, P: Parent> GenEnum<'a, P> { /// /// ``` /// # 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"); - /// # generator.assert_eq("# [derive (Clone , Default)] enum Foo { }"); + /// .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)] + /// #[derive(Clone, Default, serde::Deserialize)] /// enum Foo { } - pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { + pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { AttributeContainer::with_derive(self, derive) } @@ -96,19 +99,27 @@ impl<'a, P: Parent> GenEnum<'a, P> { /// /// ``` /// # 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()]); - /// # generator.assert_eq("# [derive (Clone , Default)] 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)] + /// #[derive(Clone, Default, serde::Deserialize)] /// enum Foo { } - pub fn with_derives(&mut self, derives: impl IntoIterator) -> &mut Self { + pub fn with_derives>( + &mut self, + derives: impl IntoIterator, + ) -> &mut Self { AttributeContainer::with_derives(self, derives) } @@ -260,7 +271,7 @@ impl<'a, P: Parent> GenEnum<'a, P> { } impl AttributeContainer for GenEnum<'_, P> { - fn derives(&mut self) -> &mut Vec { + fn derives(&mut self) -> &mut Vec { &mut self.derives } @@ -480,7 +491,7 @@ impl EnumValue { } impl AttributeContainer for EnumValue { - fn derives(&mut self) -> &mut Vec { + fn derives(&mut self) -> &mut Vec { unreachable!("enum variants cannot have derives") } diff --git a/src/generate/gen_struct.rs b/src/generate/gen_struct.rs index 8763fb7..dacc834 100644 --- a/src/generate/gen_struct.rs +++ b/src/generate/gen_struct.rs @@ -1,5 +1,6 @@ use super::{ - AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, StreamBuilder, StringOrIdent, + AttributeContainer, Field, FieldBuilder, Impl, ImplFor, Parent, Path, StreamBuilder, + StringOrIdent, }; use crate::parse::{Generic, Generics, Visibility}; use crate::prelude::{Delimiter, Ident, Span}; @@ -13,7 +14,7 @@ pub struct GenStruct<'a, P: Parent> { visibility: Visibility, generics: Option, fields: Vec, - derives: Vec, + derives: Vec, attributes: Vec, additional: Vec, struct_type: StructType, @@ -176,20 +177,23 @@ impl<'a, P: Parent> GenStruct<'a, P> { /// /// ``` /// # 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"); - /// # generator.assert_eq("# [derive (Clone , Default)] struct Foo { }"); + /// .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)] + /// #[derive(Clone, Default, serde::Deserialize)] /// struct Foo { } - pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { + /// ``` + pub fn with_derive(&mut self, derive: impl Into) -> &mut Self { AttributeContainer::with_derive(self, derive) } @@ -197,19 +201,28 @@ impl<'a, P: Parent> GenStruct<'a, P> { /// /// ``` /// # 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()]); - /// # generator.assert_eq("# [derive (Clone , Default)] 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)] + /// #[derive(Clone, Default, serde::Deserialize)] /// struct Foo { } - pub fn with_derives(&mut self, derives: impl IntoIterator) -> &mut Self { + /// ``` + pub fn with_derives>( + &mut self, + derives: impl IntoIterator, + ) -> &mut Self { AttributeContainer::with_derives(self, derives) } @@ -317,7 +330,7 @@ impl<'a, P: Parent> GenStruct<'a, P> { } impl AttributeContainer for GenStruct<'_, P> { - fn derives(&mut self) -> &mut Vec { + fn derives(&mut self) -> &mut Vec { &mut self.derives } diff --git a/src/generate/mod.rs b/src/generate/mod.rs index 070739e..d68b5c2 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -81,6 +81,66 @@ impl<'a> From<&'a str> for StringOrIdent { } } +/// 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, @@ -251,16 +311,16 @@ impl

FieldBuilder<'_, P> { /// A helper trait to share attribute code between struct and enum generators. trait AttributeContainer { - fn derives(&mut self) -> &mut Vec; + fn derives(&mut self) -> &mut Vec; fn attributes(&mut self) -> &mut Vec; - fn with_derive(&mut self, derive: impl Into) -> &mut Self { + 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); + fn with_derives>(&mut self, derives: impl IntoIterator) -> &mut Self { + self.derives().extend(derives.into_iter().map(Into::into)); self } @@ -291,10 +351,16 @@ trait AttributeContainer { if idx > 0 { b.punct(','); } - match derive { - StringOrIdent::String(s) => b.ident_str(s), - StringOrIdent::Ident(i) => b.ident(i), - }; + 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(()) }) @@ -313,7 +379,7 @@ trait AttributeContainer { } impl AttributeContainer for Field { - fn derives(&mut self) -> &mut Vec { + fn derives(&mut self) -> &mut Vec { unreachable!("fields cannot have derives") } From 55ef8d1e2d46116cc49f3b2c9f0cb12b1e3c1497 Mon Sep 17 00:00:00 2001 From: NightEule5 <24661563+NightEule5@users.noreply.github.com> Date: Thu, 13 Jun 2024 17:20:32 -0600 Subject: [PATCH 5/5] Add method to add attributes as TokenStreams and missing with_parsed_attribute --- src/generate/gen_enum.rs | 74 +++++++++++++++++++++++++++++++++++++- src/generate/gen_struct.rs | 27 +++++++++++++- src/generate/mod.rs | 37 ++++++++++++++++++- 3 files changed, 135 insertions(+), 3 deletions(-) diff --git a/src/generate/gen_enum.rs b/src/generate/gen_enum.rs index 1f682d9..0a25886 100644 --- a/src/generate/gen_enum.rs +++ b/src/generate/gen_enum.rs @@ -3,7 +3,7 @@ use super::{ 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 { { ... }, ... }` @@ -152,6 +152,53 @@ impl<'a, P: Parent> GenEnum<'a, P> { 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. /// /// ``` @@ -454,6 +501,31 @@ impl EnumValue { 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 diff --git a/src/generate/gen_struct.rs b/src/generate/gen_struct.rs index dacc834..5e431b2 100644 --- a/src/generate/gen_struct.rs +++ b/src/generate/gen_struct.rs @@ -3,7 +3,7 @@ use super::{ 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. @@ -277,6 +277,31 @@ impl<'a, P: Parent> GenStruct<'a, P> { 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 diff --git a/src/generate/mod.rs b/src/generate/mod.rs index d68b5c2..e9d3406 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -24,7 +24,7 @@ mod stream_builder; use crate::parse::Visibility; use crate::{ parse::{GenericConstraints, Generics}, - prelude::{Delimiter, Ident}, + prelude::{Delimiter, Ident, TokenStream}, }; use std::fmt; use std::marker::PhantomData; @@ -258,6 +258,33 @@ impl

FieldBuilder<'_, P> { 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. /// /// ``` @@ -342,6 +369,14 @@ trait AttributeContainer { 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() {