# shcl
**S**imple **H**ierarchical **C**onfig **L**anguage. Forgiving to write, predictable to read.
Values are stored as plain text and coerced only when *your* code asks for a type, so a config file can never silently decide that `NO` means `false`. A bad line is skipped or repaired with a diagnostic instead of taking down the whole file.
This crate is the reference implementation. Independent Go, Python, and C bindings are held byte-for-byte against it by a shared conformance corpus.
## Install
```sh
cargo add shcl
```
Zero dependencies. The crate also includes the CLI:
```sh
cargo install shcl
```
## Use
```rust
use shcl::{Document, FileStatus};
// Reads and parses in one call, and never fails: the document is usable
// either way, and the status separates missing from unreadable from
// parsed-with-errors.
let (mut doc, file_status) = Document::load_file("server.shcl");
if file_status == FileStatus::NotFound {
eprintln!("no config yet - using defaults");
}
// One call, a typed value, a visible fallback at the call site.
let limit = doc.get_int("site[example.com].max-upload-mb").unwrap_or(10);
// Or ask why a read failed: Good, Empty, NotFound, BadType, Multiple.
let r = doc.read_int("site[example.com].max-upload-mb");
if !r.ok() {
eprintln!("{:?} (raw text was {:?})", r.status, r.raw);
}
// Wildcards read across instances, with a status per slot.
let roots = doc.read_string_array("site[*].root");
// Writes through a temp file and a rename, so an interrupted save cannot
// truncate the config - and refuses if the load dropped a line this write
// would delete (save_file_lossy is the override).
// Setters are #[must_use]: a path that cannot be written writes nothing at
// all, and write_reason names which of the five reasons it hit.
let path = "site[example.com].max-upload-mb";
if !doc.set_int(path, limit * 2) {
eprintln!("not written: {:?}", doc.write_reason(path));
}
doc.save_file("server.shcl").unwrap();
```
`Document::parse` never fails, and neither does `load_file`. When you want a hard error instead, use `Document::parse_with(&text, Strictness::Strict)`.
Also here: `merge` for layered config (defaults, site, user), `validate` against a schema that is itself a SHCL file, a full writer, and a canonical formatter that preserves comments.
## Compatibility
Bindings are versioned in lockstep, so `2.x` is the same behavior in every language. `shcl = "2"` picks up minor and patch releases on its own and never crosses a major version.
## Docs
Language spec, formal grammar, and the other bindings: <https://github.com/jim-collier/shcl>
## License
MIT. SHCL™ is a trademark of Jim Collier - see the [trademark policy](https://github.com/jim-collier/shcl/blob/main/trademark.md).