Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #3 and #4: Add field documentation and annotation #6

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 72 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: String,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation strings are tricky. Reading the diff, it looks like only a single doc string line is supported. I'm not sure what the best option would be to support multi lines. Either "\n" could be detected in the documentation string and then it all gets formatted, or documentation could be a Vec<String>.

Thoughts"

Copy link

@LegNeato LegNeato Apr 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about different functions? Have doc() take a Vec<String> to stay the same as annotation() and add something like doc_with_newlines() which takes a String.


/// Field annotation
annotation: String,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this probably should be a Vec<String> to support adding multiple annotations. In this case, the annotation builder function would push a new string.

}

/// Defines an associated type.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1073,31 +1089,63 @@ impl AssociatedType {
}
}

// ===== impl Field =====

impl Field {
/// Return a field definition with the provided name and type
pub fn new<T>(name: &str, ty: T) -> Self
where T: Into<Type>,
{
Field {
name: name.into(),
ty: ty.into(),
documentation: String::new(),
annotation: String::new(),
}
}

/// Set field's documentation.
pub fn doc(&mut self, documentation: &str) -> &mut Self {
self.documentation = documentation.into();
self
}

/// Set field's annotation.
pub fn annotation(&mut self, annotation: &str) -> &mut Self {
self.annotation = annotation.into();
self
}
}

// ===== impl Fields =====

impl Fields {
fn named<T>(&mut self, name: &str, ty: T) -> &mut Self
where T: Into<Type>,
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"),
}

self
}

fn named<T>(&mut self, name: &str, ty: T) -> &mut Self
where T: Into<Type>,
{
self.push_named(Field {
name: name.to_string(),
ty: ty.into(),
documentation: String::new(),
annotation: String::new(),
})
}

fn tuple<T>(&mut self, ty: T) -> &mut Self
where T: Into<Type>,
{
Expand All @@ -1121,6 +1169,12 @@ impl Fields {

fmt.block(|fmt| {
for f in fields {
if !f.documentation.is_empty() {
write!(fmt, "/// {}\n", f.documentation)?;
}
if !f.annotation.is_empty() {
write!(fmt, "{}\n", f.annotation)?;
}
write!(fmt, "{}: ", f.name)?;
f.ty.fmt(fmt)?;
write!(fmt, ",\n")?;
Expand Down Expand Up @@ -1196,6 +1250,8 @@ impl Impl {
self.assoc_tys.push(Field {
name: name.to_string(),
ty: ty.into(),
documentation: String::new(),
annotation: String::new(),
});

self
Expand Down Expand Up @@ -1340,6 +1396,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: String::new(),
annotation: String::new(),
});

self
Expand Down
55 changes: 54 additions & 1 deletion tests/codegen.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
extern crate codegen;

use codegen::Scope;
use codegen::{Field, Scope, Struct};

#[test]
fn empty_scope() {
Expand All @@ -26,6 +26,59 @@ 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 = "Field's documentation";
let anot = r#"#[serde(rename = "bar")]"#;

let mut struct_ = Struct::new("Foo");

let mut field1 = Field::new("one", "usize");
field1.doc(doc);
struct_.push_field(field1);

let mut field2 = Field::new("two", "usize");
field2.annotation(anot);
struct_.push_field(field2);

let mut field3 = Field::new("three", "usize");
field3.doc(doc).annotation(anot);
struct_.push_field(field3);

scope.push_struct(struct_);

let expect = r#"
struct Foo {
/// Field's documentation
one: usize,
#[serde(rename = "bar")]
two: usize,
/// Field's documentation
#[serde(rename = "bar")]
three: usize,
}"#;

assert_eq!(scope.to_string(), &expect[1..]);
}

#[test]
fn empty_struct() {
let mut scope = Scope::new();
Expand Down