Crate serini

Crate serini 

Source
Expand description

§serini

A serde-based INI file parser that supports serialization and deserialization of Rust structs.

§Features

  • Serialize Rust structs to INI format - Nested structs become sections
  • Deserialize INI files to Rust structs - Type-safe parsing with automatic type conversion
  • Option handling - None values are serialized as commented lines
  • Escape sequences - Properly handles special characters in values
  • Section support - Nested structs are automatically converted to INI sections
  • Type safety - Leverages serde’s type system for safe conversions

§Quick Start

Add this to your Cargo.toml:

[dependencies]
serde = { version = "1.0", features = ["derive"] }
serini = "0.1"

§Basic Example

use serde::{Deserialize, Serialize};
use serini::{from_str, to_string};

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Config {
    name: String,
    port: u16,
    #[serde(skip_serializing_if = "Option::is_none")]
    debug: Option<usize>,
    database: Database,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Database {
    host: String,
    port: u16,
    username: String,
    password: Option<String>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config {
        name: "My Application".to_string(),
        port: 8080,
        debug: None,
        database: Database {
            host: "localhost".to_string(),
            port: 5432,
            username: "admin".to_string(),
            password: None,
        },
    };

    // Serialize to INI
    let ini_string = to_string(&config)?;
    println!("{}", ini_string);
    // Output:
    // name = My Application
    // port = 8080
    //
    // [database]
    // host = localhost
    // port = 5432
    // username = admin
    // ; password =

    // Deserialize from INI
    let parsed: Config = from_str(&ini_string)?;
    assert_eq!(config, parsed);

    Ok(())
}

§Section Handling

Nested structs automatically become INI sections:

use serde::{Deserialize, Serialize};
use serini::to_string;

#[derive(Serialize, Deserialize)]
struct ServerConfig {
    general: General,
    http: HttpConfig,
    database: DatabaseConfig,
}

#[derive(Serialize, Deserialize)]
struct General {
    name: String,
    debug: bool,
}

#[derive(Serialize, Deserialize)]
struct HttpConfig {
    host: String,
    port: u16,
    timeout: u64,
}

#[derive(Serialize, Deserialize)]
struct DatabaseConfig {
    url: String,
    max_connections: u32,
}

let config = ServerConfig {
    general: General {
        name: "MyServer".to_string(),
        debug: false,
    },
    http: HttpConfig {
        host: "0.0.0.0".to_string(),
        port: 8080,
        timeout: 30,
    },
    database: DatabaseConfig {
        url: "postgres://localhost/mydb".to_string(),
        max_connections: 100,
    },
};

let ini = to_string(&config)?;

Produces:

[general]
name = MyServer
debug = false

[http]
host = 0.0.0.0
port = 8080
timeout = 30

[database]
url = postgres://localhost/mydb
max_connections = 100

§Option Handling

Option<T> fields are handled specially:

  • Some(value) is serialized normally
  • None is serialized as a commented line
use serde::{Deserialize, Serialize};
use serini::to_string;

#[derive(Serialize, Deserialize)]
struct User {
    username: String,
    email: Option<String>,
    age: Option<u32>,
}

let user = User {
    username: "alice".to_string(),
    email: Some("alice@example.com".to_string()),
    age: None,
};

let ini = to_string(&user)?;

Produces:

username = alice
email = alice@example.com
; age =

§Escape Sequences

Special characters in values are automatically escaped:

CharacterEscaped
\\\
\n\n
\r\r
\t\t
"\"
;\;
#\#
use serde::{Deserialize, Serialize};
use serini::{from_str, to_string};

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct Message {
    text: String,
    note: String,
}

let msg = Message {
    text: "Hello\nWorld!".to_string(),
    note: "This has \"quotes\" and a ; semicolon".to_string(),
};

let ini = to_string(&msg)?;
// text = Hello\nWorld!
// note = This has \"quotes\" and a \; semicolon

let parsed: Message = from_str(&ini)?;
assert_eq!(msg, parsed);

§Supported Types

The following types are supported for serialization and deserialization:

  • Integers: i8, i16, i32, i64, u8, u16, u32, u64
  • Floats: f32, f64
  • Boolean: bool (serialized as true/false)
  • String: String, &str
  • Option: Option<T> where T is a supported type
  • Structs: Custom structs with named fields

§Limitations

The following serde types are not supported:

  • Sequences (Vec, arrays, etc.)
  • Tuples and tuple structs
  • Enums with variants
  • Maps (HashMap, BTreeMap, etc.)
  • Unit structs

Attempting to serialize or deserialize these types will result in an error.

§Error Handling

This crate uses anError type using thiserror to provide granular error variants:

use serde::{Deserialize, Serialize};
use serini::{from_str, Error};

#[derive(Deserialize)]
struct Config {
    port: u16,
}

let ini = "port = not_a_number";

match from_str::<Config>(ini) {
    Ok(_) => println!("Parsed successfully"),
    Err(Error::InvalidValue { typ, value }) => {
        println!("Invalid {} value: {}", typ, value);
    }
    Err(e) => println!("Error: {}", e),
}

§API Reference

§Functions

§to_string

Serializes a value to an INI string.

§from_str

Deserializes an INI string to a value.

§Advanced Example

Here’s a complete example showing various features:

use serde::{Deserialize, Serialize};
use serini::{from_str, to_string};

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct AppConfig {
    #[serde(rename = "app-name")]
    app_name: String,
    version: String,
    debug_mode: bool,
    max_connections: u32,
    timeout_seconds: Option<u64>,
     
    server: ServerSettings,
    database: DatabaseSettings,
    cache: Option<CacheSettings>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct ServerSettings {
    host: String,
    port: u16,
    #[serde(rename = "use-tls")]
    use_tls: bool,
    certificate_path: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct DatabaseSettings {
    #[serde(rename = "connection-string")]
    connection_string: String,
    pool_size: u32,
    timeout: u32,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct CacheSettings {
    backend: String,
    ttl_seconds: u64,
    max_entries: u64,
}

// Create a configuration
let config = AppConfig {
    app_name: "MyApp".to_string(),
    version: "1.0.0".to_string(),
    debug_mode: false,
    max_connections: 100,
    timeout_seconds: Some(30),
     
    server: ServerSettings {
        host: "0.0.0.0".to_string(),
        port: 8443,
        use_tls: true,
        certificate_path: Some("/etc/ssl/cert.pem".to_string()),
    },
     
    database: DatabaseSettings {
        connection_string: "postgres://user:pass@localhost/mydb".to_string(),
        pool_size: 20,
        timeout: 5,
    },
     
    cache: None,
};

// Serialize to INI
let ini_string = to_string(&config)?;
println!("Generated INI:\n{}", ini_string);

// Parse it back
let parsed: AppConfig = from_str(&ini_string)?;
assert_eq!(config, parsed);

// Example INI file that could be parsed
let ini_file = r#"
app-name = MyApp
version = 1.0.0
debug_mode = false
max_connections = 100
timeout_seconds = 30

[server]
host = 0.0.0.0
port = 8443
use-tls = true
certificate_path = /etc/ssl/cert.pem

[database]
connection-string = postgres://user:pass@localhost/mydb
pool_size = 20
timeout = 5

; cache =
"#;

let from_file: AppConfig = from_str(ini_file)?;
assert_eq!(config, from_file);

§License

This project is licensed under the MIT License - see the LICENSE file for details.

Re-exports§

pub use de::from_str;
pub use error::Error;
pub use ser::to_string;

Modules§

de
error
ser