schemafy 0.6.0

Generates serializeable Rust types from a json schema
Documentation
// This would be nice once it stabilizes:
// https://github.com/rust-lang/rust/issues/44732
// #![feature(external_doc)]
// #![doc(include = "../README.md")]

//! This is a Rust crate which can take a [json schema (draft
//! 4)](http://json-schema.org/) and generate Rust types which are
//! serializable with [serde](https://serde.rs/). No checking such as
//! `min_value` are done but instead only the structure of the schema
//! is followed as closely as possible.
//!
//! As a schema could be arbitrarily complex this crate makes no
//! guarantee that it can generate good types or even any types at all
//! for a given schema but the crate does manage to bootstrap itself
//! which is kind of cool.
//!
//! ## Example
//!
//! Generated types for VS Codes [debug server protocol][]: <https://docs.rs/debugserver-types>
//!
//! [debug server protocol]:https://code.visualstudio.com/docs/extensions/example-debuggers
//!
//! ## Usage
//!
//! Rust types can be generated by passing a path to a JSON schema to the [`schemafy!`]
//! procedural macro.
//!
//! ```rust
//! extern crate serde;
//! extern crate schemafy_core;
//! extern crate serde_json;
//!
//! use serde::{Serialize, Deserialize};
//!
//! schemafy::schemafy!(
//!     "tests/nested.json"
//! );
//!
//! schemafy::schemafy!(
//!     root: Schema // Optional name for the root type (if one exists)
//!     "schemafy_lib/src/schema.json"
//! );
//!
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//!     let nested: Defnested = serde_json::from_str(r#"{ "append": "abc" }"#)?;
//!     assert_eq!(nested.append, Some("abc".to_string()));
//!     Ok(())
//! }
//! ```

/// Generate Rust types from a JSON schema.
///
/// If the `root` parameter is supplied, then a type will be
/// generated from the root of the schema.
///
/// ```rust
/// extern crate serde;
/// extern crate schemafy_core;
/// extern crate serde_json;
///
/// use serde::{Serialize, Deserialize};
///
/// schemafy::schemafy!(
///     root: MyRoot // Optional name for the root type (if one exists)
///     "tests/nested.json"
/// );
///
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
///     let nested: Defnested = serde_json::from_str(r#"{ "append": "abc" }"#)?;
///     assert_eq!(nested.append, Some("abc".to_string()));
///     Ok(())
/// }
/// ```
#[proc_macro]
pub fn schemafy(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let def = syn::parse_macro_input!(tokens as Def);
    let root_name = def.root;
    let input_file = def.input_file.value();
    schemafy_lib::Generator::builder()
        .with_root_name(root_name)
        .with_input_file(&input_file)
        .build()
        .generate()
        .into()
}

struct Def {
    root: Option<String>,
    input_file: syn::LitStr,
}

impl syn::parse::Parse for Def {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let root = if input.peek(syn::Ident) {
            let root_ident: syn::Ident = input.parse()?;
            if root_ident != "root" {
                return Err(syn::Error::new(root_ident.span(), "Expected `root`"));
            }
            input.parse::<syn::Token![:]>()?;
            Some(input.parse::<syn::Ident>()?.to_string())
        } else {
            None
        };
        Ok(Def {
            root,
            input_file: input.parse()?,
        })
    }
}