jsonforge 0.1.0

A Rust procedural macro for generating JSON schema validators from Rust types
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Ident, Visibility};

use crate::schema::types::{SchemaType, StructSchema};

use super::util::make_ident;

/// Generate a struct definition token stream.
///
/// In embedded mode, string fields use `&'static str` and arrays use `&'static [T]`.
pub fn struct_def(
    vis: &Visibility,
    name: &Ident,
    schema: &StructSchema,
    embedded: bool,
) -> TokenStream {
    let fields = schema.fields.iter().map(|f| {
        let field_ident = make_ident(&f.rust_name);
        let ty = schema_type_tokens(&f.ty, embedded);
        quote! { pub #field_ident: #ty }
    });

    if embedded {
        quote! {
            #[derive(Debug, Clone, Copy)]
            #vis struct #name {
                #(#fields,)*
            }
        }
    } else {
        quote! {
            #[derive(Debug, Clone)]
            #vis struct #name {
                #(#fields,)*
            }
        }
    }
}

/// Convert a [`SchemaType`] to its Rust token representation.
pub fn schema_type_tokens(ty: &SchemaType, embedded: bool) -> TokenStream {
    match ty {
        SchemaType::Bool => quote! { bool },
        SchemaType::Integer => quote! { i64 },
        SchemaType::Float => quote! { f64 },
        SchemaType::Str => {
            if embedded {
                quote! { &'static str }
            } else {
                quote! { ::std::string::String }
            }
        },
        SchemaType::Optional(inner) => {
            let inner_ts = schema_type_tokens(inner, embedded);
            quote! { ::std::option::Option<#inner_ts> }
        },
        SchemaType::Array(inner) => {
            let inner_ts = schema_type_tokens(inner, embedded);
            if embedded {
                quote! { &'static [#inner_ts] }
            } else {
                quote! { ::std::vec::Vec<#inner_ts> }
            }
        },
        SchemaType::Struct(nested) => {
            // Nested structs are inlined as anonymous structs isn't valid Rust;
            // we generate a placeholder name. In practice, top-level JSON objects
            // contain flat structs, so deep nesting is rare.
            // The caller is responsible for emitting any nested struct definitions
            // before this one.
            let _ = nested;
            if embedded {
                quote! { &'static str }
            } else {
                quote! { ::std::string::String }
            }
        },
    }
}