Add a codegen error type to avoid unwraps
diff --git a/codegen/src/error.rs b/codegen/src/error.rs
new file mode 100644
index 0000000..0b3f191
--- /dev/null
+++ b/codegen/src/error.rs
@@ -0,0 +1,59 @@
+use std::fmt::{self, Display};
+use std::io;
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+#[derive(Debug)]
+pub enum Error {
+    Io(io::Error),
+    Json(serde_json::Error),
+    Rustfmt(rustfmt::ErrorKind),
+    Syn(syn::Error),
+    Toml(toml::de::Error),
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use self::Error::*;
+
+        match self {
+            Io(e) => write!(f, "{}", e),
+            Json(e) => write!(f, "{}", e),
+            Rustfmt(e) => write!(f, "{}", e),
+            Syn(e) => write!(f, "{}", e),
+            Toml(e) => write!(f, "{}", e),
+        }
+    }
+}
+
+impl std::error::Error for Error {}
+
+impl From<io::Error> for Error {
+    fn from(e: io::Error) -> Self {
+        Error::Io(e)
+    }
+}
+
+impl From<rustfmt::ErrorKind> for Error {
+    fn from(e: rustfmt::ErrorKind) -> Self {
+        Error::Rustfmt(e)
+    }
+}
+
+impl From<serde_json::Error> for Error {
+    fn from(e: serde_json::Error) -> Self {
+        Error::Json(e)
+    }
+}
+
+impl From<syn::Error> for Error {
+    fn from(e: syn::Error) -> Self {
+        Error::Syn(e)
+    }
+}
+
+impl From<toml::de::Error> for Error {
+    fn from(e: toml::de::Error) -> Self {
+        Error::Toml(e)
+    }
+}
diff --git a/codegen/src/file.rs b/codegen/src/file.rs
index c525938..c28f2d9 100644
--- a/codegen/src/file.rs
+++ b/codegen/src/file.rs
@@ -1,22 +1,21 @@
+use crate::error::Result;
 use proc_macro2::TokenStream;
 use std::fs::File;
 use std::io::Write;
 use std::path::Path;
 
-pub fn write<P: AsRef<Path>>(path: P, content: TokenStream) {
-    let mut file = File::create(path).unwrap();
+pub fn write<P: AsRef<Path>>(path: P, content: TokenStream) -> Result<()> {
+    let mut file = File::create(path)?;
     write!(
         file,
         "// THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT\n\n"
-    )
-    .unwrap();
+    )?;
     let mut config = rustfmt::Config::default();
     config.set().emit_mode(rustfmt::EmitMode::Stdout);
     config.set().verbose(rustfmt::Verbosity::Quiet);
     config.set().format_macro_matchers(true);
     config.set().normalize_doc_attributes(true);
     let mut session = rustfmt::Session::new(config, Some(&mut file));
-    session
-        .format(rustfmt::Input::Text(content.to_string()))
-        .unwrap();
+    session.format(rustfmt::Input::Text(content.to_string()))?;
+    Ok(())
 }
diff --git a/codegen/src/fold.rs b/codegen/src/fold.rs
index 7cadf20..b9f31bb 100644
--- a/codegen/src/fold.rs
+++ b/codegen/src/fold.rs
@@ -1,3 +1,4 @@
+use crate::error::Result;
 use crate::{file, full, gen};
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::quote;
@@ -225,7 +226,7 @@
     });
 }
 
-pub fn generate(defs: &Definitions) {
+pub fn generate(defs: &Definitions) -> Result<()> {
     let (traits, impls) = gen::traverse(defs, node);
     let full_macro = full::get_macro();
     file::write(
@@ -256,5 +257,6 @@
 
             #impls
         },
-    );
+    )?;
+    Ok(())
 }
diff --git a/codegen/src/gen.rs b/codegen/src/gen.rs
index db033f9..ba86fbf 100644
--- a/codegen/src/gen.rs
+++ b/codegen/src/gen.rs
@@ -1,7 +1,7 @@
 use inflections::Inflect;
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::quote;
-use syn_codegen::{Data, Features, Definitions, Node};
+use syn_codegen::{Data, Definitions, Features, Node};
 
 pub const TERMINAL_TYPES: &[&str] = &["Span", "Ident"];
 
diff --git a/codegen/src/json.rs b/codegen/src/json.rs
index 8e45308..e202bbe 100644
--- a/codegen/src/json.rs
+++ b/codegen/src/json.rs
@@ -1,16 +1,18 @@
-use syn_codegen::Definitions;
-
+use crate::error::Result;
 use std::fs;
 use std::path::Path;
+use syn_codegen::Definitions;
 
-pub fn generate(defs: &Definitions) {
-    let mut j = serde_json::to_string_pretty(&defs).unwrap();
+pub fn generate(defs: &Definitions) -> Result<()> {
+    let mut j = serde_json::to_string_pretty(&defs)?;
     j.push('\n');
 
-    let check: Definitions = serde_json::from_str(&j).unwrap();
+    let check: Definitions = serde_json::from_str(&j)?;
     assert_eq!(*defs, check);
 
     let codegen_root = Path::new(env!("CARGO_MANIFEST_DIR"));
     let json_path = codegen_root.join("../syn.json");
-    fs::write(json_path, j).unwrap();
+    fs::write(json_path, j)?;
+
+    Ok(())
 }
diff --git a/codegen/src/main.rs b/codegen/src/main.rs
index cc48ddd..3e27e1e 100644
--- a/codegen/src/main.rs
+++ b/codegen/src/main.rs
@@ -12,6 +12,7 @@
 #![recursion_limit = "128"]
 #![allow(clippy::needless_pass_by_value)]
 
+mod error;
 mod file;
 mod fold;
 mod full;
@@ -23,10 +24,21 @@
 mod visit;
 mod visit_mut;
 
+use crate::error::Result;
+use std::process;
+
 fn main() {
-    let defs = parse::parse();
-    json::generate(&defs);
-    fold::generate(&defs);
-    visit::generate(&defs);
-    visit_mut::generate(&defs);
+    if let Err(err) = do_main() {
+        let _ = eprintln!("error: {}", err);
+        process::exit(1);
+    }
+}
+
+fn do_main() -> Result<()> {
+    let defs = parse::parse()?;
+    json::generate(&defs)?;
+    fold::generate(&defs)?;
+    visit::generate(&defs)?;
+    visit_mut::generate(&defs)?;
+    Ok(())
 }
diff --git a/codegen/src/parse.rs b/codegen/src/parse.rs
index c6335b7..ba4bc5d 100644
--- a/codegen/src/parse.rs
+++ b/codegen/src/parse.rs
@@ -1,3 +1,4 @@
+use crate::error::Result;
 use crate::version;
 
 use indexmap::IndexMap;
@@ -21,13 +22,13 @@
 type TokenLookup = BTreeMap<String, String>;
 
 /// Parse the contents of `src` and return a list of AST types.
-pub fn parse() -> types::Definitions {
+pub fn parse() -> Result<types::Definitions> {
     let mut item_lookup = BTreeMap::new();
-    load_file(SYN_CRATE_ROOT, &[], &mut item_lookup).unwrap();
+    load_file(SYN_CRATE_ROOT, &[], &mut item_lookup)?;
 
-    let token_lookup = load_token_file(TOKEN_SRC).unwrap();
+    let token_lookup = load_token_file(TOKEN_SRC)?;
 
-    let version = version::get();
+    let version = version::get()?;
 
     let types = item_lookup
         .values()
@@ -39,11 +40,11 @@
         .map(|(name, ty)| (ty, name))
         .collect();
 
-    types::Definitions {
+    Ok(types::Definitions {
         version,
         types,
         tokens,
-    }
+    })
 }
 
 /// Data extracted from syn source
@@ -501,15 +502,13 @@
     ret
 }
 
-type Error = Box<::std::error::Error>;
-
 fn load_file<P: AsRef<Path>>(
     name: P,
     features: &[syn::Attribute],
     lookup: &mut ItemLookup,
-) -> Result<(), Error> {
+) -> Result<()> {
     let name = name.as_ref();
-    let parent = name.parent().ok_or("no parent path")?;
+    let parent = name.parent().expect("no parent path");
 
     let mut f = File::open(name)?;
     let mut src = String::new();
@@ -602,7 +601,7 @@
     Ok(())
 }
 
-fn load_token_file<P: AsRef<Path>>(name: P) -> Result<TokenLookup, Error> {
+fn load_token_file<P: AsRef<Path>>(name: P) -> Result<TokenLookup> {
     let name = name.as_ref();
     let mut f = File::open(name)?;
     let mut src = String::new();
@@ -623,5 +622,5 @@
         }
     }
 
-    Err("failed to parse Token macro".into())
+    panic!("failed to parse Token macro")
 }
diff --git a/codegen/src/version.rs b/codegen/src/version.rs
index fab7907..7ccca60 100644
--- a/codegen/src/version.rs
+++ b/codegen/src/version.rs
@@ -1,15 +1,16 @@
+use crate::error::Result;
 use semver::Version;
 use serde::Deserialize;
 
 use std::fs;
 use std::path::Path;
 
-pub fn get() -> Version {
+pub fn get() -> Result<Version> {
     let codegen_root = Path::new(env!("CARGO_MANIFEST_DIR"));
     let syn_cargo_toml = codegen_root.join("../Cargo.toml");
-    let manifest = fs::read_to_string(syn_cargo_toml).unwrap();
-    let parsed: Manifest = toml::from_str(&manifest).unwrap();
-    parsed.package.version
+    let manifest = fs::read_to_string(syn_cargo_toml)?;
+    let parsed: Manifest = toml::from_str(&manifest)?;
+    Ok(parsed.package.version)
 }
 
 #[derive(Debug, Deserialize)]
diff --git a/codegen/src/visit.rs b/codegen/src/visit.rs
index 988045d..122d57e 100644
--- a/codegen/src/visit.rs
+++ b/codegen/src/visit.rs
@@ -1,3 +1,4 @@
+use crate::error::Result;
 use crate::operand::{Borrowed, Operand, Owned};
 use crate::{file, full, gen};
 use proc_macro2::{Ident, Span, TokenStream};
@@ -195,7 +196,7 @@
     });
 }
 
-pub fn generate(defs: &Definitions) {
+pub fn generate(defs: &Definitions) -> Result<()> {
     let (traits, impls) = gen::traverse(defs, node);
     let full_macro = full::get_macro();
     file::write(
@@ -230,5 +231,6 @@
 
             #impls
         },
-    );
+    )?;
+    Ok(())
 }
diff --git a/codegen/src/visit_mut.rs b/codegen/src/visit_mut.rs
index 2202aad..2ec2961 100644
--- a/codegen/src/visit_mut.rs
+++ b/codegen/src/visit_mut.rs
@@ -1,3 +1,4 @@
+use crate::error::Result;
 use crate::operand::{Borrowed, Operand, Owned};
 use crate::{file, full, gen};
 use proc_macro2::{Ident, Span, TokenStream};
@@ -195,7 +196,7 @@
     });
 }
 
-pub fn generate(defs: &Definitions) {
+pub fn generate(defs: &Definitions) -> Result<()> {
     let (traits, impls) = gen::traverse(defs, node);
     let full_macro = full::get_macro();
     file::write(
@@ -229,5 +230,6 @@
 
             #impls
         },
-    );
+    )?;
+    Ok(())
 }