JSON Config File to Rust Struct
Generate a strongly-typed Rust config struct from a JSON config file. Covers defaults, validation, and merging with environment variables.
Detailed Explanation
Loading a JSON Config File into a Typed Struct
Many CLI tools and services load runtime configuration from a JSON file. Converting that file's structure into a Rust struct gives you compile-time validation of every key and free defaults via serde.
Example config file
{
"server": {
"host": "0.0.0.0",
"port": 8080,
"workers": 4
},
"database": {
"url": "postgres://localhost/myapp",
"pool_size": 10,
"timeout_seconds": 30
},
"logging": {
"level": "info",
"format": "json"
},
"features": {
"experimental_search": false,
"rate_limiting": true
}
}
Generated Rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppConfig {
pub server: Server,
pub database: Database,
pub logging: Logging,
pub features: Features,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Server {
pub host: String,
pub port: i32,
pub workers: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Database {
pub url: String,
pub pool_size: i32,
pub timeout_seconds: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Logging {
pub level: String,
pub format: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Features {
pub experimental_search: bool,
pub rate_limiting: bool,
}
Adding defaults
For an optional file or partial config, wrap the struct in #[serde(default)]:
impl Default for Server {
fn default() -> Self {
Self {
host: "127.0.0.1".into(),
port: 8080,
workers: 1,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AppConfig {
#[serde(default)]
pub server: Server,
// ...
}
Now missing sections fall back to the Default implementation rather than failing.
Loading the file
let raw = std::fs::read_to_string("config.json")?;
let config: AppConfig = serde_json::from_str(&raw)?;
Promoting strings to enums
logging.level is a perfect candidate for an enum (Trace, Debug, Info, Warn, Error). After generating the initial struct, follow the enum-from-string recipe to lock down the valid values.
Use Case
Web servers, CLI tools, embedded systems, and game engines all benefit from typed config parsing. A typo in the config file becomes a clear deserialization error rather than a silent runtime bug.