A fast, safe Rust parser and writer for the YINI (Yet Another INI) configuration file format. YINI extends the traditional INI format with visual nesting, arrays, and improved readability.
- Zero dependencies: Pure Rust implementation with minimal dependencies
- Memory safe: Written in 100% safe Rust with no risk of buffer overflows
- Type support: Strings, integers, doubles, booleans, and arrays
- Comment support: Both
//
line comments and/* */
multiline comments - Error handling: Comprehensive error handling with detailed messages
- Round-trip: Parse and write YINI files without data loss
YINI uses a simple but powerful syntax:
# Root-level properties
app_name = 'MyApp'
version = '1.0.0'
debug = true
^ server # Accessed with parser.section("server")
host = 'localhost'
port = 8080
^^ database # Accessed with parser.section("server").section("database")
host = 'db.example.com'
port = 5432
^^^ credentials # Accessed with parser.section("server").section("database").section("credentials")
username = 'admin'
password = 'secret'
^ features
enabled = ['auth', 'logging', 'cache'] # Arrays with []
flags = [true, false, true]
numbers = [1, 2, 3, 4, 5]
- Strings:
'single quotes'
or"double quotes"
- Integers:
42
,-10
- Doubles:
3.14
,-2.5
- Booleans:
true
,false
,yes
,no
,on
,off
- Arrays:
[item1, item2, item3]
- Line comments:
// This is a comment
- End-of-line comments:
key = value // Comment here
- Multiline comments:
/* This is a multiline comment */
- Block comments spanning multiple lines:
/* * This is a longer comment * that spans multiple lines */
Add this to your Cargo.toml
:
[dependencies]
yini-rs = "0.1.0"
use yini_rs::{Parser, Value};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a new configuration
let mut config = Parser::new();
// Set values
config["app_name"] = Value::from("MyApplication");
config["port"] = Value::from(8080);
config["debug"] = Value::from(true);
// Create nested sections
config.section("database")["host"] = Value::from("localhost");
config.section("database")["port"] = Value::from(5432);
// Create arrays
let features = vec![
Value::from("auth"),
Value::from("logging")
];
config["features"] = Value::from(features);
// Write to file
config.write_file("config.yini")?;
// Read from file
let mut reader = Parser::new();
reader.parse_file("config.yini")?;
// Access values
println!("App: {}", reader["app_name"].as_string()?);
println!("Port: {}", reader["port"].as_int()?);
println!("Debug: {}", reader["debug"].as_bool()?);
// Access nested values
println!("DB Host: {}", reader.section("database")["host"].as_string()?);
Ok(())
}
Main struct for parsing and writing YINI files.
Methods:
parse_file(&mut self, filename: &str) -> Result<()>
- Parse a YINI fileparse_string(&mut self, content: &str) -> Result<()>
- Parse YINI content from stringwrite_file(&self, filename: &str) -> Result<()>
- Write configuration to filewrite_string(&self) -> String
- Write configuration to stringroot(&self) -> &Section
- Access the root sectionroot_mut(&mut self) -> &mut Section
- Access the root section mutablysection(&mut self, name: &str) -> &mut Section
- Access or create a section
Represents a configuration value with automatic type conversion.
Type checking:
is_string(&self) -> bool
is_int(&self) -> bool
is_double(&self) -> bool
is_bool(&self) -> bool
is_array(&self) -> bool
Value access:
as_string(&self) -> Result<String>
as_int(&self) -> Result<i32>
as_double(&self) -> Result<f64>
as_bool(&self) -> Result<bool>
as_array(&self) -> Result<Vec<Value>>
Represents a configuration section containing values and subsections.
Methods:
- Implements
Index<&str>
forvalue[key]
access at(&self, key: &str) -> Result<&Value>
- Safe value accesshas_value(&self, key: &str) -> bool
- Check if value existssection(&mut self, name: &str) -> &mut Section
- Access or create subsectionget_section(&self, name: &str) -> Result<&Section>
- Get subsection (read-only)has_section(&self, name: &str) -> bool
- Check if section exists
Error::ParseError
- Returned when parsing failsError::FileError
- Returned when file operations failError::TypeError
- Returned for type conversion errorsError::KeyNotFound
- Returned when accessing non-existent keysError::SectionNotFound
- Returned when accessing non-existent sections
use yini_rs::{Parser, Value};
let mut parser = Parser::new();
parser.parse_string(r#"
numbers = [1, 2, 3, 4, 5]
names = ['alice', 'bob', 'charlie']
mixed = [true, 42, 'hello', 3.14]
"#)?;
let numbers = parser["numbers"].as_array()?;
for num in &numbers {
println!("Number: {}", num.as_int()?);
}
use yini_rs::{Parser, Error};
let mut parser = Parser::new();
// Parse errors are returned as Results
match parser.parse_string("invalid syntax") {
Err(Error::ParseError { message }) => {
println!("Parse error: {}", message);
}
_ => {}
}
// Missing key errors
match parser.root().at("nonexistent") {
Err(Error::KeyNotFound { key }) => {
println!("Key '{}' not found", key);
}
_ => {}
}
use yini_rs::{Parser, Value};
let mut parser = Parser::new();
parser.parse_string(r#"
string_number = '42'
string_bool = 'true'
int_value = 100
"#)?;
// Automatic type conversion
let num: i32 = parser["string_number"].as_int()?; // "42" -> 42
let flag: bool = parser["string_bool"].as_bool()?; // "true" -> true
let text: String = parser["int_value"].as_string()?; // 100 -> "100"
Simply add this crate to your Cargo.toml
and you're ready to go! To run the tests:
# Run tests
cargo test
# Run examples
cargo run --example basic_usage
# Build documentation
cargo doc --open
- Rust 1.56.0 or later (2021 edition)
- No additional system dependencies
YINI-RS is designed for speed and efficiency:
- Fast parsing: Optimized for large configuration files
- Low memory usage: Minimal allocations during parsing
- Zero-copy: String values reference the original input when possible
Benchmark results on a 1000-line configuration file:
- Parse time: ~9ms
- Write time: ~0.4ms
Contributions are welcome! Please feel free to submit a Pull Request.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.