1 unstable release

Uses new Rust 2024

0.1.0 Feb 18, 2026

#396 in Procedural macros

Apache-2.0

74KB
1.5K SLoC

jsonforge

A Rust procedural macro that automatically generates typed Rust data structures from local JSON files.

Features

  • Zero-boilerplate type generation — point the macro at a JSON file and get a fully-typed struct instantly.
  • Schema inference — field types, optional fields (null-able), arrays, and nested objects are inferred automatically.
  • Runtime mode (default) — structs use owned types (String, Vec<T>); the raw JSON is embedded as a &str constant via include_str!.
  • Embedded mode (opt-in) — structs use &'static str / &'static [T] fields; data is baked into the binary as a perfect-hash-function (phf) map or a fixed-length static array — no runtime file I/O, no heap allocation.
  • Fine-grained visibility — control the visibility of the generated struct type and its data item independently.
  • Per-call embedded override — opt individual json_forge! calls in or out of embedded mode regardless of the Cargo feature flag.

Installation

Add to your Cargo.toml:

[dependencies]
jsonforge = "0.1"

For embedded mode, enable the feature and add the phf runtime crate:

[dependencies]
jsonforge = { version = "0.1", features = ["embedded"] }
phf = "0.13"

Usage

Basic (runtime mode)

use jsonforge::json_forge;

json_forge! {
    path = "data/content_types.json",
    name = "ContentTypeEntry",
}

fn main() {
    // A typed struct is generated from the JSON schema.
    // The raw JSON is available as a `&'static str` constant.
    let parsed: serde_json::Value = serde_json::from_str(CONTENT_TYPE_ENTRY_JSON).unwrap();
    println!("{}", parsed["3gp"]["mime_type"]);
}

Embedded mode

use jsonforge::json_forge;

json_forge! {
    path = "data/content_types.json",
    name = "ContentTypeEntry",
}

fn main() {
    // A static `::phf::Map` is generated — O(1) lookup, zero heap allocation.
    let entry = CONTENT_TYPE_ENTRY.get("3gp").unwrap();
    println!("{}", entry.mime_type.unwrap_or("unknown"));
}

All parameters

json_forge! {
    path     = "data/content_types.json",  // path relative to CARGO_MANIFEST_DIR (required)
    name     = "ContentTypeEntry",         // name for the generated struct (required)
    vis      = pub(crate),                 // struct visibility          (default: pub)
    data_vis = pub,                        // static / const visibility  (default: same as vis)
    embedded = false,                      // embedded mode override     (default: follows feature)
}

Visibility tokens

Token Meaning
pub Fully public
pub(crate) Visible within the current crate
pub(super) Visible to the parent module
pub(self) Private to the current module (same as private)
private No visibility modifier

embedded override

Value Behaviour
omitted Follows the embedded Cargo feature flag
false Forces runtime mode even when the feature is enabled
true Forces embedded mode; requires the embedded feature

#[json_forge_typed] — Validate and embed a hand-written struct

When you want full control over the struct definition (field names, types, derives, doc-comments, etc.) but still want compile-time JSON validation and automatic data embedding, use the #[json_forge_typed] attribute macro.

How it works

  1. You write the struct with the exact field types you need.
  2. The macro reads the JSON file at compile time and verifies that every value in it is compatible with the declared field types, reporting precise errors if not.
  3. A companion data item is emitted right after the struct definition.

Usage

use jsonforge::json_forge_typed;

// Embedded mode (requires `embedded` feature + phf = "0.13" in deps)
#[json_forge_typed(path = "data/content_types.json")]
#[derive(Clone, Copy)]
pub struct ContentTypeEntry {
    pub mime_type:   Option<&'static str>,
    pub group:       Option<&'static str>,
    pub description: Option<&'static str>,
    pub extensions:  &'static [&'static str],
    pub is_text:     bool,
}

// Runtime mode (no feature needed)
#[json_forge_typed(path = "data/content_types.json")]
#[derive(Clone)]
pub struct ContentTypeEntry {
    pub mime_type:   Option<String>,
    pub extensions:  Vec<String>,
    pub is_text:     bool,
}

Parameters

Key Description
path JSON file path relative to CARGO_MANIFEST_DIR (required)
data_vis Visibility of the emitted static/const (default: inherits the struct's vis)
embedded true/false override; defaults to the embedded feature flag

Supported field types

Rust type JSON type Mode
&'static str string embedded
String string runtime
bool boolean both
i8i64, u8u64, isize, usize integer number both
f32, f64 number both
&'static [T] array embedded
Vec<T> array runtime
Option<T> null or T both

Fields with an unrecognised type produce a compile error with the field name. JSON values incompatible with a field's declared type produce a compile error with the exact entry key and field path.

Generated items

JSON shape Embedded Item
Object of objects yes static NAME: ::phf::Map<&str, Struct>
Array yes static NAME_DATA: [Struct; N]
Single object yes static NAME: Struct
Any shape no const NAME_JSON: &'static str (inlined validated JSON)

Generated items (json_forge!)

Runtime mode

Item Kind Name pattern
Struct struct <Name>
Raw JSON const &str <SCREAMING_SNAKE_NAME>_JSON

Embedded mode

Top-level JSON shape Data item Name pattern
Object of objects static ::phf::Map<&str, Name> <SCREAMING_SNAKE_NAME>
Array static [T; N] <SCREAMING_SNAKE_NAME>_DATA
Single object static Name <SCREAMING_SNAKE_NAME>

Type inference rules

JSON type Rust type (runtime) Rust type (embedded)
string String &'static str
number (integer) i64 i64
number (float) f64 f64
boolean bool bool
array Vec<T> &'static [T]
object nested struct nested struct
null / missing Option<T> Option<T>

Fields that are null in any entry across the dataset are automatically promoted to Option<T>.

License

Apache-2.0

Dependencies

~0.4–1.2MB
~26K SLoC