Crate noml

Crate noml 

Source
Expand description

§NOML - Nested Object Markup Language

NOML is a modern configuration language that combines the simplicity of TOML with advanced features like environment variables, file inclusion, variable interpolation, native types, HTTP includes, and schema validation.

§Quick Start

use noml::{parse, Value};

let source = r#"
    name = "my-app"
    version = "1.0.0"
    debug = true
     
    database_url = env("DATABASE_URL", "sqlite:memory:")
     
    max_file_size = @size("10MB")
    timeout = @duration("30s")
    server_ip = @ip("127.0.0.1")
     
    [server]
    host = "0.0.0.0"
    port = 8080
     
    [database]
    host = "localhost"
    port = 5432
"#;

let config = parse(source)?;

// Access values with type safety
assert_eq!(config.get("name").unwrap().as_string().unwrap(), "my-app");
assert_eq!(config.get("server.port").unwrap().as_integer().unwrap(), 8080);

§Core Features

  • 🔧 TOML-compatible syntax with extended functionality
  • 🌍 Environment variables via env("VAR_NAME", "default")
  • 📁 File inclusion via include "path/to/file.noml"
  • 🌐 HTTP includes via include "https://example.com/config.noml"
  • 🔗 Variable interpolation via "Hello ${name}!"
  • ⚡ Native types like @size("10MB"), @duration("30s"), @ip("127.0.0.1")
  • ✅ Schema validation for type safety and error prevention
  • 💬 Comment preservation for tooling and round-trip editing
  • 🎯 Detailed error reporting with precise source locations
  • 🚀 Zero-copy parsing for optimal performance
  • 🔄 Async support with tokio integration

§Native Types

NOML includes built-in support for common configuration types:

use noml::parse;

let config = parse(r#"
    max_upload = @size("100MB")
    cache_size = @size("2GB")
     
    timeout = @duration("30s")
    retry_delay = @duration("5m")
     
    api_endpoint = @url("https://api.example.com/v1")
"#)?;

// Native types are automatically converted to appropriate Rust types

§Advanced Configuration Management

use noml::{Config, Schema, FieldType, SchemaBuilder};

// Load configuration with schema validation
let config = Config::from_string(r#"
    app_name = "my-service"
    port = 8080
    debug = false
     
    [database]
    host = "localhost"
    max_connections = 100
"#)?;

// Define and validate schema
let schema = SchemaBuilder::new()
    .require_string("app_name")
    .require_integer("port")
    .optional_bool("debug")
    .build();

config.validate_schema(&schema)?;

// Access with type safety
let port = config.get("port").unwrap().as_integer()?;
let debug = config.get("debug").unwrap_or(&noml::Value::Bool(false)).as_bool()?;

§Async Support

Enable the async feature for non-blocking operations:

[dependencies]
noml = { version = "0.9", features = ["async"] }
use noml::{parse_async, Config};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Parse with HTTP includes
    let config = parse_async(r#"
        app_name = "my-app"
         
        include "https://config-server.com/common.noml"
    "#).await?;
     
    // Async file operations
    let mut config = Config::load_async("config.noml").await?;
    config.set("updated_at", chrono::Utc::now().to_rfc3339())?;
    config.save_async().await?;
     
    Ok(())
}

§Error Handling

NOML provides detailed error information for debugging:

use noml::parse;

let result = parse(r#"
    invalid_syntax = [  # Missing closing bracket
"#);

match result {
    Err(e) => {
        println!("Parse error: {}", e);
        // Error contains source location information
    }
    Ok(_) => unreachable!(),
}

§Advanced Usage

use noml::{Resolver, ResolverConfig, parse_string};
use std::collections::HashMap;

// Custom environment variables
let mut env_vars = HashMap::new();
env_vars.insert("APP_NAME".to_string(), "my-app".to_string());

// Custom resolver configuration
let config = ResolverConfig {
    env_vars: Some(env_vars),
    allow_missing_env: true,
    ..Default::default()
};

let mut resolver = Resolver::with_config(config);
let document = parse_string(r#"name = env("APP_NAME")"#, None)?;
let value = resolver.resolve(&document)?;

assert_eq!(value.get("name").unwrap().as_string().unwrap(), "my-app");

Re-exports§

pub use config::Config;
pub use error::NomlError;
pub use error::Result;
pub use parser::ast::AstNode;
pub use parser::parse_file;
pub use parser::parse_string;
pub use parser::Document;
pub use resolver::NativeResolver;
pub use resolver::Resolver;
pub use resolver::ResolverConfig;
pub use serializer::serialize_document;
pub use serializer::serialize_document_with_options;
pub use serializer::Serializer;
pub use value::Value;
pub use schema::FieldSchema;
pub use schema::FieldType;
pub use schema::Schema;
pub use schema::SchemaBuilder;

Modules§

config
NOML Configuration Management
error
Error Handling
macros
NOML Macros
parser
NOML Parser Module
resolver
NOML Resolver
schema
NOML Schema Validation
serializer
Format-Preserving NOML Serializer
value
NOML Value System

Macros§

noml_value
Create a NOML value using a convenient macro syntax

Functions§

modify_preserving
Modify a NOML document while preserving formatting.
parse
Parse NOML from a string and resolve all dynamic features
parse_from_file
Parse NOML from a file and resolve all dynamic features
parse_preserving
Create a NOML value using a convenient macro syntax
parse_preserving_from_file
Parse NOML from a file with full format preservation.
parse_raw
Parse NOML from a string without resolving dynamic features
parse_raw_from_file
Parse NOML from a file without resolving dynamic features
save_preserving
Save a NOML document to a file with format preservation.
validate
Validate NOML syntax without parsing into values