bookspec is a specification first: every rule normative and sourced,
every rule demonstrated by concrete examples, and any accounting engine
checked against them through one interface (D-34). This moves the
examples' schema out of the test that happened to parse them and makes
the interface explicit.
- `bookspec/checker` (its own workspace, inside this repository until
the harness is stable): `Example`, `Setup`, `Event`, `Expect`, `Query`,
`Refusal` and the TOML parser, every structure `deny_unknown_fields`,
with `Example::from_toml` checking the cross-field shape (closed lists,
exact decimal amounts, dates, one event kind, `reverses` counting from
1, the shares summing to 1, commands accepted or refused and not both).
- `Engine` (`load`, `apply`, `query`, `claims`) and `Refusal { rule,
reason }`; the refusal's rule is the bookspec identifier, so a refusal
names the rule it belongs to. `Claims` also carries the option an
engine picks for a CHOICE rule.
- `bookspec/runner` drives any `Engine` through the examples and reports
per rule: pass, fail, not claimed. It compares the answer itself; an
engine is never asked whether it agrees with itself.
- `crates/ledger-core/tests/bookspec.rs` is the wanderbooks adapter over
`Store`: the loader parses through the checker, its own checks are
unchanged, and the new `the_runner_reports_every_claimed_rule_green`
runs all 56 examples through the trait. The IIF appendix is answered
through `ledger_package::export_iif`, the code its own test checks.
- `just check` runs the bookspec workspace's fmt, clippy, rustdoc and
tests too, so an example cannot rot (D-34, Z-17).
The plan's trait has four methods and no path for an example's
`[[changes]]` (a rename, the policy, an asset correction), which post no
entry: `change` is the fifth, required so an engine cannot skip those
examples silently. This is reported to the EM as the one departure.
One money rule holds here too: an answer's figures are exact decimal
strings, never JSON numbers, so no engine can round a cent through a
float.
39 lines
1.2 KiB
TOML
39 lines
1.2 KiB
TOML
[package]
|
|
name = "ledger-core"
|
|
version = "0.1.0"
|
|
edition.workspace = true
|
|
license.workspace = true
|
|
repository.workspace = true
|
|
description = "Canonical double-entry bookkeeping semantics: the engine everything else is built on."
|
|
|
|
[dependencies]
|
|
rust_decimal = { version = "1", features = ["serde"] }
|
|
thiserror = "2"
|
|
chrono = { version = "0.4", features = ["serde"] }
|
|
serde = { version = "1", features = ["derive"] }
|
|
serde_json = "1"
|
|
# company.timezone: IANA names, with the zone database built in so every
|
|
# computer reads a name the same way.
|
|
jiff = { version = "0.2.35", default-features = false, features = ["std", "tz-system", "tzdb-bundle-always"] }
|
|
|
|
[lints]
|
|
workspace = true
|
|
|
|
[dev-dependencies]
|
|
criterion = "0.8.2"
|
|
proptest = "1"
|
|
# bookspec examples are TOML (tests/bookspec.rs), run on the engine's own
|
|
# store (a dev-dependency only: the store depends on this crate), each in
|
|
# a directory of its own.
|
|
toml = "1"
|
|
bookspec-checker = { path = "../../bookspec/checker" }
|
|
bookspec-runner = { path = "../../bookspec/runner" }
|
|
ledger-package = { path = "../ledger-package" }
|
|
ledger-import = { path = "../ledger-import" }
|
|
ledger-store = { path = "../ledger-store" }
|
|
tempfile = "3"
|
|
|
|
[[bench]]
|
|
name = "books"
|
|
harness = false
|