Move ident and features fields into Node struct
diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml
index 75e8394..70274fa 100644
--- a/codegen/Cargo.toml
+++ b/codegen/Cargo.toml
@@ -13,8 +13,8 @@
 inflections = "1.1"
 proc-macro2 = "0.4"
 rustfmt-nightly = { git = "https://github.com/rust-lang-nursery/rustfmt" }
-serde = "1.0.87"
-serde_derive = "1.0.87"
+serde = "1.0.88"
+serde_derive = "1.0.88"
 serde_json = "1.0.38"
 toml = "0.4.10"
 
diff --git a/codegen/src/gen.rs b/codegen/src/gen.rs
index 58f03fb..83ddc4e 100644
--- a/codegen/src/gen.rs
+++ b/codegen/src/gen.rs
@@ -11,7 +11,6 @@
 //! 3. The path to `syn` is hardcoded.
 
 use crate::types;
-use indexmap::IndexMap;
 use proc_macro2::TokenStream;
 
 use std::fs::File;
@@ -361,10 +360,10 @@
 
                 let mut res = simple_visit(t, kind, name);
 
-                let target = defs.types.iter().find(|ty| ty.ident() == t).unwrap();
+                let target = defs.types.iter().find(|ty| ty.ident == *t).unwrap();
 
                 Some(
-                    if requires_full(target.features()) && !requires_full(features) {
+                    if requires_full(&target.features) && !requires_full(features) {
                         quote! {
                             full!(#res)
                         }
@@ -396,9 +395,9 @@
     }
 
     pub fn generate(state: &mut State, s: &types::Node, defs: &types::Definitions) {
-        let features = visit_features(s.features());
-        let under_name = under_name(s.ident());
-        let ty = Ident::new(s.ident(), Span::call_site());
+        let features = visit_features(&s.features);
+        let under_name = under_name(&s.ident);
+        let ty = Ident::new(&s.ident, Span::call_site());
         let visit_fn = Ident::new(&format!("visit_{}", under_name), Span::call_site());
         let visit_mut_fn = Ident::new(&format!("visit_{}_mut", under_name), Span::call_site());
         let fold_fn = Ident::new(&format!("fold_{}", under_name), Span::call_site());
@@ -407,13 +406,13 @@
         let mut visit_mut_impl = TokenStream::new();
         let mut fold_impl = TokenStream::new();
 
-        match s {
-            types::Node::Enum(ref e) => {
+        match &s.data {
+            types::Data::Enum(variants) => {
                 let mut visit_variants = TokenStream::new();
                 let mut visit_mut_variants = TokenStream::new();
                 let mut fold_variants = TokenStream::new();
 
-                for variant in &e.variants {
+                for variant in variants {
                     let variant_ident = Ident::new(&variant.ident, Span::call_site());
 
                     if variant.fields.is_empty() {
@@ -455,15 +454,15 @@
                             let owned_binding = Owned(quote!(#binding));
 
                             visit_fields.append_all(
-                                visit(ty, s.features(), defs, Visit, &borrowed_binding)
+                                visit(ty, &s.features, defs, Visit, &borrowed_binding)
                                     .unwrap_or_else(|| noop_visit(Visit, &borrowed_binding)),
                             );
                             visit_mut_fields.append_all(
-                                visit(ty, s.features(), defs, VisitMut, &borrowed_binding)
+                                visit(ty, &s.features, defs, VisitMut, &borrowed_binding)
                                     .unwrap_or_else(|| noop_visit(VisitMut, &borrowed_binding)),
                             );
                             fold_fields.append_all(
-                                visit(ty, s.features(), defs, Fold, &owned_binding)
+                                visit(ty, &s.features, defs, Fold, &owned_binding)
                                     .unwrap_or_else(|| noop_visit(Fold, &owned_binding)),
                             );
 
@@ -512,23 +511,23 @@
                     }
                 });
             }
-            types::Node::Struct(ref v) => {
+            types::Data::Struct(fields) => {
                 let mut fold_fields = TokenStream::new();
 
-                for (field, ty) in &v.fields {
+                for (field, ty) in fields {
                     let id = Ident::new(&field, Span::call_site());
                     let ref_toks = Owned(quote!(_i.#id));
-                    let visit_field = visit(&ty, &v.features, defs, Visit, &ref_toks)
+                    let visit_field = visit(&ty, &s.features, defs, Visit, &ref_toks)
                         .unwrap_or_else(|| noop_visit(Visit, &ref_toks));
                     visit_impl.append_all(quote! {
                         #visit_field;
                     });
-                    let visit_mut_field = visit(&ty, &v.features, defs, VisitMut, &ref_toks)
+                    let visit_mut_field = visit(&ty, &s.features, defs, VisitMut, &ref_toks)
                         .unwrap_or_else(|| noop_visit(VisitMut, &ref_toks));
                     visit_mut_impl.append_all(quote! {
                         #visit_mut_field;
                     });
-                    let fold = visit(&ty, &v.features, defs, Fold, &ref_toks)
+                    let fold = visit(&ty, &s.features, defs, Fold, &ref_toks)
                         .unwrap_or_else(|| noop_visit(Fold, &ref_toks));
 
                     fold_fields.append_all(quote! {
@@ -536,7 +535,7 @@
                     });
                 }
 
-                if !v.fields.is_empty() {
+                if !fields.is_empty() {
                     fold_impl.append_all(quote! {
                         #ty {
                             #fold_fields
@@ -555,15 +554,25 @@
                     });
                 }
             }
-        }
-
-        let mut include_fold_impl = true;
-        if let types::Node::Struct(ref data) = s {
-            if data.fields.is_empty() && !super::TERMINAL_TYPES.contains(&&s.ident()) {
-                include_fold_impl = false;
+            types::Data::Private => {
+                if ty == "Ident" {
+                    fold_impl.append_all(quote! {
+                        let mut _i = _i;
+                        let span = _visitor.fold_span(_i.span());
+                        _i.set_span(span);
+                    });
+                }
+                fold_impl.append_all(quote! {
+                    _i
+                });
             }
         }
 
+        let include_fold_impl = match &s.data {
+            types::Data::Private => super::TERMINAL_TYPES.contains(&s.ident.as_str()),
+            types::Data::Struct(_) | types::Data::Enum(_) => true,
+        };
+
         state.visit_trait.append_all(quote! {
             #features
             fn #visit_fn(&mut self, i: &'ast #ty) {
@@ -640,11 +649,11 @@
     let mut defs = defs.clone();
 
     for &tt in TERMINAL_TYPES {
-        defs.types.push(types::Node::Struct(types::Struct::new(
-            tt.to_string(),
-            types::Features::default(),
-            IndexMap::new(),
-        )));
+        defs.types.push(types::Node {
+            ident: tt.to_string(),
+            features: types::Features::default(),
+            data: types::Data::Private,
+        });
     }
 
     let mut state = codegen::State::default();
diff --git a/codegen/src/parse.rs b/codegen/src/parse.rs
index e38c1e2..10872ea 100644
--- a/codegen/src/parse.rs
+++ b/codegen/src/parse.rs
@@ -48,33 +48,32 @@
     let features = introspect_features(&item.features);
 
     match &item.ast.data {
-        Data::Enum(ref data) => types::Node::Enum(introspect_enum(
-            &item.ast.ident,
+        Data::Enum(ref data) => types::Node {
+            ident: item.ast.ident.to_string(),
             features,
-            data,
-            items,
-            tokens,
-        )),
-        Data::Struct(ref data) => types::Node::Struct(introspect_struct(
-            &item.ast.ident,
+            data: types::Data::Enum(introspect_enum(data, items, tokens)),
+        },
+        Data::Struct(ref data) => types::Node {
+            ident: item.ast.ident.to_string(),
             features,
-            data,
-            items,
-            tokens,
-        )),
+            data: {
+                if data.fields.iter().all(|f| is_pub(&f.vis)) {
+                    types::Data::Struct(introspect_struct(data, items, tokens))
+                } else {
+                    types::Data::Private
+                }
+            },
+        },
         Data::Union(..) => panic!("Union not supported"),
     }
 }
 
 fn introspect_enum(
-    ident: &Ident,
-    features: types::Features,
     item: &syn::DataEnum,
     items: &ItemLookup,
     tokens: &TokenLookup,
-) -> types::Enum {
-    let variants = item
-        .variants
+) -> Vec<types::Variant> {
+    item.variants
         .iter()
         .map(|variant| {
             let fields = match &variant.fields {
@@ -89,24 +88,15 @@
 
             types::Variant::new(variant.ident.to_string(), fields)
         })
-        .collect();
-
-    types::Enum::new(ident.to_string(), features, variants)
+        .collect()
 }
 
 fn introspect_struct(
-    ident: &Ident,
-    features: types::Features,
     item: &syn::DataStruct,
     items: &ItemLookup,
     tokens: &TokenLookup,
-) -> types::Struct {
-    let all_fields_pub = item.fields.iter().all(|field| is_pub(&field.vis));
-    if !all_fields_pub {
-        return types::Struct::new(ident.to_string(), features, IndexMap::new());
-    }
-
-    let fields = match &item.fields {
+) -> IndexMap<String, types::Type> {
+    match &item.fields {
         syn::Fields::Named(fields) => fields
             .named
             .iter()
@@ -119,9 +109,7 @@
             .collect(),
         syn::Fields::Unit => IndexMap::new(),
         _ => panic!("Struct representation not supported"),
-    };
-
-    types::Struct::new(ident.to_string(), features, fields)
+    }
 }
 
 fn introspect_type(item: &syn::Type, items: &ItemLookup, tokens: &TokenLookup) -> types::Type {
diff --git a/codegen/src/types.rs b/codegen/src/types.rs
index 3cbc0b2..58ba448 100644
--- a/codegen/src/types.rs
+++ b/codegen/src/types.rs
@@ -1,4 +1,5 @@
 use indexmap::IndexMap;
+use serde::{Deserialize, Deserializer};
 
 use std::collections::BTreeMap;
 use std::ops;
@@ -10,25 +11,24 @@
 }
 
 #[derive(Debug, Clone, Serialize)]
-#[serde(tag = "node", rename_all = "lowercase")]
-pub enum Node {
-    Struct(Struct),
-    Enum(Enum),
+pub struct Node {
+    pub ident: String,
+    pub features: Features,
+    #[serde(
+        flatten,
+        skip_serializing_if = "is_private",
+        deserialize_with = "de_data"
+    )]
+    pub data: Data,
 }
 
 #[derive(Debug, Clone, Serialize)]
-pub struct Struct {
-    pub ident: String,
-    pub features: Features,
-    #[serde(skip_serializing_if = "IndexMap::is_empty")]
-    pub fields: IndexMap<String, Type>,
-}
-
-#[derive(Debug, Clone, Serialize)]
-pub struct Enum {
-    pub ident: String,
-    pub features: Features,
-    pub variants: Vec<Variant>,
+pub enum Data {
+    Private,
+    #[serde(rename = "fields")]
+    Struct(IndexMap<String, Type>),
+    #[serde(rename = "variants")]
+    Enum(Vec<Variant>),
 }
 
 #[derive(Debug, Clone, Serialize)]
@@ -76,42 +76,6 @@
     any: Vec<String>,
 }
 
-impl Node {
-    pub fn ident(&self) -> &str {
-        match self {
-            Node::Struct(i) => &i.ident,
-            Node::Enum(i) => &i.ident,
-        }
-    }
-
-    pub fn features(&self) -> &Features {
-        match self {
-            Node::Struct(i) => &i.features,
-            Node::Enum(i) => &i.features,
-        }
-    }
-}
-
-impl Struct {
-    pub fn new(ident: String, features: Features, fields: IndexMap<String, Type>) -> Struct {
-        Struct {
-            ident,
-            features,
-            fields,
-        }
-    }
-}
-
-impl Enum {
-    pub fn new(ident: String, features: Features, variants: Vec<Variant>) -> Enum {
-        Enum {
-            ident,
-            features,
-            variants,
-        }
-    }
-}
-
 impl Variant {
     pub fn new(ident: String, fields: Vec<Type>) -> Variant {
         Variant { ident, fields }
@@ -164,3 +128,18 @@
         self.any.index(index)
     }
 }
+
+fn is_private(data: &Data) -> bool {
+    match data {
+        Data::Private => true,
+        Data::Struct(_) | Data::Enum(_) => false,
+    }
+}
+
+fn de_data<'de, D>(deserializer: D) -> Result<Data, D::Error>
+where
+    D: Deserializer<'de>,
+{
+    let option = Option::deserialize(deserializer)?;
+    Ok(option.unwrap_or(Data::Private))
+}