diff --git a/src/generate/gen_enum.rs b/src/generate/gen_enum.rs new file mode 100644 index 0000000..de9a9ee --- /dev/null +++ b/src/generate/gen_enum.rs @@ -0,0 +1,236 @@ +use super::{Impl, ImplFor, Parent, StreamBuilder, StringOrIdent}; +use crate::parse::Visibility; +use crate::prelude::{Delimiter, Ident, Span}; +use crate::Result; + +/// Builder to generate an `enum { { ... }, ... }` +/// +/// ``` +/// # use virtue::prelude::Generator; +/// # let mut generator = Generator::with_name("Fooz"); +/// { +/// let mut enumgen = generator.generate_enum("Foo"); +/// enumgen +/// .add_value("ZST") +/// .make_zst(); +/// enumgen +/// .add_value("Named") +/// .add_field("bar", "u16") +/// .add_field("baz", "String"); +/// enumgen +/// .add_value("Unnamed") +/// .add_field("", "u16") +/// .add_field("baz", "String") +/// .make_tuple(); +/// } +/// # generator.assert_eq("enum Foo { ZST , Named { bar : u16 , baz : String , } , Unnamed (u16 , String ,) , }"); +/// # Ok::<_, virtue::Error>(()) +/// ``` +/// +/// Generates: +/// ``` +/// enum Foo { +/// ZST, +/// Named { +/// bar: u16, +/// baz: String, +/// }, +/// Unnamed(u16, String), +/// }; +/// ``` +pub struct GenEnum<'a, P: Parent> { + parent: &'a mut P, + name: Ident, + visibility: Visibility, + values: Vec, + additional: Vec, +} + +impl<'a, P: Parent> GenEnum<'a, P> { + pub(crate) fn new(parent: &'a mut P, name: impl Into) -> Self { + Self { + parent, + name: Ident::new(name.into().as_str(), Span::call_site()), + visibility: Visibility::Default, + values: Vec::new(), + additional: Vec::new(), + } + } + + /// Make the enum `pub`. By default the struct will have no visibility modifier and will only be visible in the current scope. + pub fn make_pub(&mut self) -> &mut Self { + self.visibility = Visibility::Pub; + self + } + + /// Add an enum value + /// + /// Returns a builder for the value that's similar to GenStruct + pub fn add_value(&mut self, name: impl Into) -> &mut EnumValue { + self.values.push(EnumValue::new(name)); + self.values.last_mut().unwrap() + } + + /// Add an `impl for ` + pub fn impl_for(&mut self, name: impl Into) -> ImplFor { + ImplFor::new(self, name.into(), None) + } + + /// Generate an `impl ` implementation. See [`Impl`] for more information. + pub fn r#impl(&mut self) -> Impl { + Impl::with_parent_name(self) + } + + /// Generate an `impl ` implementation. See [`Impl`] for more information. + /// + /// Alias for [`impl`] which doesn't need a `r#` prefix. + /// + /// [`impl`]: #method.impl + pub fn generate_impl(&mut self) -> Impl { + Impl::with_parent_name(self) + } +} + +impl<'a, P: Parent> Parent for GenEnum<'a, P> { + fn append(&mut self, builder: StreamBuilder) { + self.additional.push(builder); + } + + fn name(&self) -> &Ident { + &self.name + } + + fn generics(&self) -> Option<&crate::parse::Generics> { + None + } + + fn generic_constraints(&self) -> Option<&crate::parse::GenericConstraints> { + None + } +} + +impl<'a, P: Parent> Drop for GenEnum<'a, P> { + fn drop(&mut self) { + let mut builder = StreamBuilder::new(); + if self.visibility == Visibility::Pub { + builder.ident_str("pub"); + } + builder + .ident_str("enum") + .ident(self.name.clone()) + .group(Delimiter::Brace, |b| { + for value in &self.values { + build_value(b, value)?; + } + + Ok(()) + }) + .expect("Could not build enum"); + + for additional in std::mem::take(&mut self.additional) { + builder.append(additional); + } + self.parent.append(builder); + } +} + +fn build_value(builder: &mut StreamBuilder, value: &EnumValue) -> Result { + builder.ident(value.name.clone()); + + match value.value_type { + ValueType::Named => builder.group(Delimiter::Brace, |b| { + for field in &value.fields { + if field.vis == Visibility::Pub { + b.ident_str("pub"); + } + b.ident_str(&field.name) + .punct(':') + .push_parsed(&field.ty)? + .punct(','); + } + Ok(()) + })?, + ValueType::Unnamed => builder.group(Delimiter::Parenthesis, |b| { + for field in &value.fields { + if field.vis == Visibility::Pub { + b.ident_str("pub"); + } + b.push_parsed(&field.ty)?.punct(','); + } + Ok(()) + })?, + ValueType::Zst => builder, + }; + + builder.punct(','); + + Ok(()) +} + +pub struct EnumValue { + name: Ident, + fields: Vec, + value_type: ValueType, +} + +impl EnumValue { + fn new(name: impl Into) -> Self { + Self { + name: Ident::new(name.into().as_str(), Span::call_site()), + fields: Vec::new(), + value_type: ValueType::Named, + } + } + + /// Make the struct a zero-sized type (no fields) + /// + /// Any fields will be ignored + pub fn make_zst(&mut self) -> &mut Self { + self.value_type = ValueType::Zst; + self + } + + /// Make the struct fields unnamed + /// + /// The names of any field will be ignored + pub fn make_tuple(&mut self) -> &mut Self { + self.value_type = ValueType::Unnamed; + self + } + + /// Add 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 + } + + /// 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 + } +} + +struct EnumField { + name: String, + vis: Visibility, + ty: String, +} + +enum ValueType { + Named, + Unnamed, + Zst, +} diff --git a/src/generate/gen_struct.rs b/src/generate/gen_struct.rs index c4d5c74..40f1ae3 100644 --- a/src/generate/gen_struct.rs +++ b/src/generate/gen_struct.rs @@ -2,15 +2,15 @@ use super::{Impl, ImplFor, Parent, StreamBuilder, StringOrIdent}; use crate::parse::Visibility; use crate::prelude::{Delimiter, Ident, Span}; -/// Builder to generate a `struct { : , ... }` -/// -/// Currently only structs with named fields are supported. +/// Builder to generate a struct. +/// Defaults to a struct with named fields `struct { : , ... }` pub struct GenStruct<'a, P: Parent> { parent: &'a mut P, name: Ident, visibility: Visibility, fields: Vec, additional: Vec, + struct_type: StructType, } impl<'a, P: Parent> GenStruct<'a, P> { @@ -21,9 +21,60 @@ impl<'a, P: Parent> GenStruct<'a, P> { visibility: Visibility::Default, fields: Vec::new(), additional: Vec::new(), + struct_type: StructType::Named, } } + /// Make the struct a zero-sized type (no fields) + /// + /// Any fields will be ignored + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Fooz"); + /// generator + /// .generate_struct("Foo") + /// .make_zst() + /// .add_field("bar", "u16") + /// .add_field("baz", "String"); + /// # generator.assert_eq("struct Foo ;"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ``` + /// struct Foo; + /// ``` + pub fn make_zst(&mut self) -> &mut Self { + self.struct_type = StructType::Zst; + self + } + + /// Make the struct fields unnamed + /// + /// The names of any field will be ignored + /// + /// ``` + /// # use virtue::prelude::Generator; + /// # let mut generator = Generator::with_name("Fooz"); + /// generator + /// .generate_struct("Foo") + /// .make_tuple() + /// .add_field("bar", "u16") + /// .add_field("baz", "String"); + /// # generator.assert_eq("struct Foo (u16 , String ,) ;"); + /// # Ok::<_, virtue::Error>(()) + /// ``` + /// + /// Generates: + /// ``` + /// struct Foo(u16, String); + /// ``` + pub fn make_tuple(&mut self) -> &mut Self { + self.struct_type = StructType::Unnamed; + self + } + /// Make the struct `pub`. By default the struct will have no visibility modifier and will only be visible in the current scope. pub fn make_pub(&mut self) -> &mut Self { self.visibility = Visibility::Pub; @@ -31,6 +82,27 @@ impl<'a, P: Parent> GenStruct<'a, P> { } /// 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(StructField { name: name.into(), @@ -41,6 +113,8 @@ impl<'a, P: Parent> GenStruct<'a, P> { } /// 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(), @@ -94,22 +168,37 @@ impl<'a, P: Parent> Drop for GenStruct<'a, P> { if self.visibility == Visibility::Pub { builder.ident_str("pub"); } - builder - .ident_str("struct") - .ident(self.name.clone()) - .group(Delimiter::Brace, |b| { - for field in &self.fields { - if field.vis == Visibility::Pub { - b.ident_str("pub"); + builder.ident_str("struct").ident(self.name.clone()); + + match self.struct_type { + StructType::Named => builder + .group(Delimiter::Brace, |b| { + for field in &self.fields { + if field.vis == Visibility::Pub { + b.ident_str("pub"); + } + b.ident_str(&field.name) + .punct(':') + .push_parsed(&field.ty)? + .punct(','); + } + Ok(()) + }) + .expect("Could not build struct"), + StructType::Unnamed => builder + .group(Delimiter::Parenthesis, |b| { + for field in &self.fields { + if field.vis == Visibility::Pub { + b.ident_str("pub"); + } + b.push_parsed(&field.ty)?.punct(','); } - b.ident_str(&field.name) - .punct(':') - .push_parsed(&field.ty)? - .punct(','); - } - Ok(()) - }) - .expect("Could not build struct"); + Ok(()) + }) + .expect("Could not build struct") + .punct(';'), + StructType::Zst => builder.punct(';'), + }; for additional in std::mem::take(&mut self.additional) { builder.append(additional); @@ -118,6 +207,12 @@ impl<'a, P: Parent> Drop for GenStruct<'a, P> { } } +enum StructType { + Named, + Unnamed, + Zst, +} + struct StructField { name: String, vis: Visibility, diff --git a/src/generate/generate_mod.rs b/src/generate/generate_mod.rs index bf6412a..859706d 100644 --- a/src/generate/generate_mod.rs +++ b/src/generate/generate_mod.rs @@ -1,4 +1,4 @@ -use super::{GenStruct, Impl, Parent, StreamBuilder}; +use super::{GenEnum, GenStruct, Impl, Parent, StreamBuilder}; use crate::{ parse::Visibility, prelude::{Delimiter, Ident, Span}, @@ -43,11 +43,16 @@ impl<'a, P: Parent> GenerateMod<'a, P> { Ok(()) } - /// Generate a struct with the given name. + /// Generate a struct with the given name. See [`GenStruct`] for more info. pub fn generate_struct(&mut self, name: impl Into) -> GenStruct { GenStruct::new(self, name) } + /// Generate an enum with the given name. See [`GenEnum`] for more info. + pub fn generate_enum(&mut self, name: impl Into) -> GenEnum { + GenEnum::new(self, name) + } + /// Generate an `impl ` implementation. See [`Impl`] for more information. pub fn r#impl(&mut self, name: impl Into) -> Impl { Impl::new(self, name) diff --git a/src/generate/generator.rs b/src/generate/generator.rs index c6c3e51..4ddbfd9 100644 --- a/src/generate/generator.rs +++ b/src/generate/generator.rs @@ -1,4 +1,4 @@ -use super::{GenerateMod, Impl, ImplFor, StreamBuilder, StringOrIdent}; +use super::{GenEnum, GenStruct, GenerateMod, Impl, ImplFor, StreamBuilder, StringOrIdent}; use crate::parse::{GenericConstraints, Generics}; use crate::prelude::{Ident, TokenStream}; @@ -135,6 +135,16 @@ impl Generator { .with_lifetimes(lifetimes) } + /// Generate a struct with the given name. See [`GenStruct`] for more info. + pub fn generate_struct(&mut self, name: impl Into) -> GenStruct { + GenStruct::new(self, name) + } + + /// Generate an enum with the given name. See [`GenEnum`] for more info. + pub fn generate_enum(&mut self, name: impl Into) -> GenEnum { + GenEnum::new(self, name) + } + /// Generate a `mod { ... }`. See [`GenerateMod`] for more info. pub fn generate_mod(&mut self, mod_name: impl Into) -> GenerateMod { GenerateMod::new(self, mod_name) diff --git a/src/generate/mod.rs b/src/generate/mod.rs index e74f935..260c289 100644 --- a/src/generate/mod.rs +++ b/src/generate/mod.rs @@ -12,6 +12,7 @@ //! [`Generator::finish()`]: struct.Generator.html#method.finish //! [`TokenStream`]: ../prelude/struct.TokenStream.html +mod gen_enum; mod gen_struct; mod generate_item; mod generate_mod; @@ -26,6 +27,7 @@ use crate::{ }; use std::fmt; +pub use self::gen_enum::GenEnum; pub use self::gen_struct::GenStruct; pub use self::generate_item::{FnBuilder, FnSelfArg, GenConst}; pub use self::generate_mod::GenerateMod;