Zillowe FoundationZillowe Documentation

API Reference

Rust API reference for the zoko-parser crate

The zoko-parser crate provides a Rust API for parsing and manipulating Zoko files.

Installation

Add to your Cargo.toml:

[dependencies]
zoko-parser = "0.1"

Core Functions

parse_zoko

Parse a Zoko string into a ZokoFile structure.

use zoko_parser::parse_zoko;

fn main() {
    let zoko_content = r#"
name: "My Application",
version: "1.0.0",
debug: true,
"#;

    match parse_zoko(zoko_content) {
        Ok(zoko_file) => {
            println!("Parsed {} entries", zoko_file.entries.len());
        }
        Err(e) => {
            eprintln!("Parse error: {}", e);
        }
    }
}

Signature:

pub fn parse_zoko(input: &str) -> Result<ZokoFile, ParseErrorKind>

Parameters:

  • input: A string slice containing Zoko content

Returns:

  • Ok(ZokoFile): Successfully parsed Zoko file
  • Err(ParseErrorKind): Parse error with details

parse_zoko_to_json

Parse a Zoko string and convert it to a JSON string.

use zoko_parser::parse_zoko_to_json;

fn main() {
    let zoko_content = r#"
name: "My Application",
version: "1.0.0",
"#;

    match parse_zoko_to_json(zoko_content) {
        Ok(json) => {
            println!("{}", json);
        }
        Err(e) => {
            eprintln!("Conversion error: {}", e);
        }
    }
}

Signature:

pub fn parse_zoko_to_json(input: &str) -> Result<String, ParseErrorKind>

Parameters:

  • input: A string slice containing Zoko content

Returns:

  • Ok(String): JSON string representation
  • Err(ParseErrorKind): Parse error with details

Data Structures

ZokoFile

The main structure representing a parsed Zoko file.

pub struct ZokoFile {
    pub entries: IndexMap<String, Value>,
}

Fields:

  • entries: Ordered map of key-value pairs (uses IndexMap for order preservation)

Value

Enum representing all possible values in a Zoko file.

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Value {
    String(String),
    Number(f64),
    Boolean(bool),
    Array(Vec<Value>),
    Object(IndexMap<String, Value>),
    Null,
}

Variants:

  • String(String): Text data
  • Number(f64): Numeric values
  • Boolean(bool): True/false values
  • Array(Vec<Value>): Ordered list of values
  • Object(IndexMap<String, Value>): Key-value mappings
  • Null: Absence of value

ParseErrorKind

Error type for parsing errors.

#[derive(Debug, Error)]
pub enum ParseErrorKind {
    #[error("Unexpected character: {0}")]
    UnexpectedChar(char),

    #[error("Unexpected end of input")]
    UnexpectedEof,

    #[error("Invalid number format: {0}")]
    InvalidNumber(String),

    #[error("Invalid escape sequence: {0}")]
    InvalidEscape(String),

    #[error("Expected {expected}, found {found}")]
    Expected { expected: String, found: String },
}

Usage Examples

Reading and Parsing a File

use std::fs;
use zoko_parser::parse_zoko;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let content = fs::read_to_string("config.zo")?;
    let zoko_file = parse_zoko(&content)?;

    println!("Parsed {} entries", zoko_file.entries.len());
    Ok(())
}

Accessing Parsed Values

use zoko_parser::parse_zoko;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let content = r#"
name: "My App",
version: "1.0.0",
debug: true,
"#;

    let zoko_file = parse_zoko(content)?;

    // Access values
    if let Some(Value::String(name)) = zoko_file.entries.get("name") {
        println!("Application name: {}", name);
    }

    if let Some(Value::Boolean(debug)) = zoko_file.entries.get("debug") {
        println!("Debug mode: {}", debug);
    }

    Ok(())
}

Working with Nested Structures

use zoko_parser::parse_zoko;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let content = r#"
config: {
  database: {
    host: "localhost",
    port: 5432,
  },
},
"#;

    let zoko_file = parse_zoko(content)?;

    // Access nested values
    if let Some(Value::Object(config)) = zoko_file.entries.get("config") {
        if let Some(Value::Object(database)) = config.get("database") {
            if let Some(Value::String(host)) = database.get("host") {
                println!("Database host: {}", host);
            }
        }
    }

    Ok(())
}

Working with Arrays

use zoko_parser::parse_zoko;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let content = r#"
tags: ["production", "api"],
servers: [
  {host: "server1", port: 8080},
  {host: "server2", port: 8080},
],
"#;

    let zoko_file = parse_zoko(content)?;

    // Access array values
    if let Some(Value::Array(tags)) = zoko_file.entries.get("tags") {
        for tag in tags {
            if let Value::String(tag_str) = tag {
                println!("Tag: {}", tag_str);
            }
        }
    }

    // Access array of objects
    if let Some(Value::Array(servers)) = zoko_file.entries.get("servers") {
        for server in servers {
            if let Value::Object(server_obj) = server {
                if let Some(Value::String(host)) = server_obj.get("host") {
                    println!("Server: {}", host);
                }
            }
        }
    }

    Ok(())
}

Converting to Custom Types

use serde::Deserialize;
use zoko_parser::{parse_zoko, Value};

#[derive(Debug, Deserialize)]
struct Config {
    name: String,
    version: String,
    debug: bool,
}

impl TryFrom<zoko_parser::ZokoFile> for Config {
    type Error = String;

    fn try_from(zoko_file: zoko_parser::ZokoFile) -> Result<Self, Self::Error> {
        // Convert ZokoFile to your custom type
        let json = serde_json::to_string(&zoko_file)
            .map_err(|e| e.to_string())?;
        serde_json::from_str(&json)
            .map_err(|e| e.to_string())
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let content = r#"
name: "My App",
version: "1.0.0",
debug: true,
"#;

    let zoko_file = parse_zoko(content)?;
    let config: Config = zoko_file.try_into()?;

    println!("Config: {:?}", config);
    Ok(())
}

Error Handling

use zoko_parser::{parse_zoko, ParseErrorKind};

fn main() {
    let content = r#"
name: "My App",
version: 1.0.0,  // Invalid: should be "1.0.0"
"#;

    match parse_zoko(content) {
        Ok(zoko_file) => {
            println!("Successfully parsed");
        }
        Err(ParseErrorKind::InvalidNumber(num)) => {
            eprintln!("Invalid number format: {}", num);
        }
        Err(ParseErrorKind::Expected { expected, found }) => {
            eprintln!("Expected {}, found {}", expected, found);
        }
        Err(e) => {
            eprintln!("Parse error: {}", e);
        }
    }
}

Serialization

The ZokoFile and Value types implement Serialize and Deserialize from serde, making them compatible with JSON and other formats.

Serialize to JSON

use zoko_parser::parse_zoko;
use serde_json;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let content = r#"
name: "My App",
version: "1.0.0",
"#;

    let zoko_file = parse_zoko(content)?;
    let json = serde_json::to_string_pretty(&zoko_file)?;

    println!("{}", json);
    Ok(())
}

Deserialize from JSON

use zoko_parser::ZokoFile;
use serde_json;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let json = r#"{
        "entries": {
            "name": "My App",
            "version": "1.0.0"
        }
    }"#;

    let zoko_file: ZokoFile = serde_json::from_str(json)?;
    println!("Parsed {} entries", zoko_file.entries.len());

    Ok(())
}

Performance Considerations

  • Order Preservation: The use of IndexMap ensures order preservation but has slightly higher memory overhead than HashMap
  • Parsing Speed: The parser uses nom combinators for efficient parsing
  • Memory Usage: Large files are parsed entirely into memory; consider streaming for very large files

Thread Safety

The ZokoFile and Value types are Send and Sync when their contents are Send and Sync, making them safe to share across threads.

Integration with Other Crates

Use with Serde

use zoko_parser::{parse_zoko, Value};
use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct AppConfig {
    #[serde(flatten)]
    entries: IndexMap<String, Value>,
}

Use with Clap

use clap::Parser;
use zoko_parser::parse_zoko;

#[derive(Parser)]
struct Cli {
    #[arg(short, long)]
    config: String,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let cli = Cli::parse();
    let content = std::fs::read_to_string(cli.config)?;
    let zoko_file = parse_zoko(&content)?;

    println!("Parsed configuration");
    Ok(())
}

A software organization

2026 © All Rights Reserved.

  • All the content is available under CC BY-SA 4.0, expect where otherwise stated.

Last updated on