# 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`:
```toml
[dependencies]
jsonforge = "0.1"
```
For embedded mode, enable the feature and add the `phf` runtime crate:
```toml
[dependencies]
jsonforge = { version = "0.1", features = ["embedded"] }
phf = "0.13"
```
## Usage
### Basic (runtime mode)
```rust
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
```rust
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
```rust
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
| `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
| 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
```rust
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
| `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
| `&'static str` | string | embedded |
| `String` | string | runtime |
| `bool` | boolean | both |
| `i8` … `i64`, `u8` … `u64`, `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
| 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
| Struct | `struct` | `<Name>` |
| Raw JSON | `const &str` | `<SCREAMING_SNAKE_NAME>_JSON` |
### Embedded mode
| 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
| `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