diff --git a/src/lib.rs b/src/lib.rs index 26368cb..396fc6b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,12 +128,18 @@ enum Fields { /// Defines a struct field. #[derive(Debug, Clone)] -struct Field { +pub struct Field { /// Field name name: String, /// Field type ty: Type, + + /// Field documentation + documentation: Vec, + + /// Field annotation + annotation: Vec, } /// Defines an associated type. @@ -582,6 +588,16 @@ impl Struct { self } + /// Push a named field to the struct. + /// + /// A struct can either set named fields with this function or tuple fields + /// with `push_tuple_field`, but not both. + pub fn push_field(&mut self, field: Field) -> &mut Self + { + self.fields.push_named(field); + self + } + /// Add a named field to the struct. /// /// A struct can either set named fields with this function or tuple fields @@ -1073,24 +1089,45 @@ impl AssociatedType { } } +// ===== impl Field ===== + +impl Field { + /// Return a field definition with the provided name and type + pub fn new(name: &str, ty: T) -> Self + where T: Into, + { + Field { + name: name.into(), + ty: ty.into(), + documentation: Vec::new(), + annotation: Vec::new(), + } + } + + /// Set field's documentation. + pub fn doc(&mut self, documentation: Vec<&str>) -> &mut Self { + self.documentation = documentation.iter().map(|doc| doc.to_string()).collect(); + self + } + + /// Set field's annotation. + pub fn annotation(&mut self, annotation: Vec<&str>) -> &mut Self { + self.annotation = annotation.iter().map(|ann| ann.to_string()).collect(); + self + } +} + // ===== impl Fields ===== impl Fields { - fn named(&mut self, name: &str, ty: T) -> &mut Self - where T: Into, + fn push_named(&mut self, field: Field) -> &mut Self { match *self { Fields::Empty => { - *self = Fields::Named(vec![Field { - name: name.to_string(), - ty: ty.into(), - }]); + *self = Fields::Named(vec![field]); } Fields::Named(ref mut fields) => { - fields.push(Field { - name: name.to_string(), - ty: ty.into(), - }); + fields.push(field); } _ => panic!("field list is named"), } @@ -1098,6 +1135,17 @@ impl Fields { self } + fn named(&mut self, name: &str, ty: T) -> &mut Self + where T: Into, + { + self.push_named(Field { + name: name.to_string(), + ty: ty.into(), + documentation: Vec::new(), + annotation: Vec::new(), + }) + } + fn tuple(&mut self, ty: T) -> &mut Self where T: Into, { @@ -1121,6 +1169,16 @@ impl Fields { fmt.block(|fmt| { for f in fields { + if !f.documentation.is_empty() { + for doc in &f.documentation { + write!(fmt, "/// {}\n", doc)?; + } + } + if !f.annotation.is_empty() { + for ann in &f.annotation { + write!(fmt, "{}\n", ann)?; + } + } write!(fmt, "{}: ", f.name)?; f.ty.fmt(fmt)?; write!(fmt, ",\n")?; @@ -1196,6 +1254,8 @@ impl Impl { self.assoc_tys.push(Field { name: name.to_string(), ty: ty.into(), + documentation: Vec::new(), + annotation: Vec::new(), }); self @@ -1340,6 +1400,11 @@ impl Function { self.args.push(Field { name: name.to_string(), ty: ty.into(), + // While a `Field` is used here, both `documentation` + // and `annotation` does not make sense for function arguments. + // Simply use empty strings. + documentation: Vec::new(), + annotation: Vec::new(), }); self diff --git a/tests/codegen.rs b/tests/codegen.rs index 0a4f7c9..2f68dbc 100644 --- a/tests/codegen.rs +++ b/tests/codegen.rs @@ -1,6 +1,6 @@ extern crate codegen; -use codegen::Scope; +use codegen::{Field, Scope, Struct}; #[test] fn empty_scope() { @@ -26,6 +26,64 @@ struct Foo { assert_eq!(scope.to_string(), &expect[1..]); } +#[test] +fn struct_with_pushed_field() { + let mut scope = Scope::new(); + let mut struct_ = Struct::new("Foo"); + let mut field = Field::new("one", "usize"); + struct_.push_field(field); + scope.push_struct(struct_); + + let expect = r#" +struct Foo { + one: usize, +}"#; + + assert_eq!(scope.to_string(), &expect[1..]); +} + +#[test] +fn single_struct_documented_field() { + let mut scope = Scope::new(); + + let doc = vec!["Field's documentation", "Second line"]; + + let mut struct_ = Struct::new("Foo"); + + let mut field1 = Field::new("one", "usize"); + field1.doc(doc.clone()); + struct_.push_field(field1); + + let mut field2 = Field::new("two", "usize"); + field2.annotation(vec![r#"#[serde(rename = "bar")]"#]); + struct_.push_field(field2); + + let mut field3 = Field::new("three", "usize"); + field3.doc(doc).annotation(vec![ + r#"#[serde(skip_serializing)]"#, + r#"#[serde(skip_deserializing)]"#, + ]); + struct_.push_field(field3); + + scope.push_struct(struct_); + + let expect = r#" +struct Foo { + /// Field's documentation + /// Second line + one: usize, + #[serde(rename = "bar")] + two: usize, + /// Field's documentation + /// Second line + #[serde(skip_serializing)] + #[serde(skip_deserializing)] + three: usize, +}"#; + + assert_eq!(scope.to_string(), &expect[1..]); +} + #[test] fn empty_struct() { let mut scope = Scope::new();