agent-first-data 0.18.0

A naming convention that lets AI agents understand your data without being told what it means, plus a CLI and library for reading and safely editing structured JSON, TOML, YAML, dotenv, and INI documents.
Documentation
use agent_first_data::{OutputFormat, OutputOptions, PlainStyle, redacted_value, render};
use serde_json::{Value, json};
use std::hint::black_box;
use std::io::{self, Write};
use std::time::Instant;

#[derive(Clone, Copy)]
struct InputSize {
    name: &'static str,
    rows: usize,
    iterations: u64,
}

const INPUT_SIZES: &[InputSize] = &[
    InputSize {
        name: "small",
        rows: 8,
        iterations: 20_000,
    },
    InputSize {
        name: "medium",
        rows: 128,
        iterations: 2_000,
    },
    InputSize {
        name: "large",
        rows: 2_048,
        iterations: 100,
    },
];

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut stdout = io::stdout().lock();
    writeln!(
        stdout,
        "{}",
        json!({
            "kind": "log",
            "log": {
                "benchmark": "afdata_formatters",
                "note": "Dependency-free baseline. Use for regression/profiling before choosing optimizations."
            }
        })
    )?;

    for size in INPUT_SIZES {
        let input = build_input(*size);
        run_case(&mut stdout, "json", *size, &input, bench_json)?;
        #[cfg(feature = "yaml")]
        run_case(&mut stdout, "yaml", *size, &input, bench_yaml)?;
        run_case(&mut stdout, "plain", *size, &input, bench_plain)?;
        run_case(&mut stdout, "redaction", *size, &input, bench_redaction)?;
    }

    Ok(())
}

fn run_case(
    stdout: &mut impl Write,
    operation: &str,
    size: InputSize,
    input: &Value,
    mut bench: impl FnMut(&Value) -> usize,
) -> io::Result<()> {
    let started = Instant::now();
    let mut observed_bytes = 0usize;
    for _ in 0..size.iterations {
        observed_bytes = observed_bytes.wrapping_add(black_box(bench(input)));
    }
    let elapsed = started.elapsed();
    let elapsed_ns = elapsed.as_nanos();
    let ns_per_iter = elapsed_ns / u128::from(size.iterations);
    writeln!(
        stdout,
        "{}",
        json!({
            "kind": "result",
            "result": {
                "operation": operation,
                "input_scale": size.name,
                "rows": size.rows,
                "iterations": size.iterations,
                "elapsed_ns": elapsed_ns,
                "ns_per_iter": ns_per_iter,
                "observed_bytes": observed_bytes
            }
        })
    )
}

fn bench_json(value: &Value) -> usize {
    render(
        black_box(value),
        OutputFormat::Json,
        &OutputOptions::default(),
    )
    .len()
}

#[cfg(feature = "yaml")]
fn bench_yaml(value: &Value) -> usize {
    render(
        black_box(value),
        OutputFormat::Yaml,
        &OutputOptions::default(),
    )
    .len()
}

fn bench_plain(value: &Value) -> usize {
    render(
        black_box(value),
        OutputFormat::Plain,
        &OutputOptions::default(),
    )
    .len()
}

fn bench_redaction(value: &Value) -> usize {
    let redacted = redacted_value(black_box(value));
    value_node_count(black_box(&redacted))
}

fn build_input(size: InputSize) -> Value {
    let rows = (0..size.rows)
        .map(|idx| {
            json!({
                "id": idx,
                "request_duration_ms": 25 + (idx % 9) * 125,
                "payload_size_bytes": 512 + idx * 257,
                "created_at_epoch_ms": 1_738_886_400_000i64 + (idx as i64 * 1_000),
                "price_usd_cents": 1_299 + idx,
                "success_rate_percent": 97.5,
                "api_key_secret": format!("bench-secret-{idx}"),
                "callback_url": format!("https://user:bench-secret-{idx}@example.test/callback?trace={idx}&token_secret=bench-secret-{idx}"),
                "tags": ["alpha", "beta", "gamma"],
                "metadata": {
                    "attempt_count": idx % 5,
                    "cache_ttl_s": 60,
                    "nested_secret": {
                        "raw": format!("nested-bench-secret-{idx}")
                    }
                }
            })
        })
        .collect::<Vec<_>>();

    let output_options = OutputOptions {
        redaction: Default::default(),
        style: PlainStyle::Readable,
    };

    json!({
        "dataset": size.name,
        "rows": rows,
        "output_options_debug": format!("{output_options:?}"),
        "root_secret": "bench-root-secret"
    })
}

fn value_node_count(value: &Value) -> usize {
    match value {
        Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => 1,
        Value::Array(values) => 1 + values.iter().map(value_node_count).sum::<usize>(),
        Value::Object(map) => 1 + map.values().map(value_node_count).sum::<usize>(),
    }
}