A Rust implementation of HOCON
(Human-Optimized Config Object Notation), with full spec compliance, serde
integration, and support for advanced
features like substitutions and includes.
Add this to your Cargo.toml
:
[dependencies]
hocon-rs = "0.1"
# app.conf - Basic configuration
app-name = "My Application"
version = "1.0"
# Database configuration
database {
host = "localhost"
port = 5432
name = "mydb"
}
# Server configuration
server {
port = 8080
timeout = 30s # 30 seconds
}
# Feature flags
features {
logging = true
cache = false
}
fn main() -> Result<(), Error> {
let value: hocon_rs::Value = hocon_rs::Config::load("application.conf", None)?;
let host = value.get_by_path(["database", "host"]).unwrap();
println!("{}", host);
Ok(())
}
fn main() -> Result<(), Error> {
let value: hocon_rs::Value = hocon_rs::Config::parse_str("{name = mikai233}", None)?;
println!("{value}");
Ok(())
}
#[derive(Debug, Deserialize)]
struct Person {
name: String,
age: u32,
scores: Vec<i32>,
}
fn main() -> Result<(), Error> {
let person: Person = hocon_rs::Config::parse_str("{name = mikai233, age = 18, scores = [99, 100]}", None)?;
println!("{person:?}");
Ok(())
}
This library depends on serde_json
for JSON deserialization.
hocon_rs::Value
implements conversions to and from serde_json::Value
, making it easy to interoperate with other
serde-compatible libraries.
This library is still under active development. Most features are already implemented, but the public API may still change in future versions.
In HOCON, configurations can be loaded from the classpath. Since classpath is a Java-specific concept, the Rust implementation defines the classpath as a set of directory roots used to search for configuration files.
If you do not configure a classpath in ConfigOptions
, hocon-rs
will only search in the current working directory.
When parsing deeply nested objects or arrays, you may encounter a RecursionDepthExceeded
error.
This happens because hocon-rs
uses recursive functions to parse objects, and excessive recursion could cause stack
overflows.
The default depth limit is 64.
You can increase this limit via ConfigOptions
.
Substitution resolution has its own depth limit to avoid infinite recursion or stack overflows. This is usually not an issue unless your configuration comes from untrusted user input.
Substitutions are resolved as the last step in parsing. This means a substitution can refer to values defined later in the file or even in other included files.
For example:
substitution1.conf
a = hello
a = ${a}
a = ${b}
b = [1, 2]
b += ${a}
a = {}
substitution2.conf
b = hello
b = ${b}
b = ${a}
a = [1, 2]
a += ${b}
b = {}
In Java’s implementation:
-
substitution1.conf
→{"a":{},"b":[1,2,"hello"]}
-
substitution2.conf
→{"a":[1,2,{}],"b":{}}
In the Rust implementation, both examples produce a parse error. For clarity and consistency, avoid such tricky substitution patterns.
According to the HOCON spec, if a file extension is omitted, the loader attempts to parse all supported formats at the
given path (JSON
, JavaProperties
, HOCON
) and merge them into a single object.
You can customize merge priority using the comparison function defined in ConfigOptions
.
- Comments
- Root braces may be omitted
- Flexible key-value separators (
=
:
+=
) - Optional commas
- Whitespace handling
- Duplicate keys and object merging
- Unquoted strings
- Multi-line strings
- Value concatenation
- String concatenation
- Array and object concatenation
- Path expressions
- Paths as keys
- Substitutions
- Includes
- Conversion of numerically indexed objects into arrays
- Duration unit format
- Period unit format
- Size unit format
- More descriptive error messages
- Serialize to HOCON format
- Serialize raw HOCON text
- Parse and preserve comments
- Refactor recursive functions to iterative implementations
For detailed API documentation, see docs.rs
- Original Java implementation: lightbend/confg
- HOCON format specification: HOCON