2 unstable releases
| 0.2.1 | Mar 7, 2026 |
|---|---|
| 0.1.0 | Mar 7, 2026 |
#579 in Configuration
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 fromstdin--base <json>: provide base JSON directly; only valid with--apply--base-file <path>: read base JSON from a file; use-to read fromstdin; 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