TOON (Token-Oriented Object Notation)

repository·main·Indexed 41 days ago

https://github.com/toon-format/toon-rust

A token-efficient JSON alternative for LLM prompts, providing a Rust library and CLI tool for encoding and decoding structured data. Features include key folding (v1.5) to reduce token usage, path expansion for decoding dotted keys, and a comprehensive TUI for real-time conversion and statistics. Supports serde for programmatic use and a `json_stream` feature for processing large files.

Tokens
12.1K
Snippets
45
Records
55
Agent score
51%

What's inside TOON

  1. Use Key Folding to reduce tokens (v1.5)

    main

    Key folding collapses single-key object chains into dotted paths (e.g., data: { metadata: { items: [...] } } becomes data.metadata.items[...]). This significantly reduces token usage for LLMs.

    To use it, enable KeyFoldingMode::Safe in EncodeOptions. You can also limit the depth of folding using with_flatten_depth(n).

    Safety Rules: Folding only applies if segments are valid identifiers (a-z, A-Z, 0-9, _), each level has exactly one key, and there are no collisions with sibling keys.

    use serde_json::json;
    use toon_format::{encode, EncodeOptions, KeyFoldingMode};
    
    let data = json!({
        "data": {
            "metadata": {
                "items": ["a", "b"]
            }
        }
    });
    
    // Enable key folding
    let opts = EncodeOptions::new().with_key_folding(KeyFoldingMode::Safe);
    let toon = encode(&data, &opts)?;
    // Output: data.metadata.items[2]: a,b
  2. Use Path Expansion to decode dotted keys (v1.5)

    main

    Path expansion allows the decoder to automatically expand dotted keys into nested objects. This allows you to provide highly compact TOON input that still maps to complex nested structures.

    To use it, enable PathExpansionMode::Safe in DecodeOptions.

    Example: Input a.b.c: 1 will be decoded into {"a": {"b": {"c": 1}}}.

    Note: Quoted keys (e.g., "c.d": 2) are treated as literal keys and will NOT be expanded.

    use serde_json::Value;
    use toon_format::{decode, DecodeOptions, PathExpansionMode};
    
    let toon = "a.b.c: 1\na.b.d: 2";
    
    // Enable path expansion
    let opts = DecodeOptions::new().with_expand_paths(PathExpansionMode::Safe);
    let json: Value = decode(toon, &opts)?;
    // Result: {"a": {"b": {"c": 1, "d": 2}}}
  3. Encode and decode data with TOON

    main

    The library provides high-level functions encode_default and decode_default that work with any type implementing serde::Serialize or serde::Deserialize (including custom structs and serde_json::Value).

    Using Custom Structs

    use serde::{Serialize, Deserialize};
    use toon_format::{encode_default, decode_default};
    
    #[derive(Serialize, Deserialize, Debug, PartialEq)]
    struct User {
        name: String,
        age: u32,
        email: String,
    }
    
    fn main() -> Result<(), toon_format::ToonError> {
        let user = User { name: "Alice".to_string(), age: 30, email: "alice@example.com".to_string() };
    
        // Encode to TOON
        let toon = encode_default(&user)?;
        
        // Decode back to struct
        let decoded: User = decode_default(&toon)?;
        assert_eq!(user, decoded);
        Ok(())
    }
    use serde::{Serialize, Deserialize};
    use toon_format::{encode_default, decode_default};
    
    #[derive(Serialize, Deserialize, Debug, PartialEq)]
    struct User {
        name: String,
        age: u32,
        email: String,
    }
    
    fn main() -> Result<(), toon_format::ToonError> {
        let user = User { name: "Alice".to_string(), age: 30, email: "alice@example.com".to_string() };
    
        let toon = encode_default(&user)?;
        let decoded: User = decode_default(&toon)?;
        assert_eq!(user, decoded);
        Ok(())
    }
  4. Install the TOON Format library or CLI

    main

    You can use TOON as a Rust library for programmatic encoding/decoding or as a standalone command-line tool.

    As a Library

    Add toon-format to your Cargo.toml dependencies:

    cargo add toon-format

    As a CLI Tool

    Install the toon binary globally using cargo:

    cargo install toon-format
    # Library
    cargo add toon-format
    
    # CLI
    cargo install toon-format
  5. Use the TOON TUI Split-Screen Editor

    main

    The TUI provides a split-screen environment for real-time conversion:

    • Input Panel (Left): Enter your JSON or TOON data here.
    • Output Panel (Right): View the converted result instantly.
    • Navigation: Use Tab to switch focus between the input and output panels.
    • Modes: Toggle between Encode Mode (JSON $\to$ TOON) and Decode Mode (TOON $\to$ JSON) using Ctrl+E or Ctrl+M.
    • Live Statistics: The bottom of the screen displays real-time token count comparisons, byte size comparisons, and percentage savings.
  6. Use the TOON TUI REPL mode

    main

    Press Ctrl+R to enter the REPL (Read-Eval-Print Loop) mode. This provides an interactive command-line interface for executing specific conversion commands and managing variables.

    REPL Commands

    CommandDescriptionExample
    encode <data>Encode JSON to TOONencode {"name": "Alice"}
    decode <data>Decode TOON to JSONdecode name: Alice
    let $var = <data>Store variablelet $config = {"port": 8080}
    varsList all variablesvars
    clearClear outputclear
    helpShow helphelp
    exitClose REPLexit

    Variable Usage

    • Use $_ to reference the last result.
    • Use $varname to reference a stored variable.
    • Example: let $data = {...} followed by encode $data.
    encode {"name": "Alice"}
    decode name: Alice
    let $config = {"port": 8080}
  7. How the Layout abstraction works

    main

    The Layout struct captures structural information about how a TOON document was encoded on the wire—metadata that is lost when a document is decoded into a standard serde_json::Value.

    Layout is keyed by JSON Pointers (RFC 6901). The empty pointer "" refers to the document root. Only nodes with layout-relevant information are recorded; primitive scalars are excluded.

    To obtain a Layout, use crate::decode::decode_with_layout.

    Note: This feature is experimental and requires the layout cargo feature to be enabled. The API is not part of the core TOON specification and may change.

    Key Concepts

    • JSON Pointer Access: Use .get("/path/to/node") to retrieve the layout of a specific node.
    • NodeLayout: Describes the specific encoding style used for an array (e.g., Tabular, List, or InlineArray).
    • FieldDescriptor: Used in Tabular layouts to describe the schema of the columns/fields declared in the header.
    # #[cfg(feature = "layout")]
    # fn main() -> Result<(), toon_format::ToonError> {
    use toon_format::{
        decode_with_layout,
        DecodeOptions,
    };
    
    let toon = "users[2]{id,name}:\n  1,Alice\n  2,Bob";
    let (_value, layout) = decode_with_layout(toon, &DecodeOptions::default())?;
    
    assert!(matches!(
        layout.get("/users"),
        Some(toon_format::NodeLayout::Tabular { .. })
    ));
    # Ok(())
    # }
    # #[cfg(not(feature = "layout"))]
    # fn main() {}
  8. Configure encoding and decoding settings in the TUI

    main

    Press Ctrl+P to open the Interactive Settings Panel. You can adjust parameters on-the-fly using the following controls:

    Encode Settings

    • Delimiter: Cycle through Comma (,), Tab (\t), or Pipe (|) by pressing d.
    • Indentation: Adjust spaces using + / = to increase and - / _ to decrease.
    • Key Folding: Toggle with f (e.g., collapses {a:{b:1}} $\to$ a.b: 1).
    • Flatten Depth: Control folding depth with [ / ] keys; press u for unlimited depth.
  9. Configure decoding settings in the TUI

    main

    Press Ctrl+P to open the Interactive Settings Panel. Use these keys to adjust decoding behavior:

    • Strict Mode: Toggle with s to enforce spec compliance.
    • Type Coercion: Toggle with c to auto-convert strings to their appropriate types.
    • Path Expansion: Toggle with p to expand folded paths (e.g., a.b:1 $\to$ {"a":{"b":1}}).
  10. Use the `toon` CLI tool

    main

    The toon CLI tool is used to encode JSON into the token-efficient TOON format or decode TOON back into JSON. It supports file input/output, stdin/stdout piping, and an interactive TUI mode.

    Basic Usage Patterns:

    • Encode JSON to TOON: toon input.json -o output.toon or cat data.json | toon -e
    • Decode TOON to JSON: toon input.toon or toon input.toon --json-indent 2
    • Interactive Mode: toon --interactive (launches a TUI)
    # Encode JSON to TOON
    toon input.json -o output.toon
    
    # Decode TOON to JSON with indentation
    toon input.toon --json-indent 2
    
    # Pipe JSON to TOON and see token savings
    cat data.json | toon -e --stats
    
    # Launch interactive TUI
    toon --interactive
  11. Explore TOON Rust examples

    main

    The repository includes several example files that demonstrate different usage patterns. You can run them using cargo run --example <name>.

    Key examples include:

    • structs.rs: Custom struct serialization.
    • tabular.rs: Tabular array formatting.
    • arrays.rs & arrays_of_arrays.rs: Various single and nested array formats.
    • objects.rs: Object encoding.
    • mixed_arrays.rs: Handling mixed-type arrays.
    • delimiters.rs: Using custom delimiters.
    • round_trip.rs: Testing encode/decode cycles.
    • decode_strict.rs: Using strict validation.
    • empty_and_root.rs: Handling edge cases like empty or root-level data.
    # Run all examples
    cargo run --example examples
  12. Handle TOON errors in Rust

    main

    All TOON operations return a Result<T, ToonError>. You should handle ToonError to manage specific failure cases such as syntax errors or data mismatches.

    Common Error Variants

    • ParseError: Syntax errors including line and column information.
    • LengthMismatch: Occurs when an array length does not match the header.
    • TypeMismatch: Occurs when an unexpected value type is encountered.
    • InvalidStructure: Occurs when the TOON structure is malformed.
    • SerializationError / DeserializationError: Failures during conversion processes.
    use serde_json::Value;
    use toon_format::{decode_strict, ToonError};
    
    match decode_strict::<Value>("items[3]: a,b") {
        Ok(value) => println!("Success: {:?}", value),
        Err(ToonError::LengthMismatch { expected, found, .. }) => {
            eprintln!("Array length mismatch: expected {}, found {}", expected, found);
        }
        Err(e) => eprintln!("Error: {}", e),
    }