Thanks to visit codestin.com
Credit goes to lib.rs

#json #shell #cli

app ssof-cli

CLI for converting and applying Shell Safe Options Format data

2 unstable releases

0.2.1 Mar 7, 2026
0.1.0 Mar 7, 2026

#579 in Configuration

MIT license

59KB
2K SLoC

SHELL SAFE OPTIONS FORMAT

This repository contains a Rust implementation of SSOF (Shell Safe Options Format), along with a small command-line tool for converting between SSOF and JSON.

The workspace currently includes two crates:

  • ssof: a library for parsing SSOF into JSON, serializing JSON into normalized SSOF, and applying SSOF patches to existing JSON values.
  • ssof-cli: a command-line tool for conversion checks, quick manual testing, and shell-based workflows.

The format specification lives in shell-safe-options-format.md.

Purpose

SSOF is designed to express hierarchical configuration in a single shell-safe string while still supporting:

  • object and array construction
  • explicit path entry, backtracking, and backtrack-only navigation
  • boolean flags
  • array append and remove operations
  • incremental updates to existing configuration

This repository focuses on turning the specification into a testable, embeddable Rust implementation.

The ssof Library

ssof uses serde_json::Value as its core data model and currently exposes three main entry points:

  • parse_str(&str) -> Result<Value, Error>
  • apply_str(&mut Value, &str) -> Result<(), Error>
  • to_string(&Value) -> Result<String, Error>

These cover three related use cases:

  • parse a full SSOF string into JSON
  • apply an SSOF patch to an existing JSON value
  • serialize JSON into normalized SSOF

Example: SSOF to JSON

use ssof::parse_str;

let value = parse_str("system:,+debug,db.nodes:,10.0.0.1,10.0.0.2")?;

The resulting JSON is equivalent to:

{
  "system": {
    "debug": true,
    "db": {
      "nodes": ["10.0.0.1", "10.0.0.2"]
    }
  }
}

Example: Applying an Incremental Patch

use serde_json::json;
use ssof::apply_str;

let mut config = json!({
  "system": {
    "tags": ["prod", "test"]
  }
});

apply_str(&mut config, "system:,tags-=test,tags+=stable")?;

Example: Self-Path Operations

The explicit-current path . can target the current namespace itself.

use ssof::parse_str;

let root_object = parse_str(".:")?;
let root_array = parse_str(".::")?;
let nested = parse_str("items:,.0:,.+=prod,.+=stable")?;

These values are equivalent to:

{}
[]
{"items":[["prod","stable"]]}

Example: Backtracking Without Implicit null

Backtracking with a prefix : and no remaining body changes only the current path. It does not append a null value.

use ssof::parse_str;

let navigates_only = parse_str(".::,.0:,x=y,:,2")?;
let explicit_null = parse_str(".::,.0:,x=y,:=,2")?;

These values are equivalent to:

[{"x":"y"},2]
[{"x":"y"},null,2]

Example: JSON to SSOF

use serde_json::json;
use ssof::to_string;

let text = to_string(&json!({
  "system": {
    "debug": true,
    "nodes": ["10.0.0.1", "10.0.0.2"]
  }
}))?;

Serialization produces a normalized SSOF string. The goal is stable round-tripping and predictable output, not preservation of the original handwritten form.

The ssof-cli Tool

ssof-cli uses a single-command, flag-driven interface that works well in scripts and one-off conversions.

Basic Usage

cargo run -p ssof-cli -- --from ssof --to json --input 'system:,+debug'
cargo run -p ssof-cli -- --from json --to ssof --input '{"system":{"debug":true}}'

Reading from a File

cargo run -p ssof-cli -- --from ssof --to json --input-file input.ssof --pretty
printf '{"system":{"debug":true}}\n' | cargo run -p ssof-cli -- --from json --to ssof --input-file -

Applying a Patch to Existing JSON

cargo run -p ssof-cli -- \
  --from ssof \
  --to json \
  --apply \
  --base-file config.json \
  --input 'system:,db.nodes+=10.0.0.3'

Flags

  • --from <json|ssof>: input format
  • --to <json|ssof>: output format
  • --apply: treat the input as an SSOF patch and apply it to a base JSON value
  • --input <text>: provide input directly
  • --input-file <path>: read input from a file; use - to read from stdin
  • --base <json>: provide base JSON directly; only valid with --apply
  • --base-file <path>: read base JSON from a file; use - to read from stdin; only valid with --apply
  • --pretty: pretty-print JSON output

Input precedence is: --input > --input-file > stdin. When using --apply, --input-file - and --base-file - cannot be used together because they would both consume the same stdin stream.

Current Coverage

The implementation currently covers the core behavior described by the specification, including:

  • entry splitting and percent-decoding
  • key path parsing, explicit current-namespace addressing, and self-path operations
  • distinction between object keys and array indexes
  • container typing, path backtracking, and backtrack-only entries
  • execution of =, :, ::, +flag, -flag, +=, and -= operations
  • normalized serialization through serde_json::Value

The repository also includes unit tests for minimal construction, incremental updates, array indexing, self-path operations, forced values, and representative error cases.

Known Limits

The current implementation follows the existing specification closely, but it also enforces a few explicit limits:

  • empty strings are not currently serializable

Those are current format and v1 implementation boundaries, not accidental omissions.

Development

Run Tests

cargo test

Check Formatting

cargo fmt --all --check

Dependencies

~260–740KB
~15K SLoC