merge: U0d, the reader crate (branch U0d, bb1eb842; D-61)
check / check (push) Canceled after 0s
two-user / two-user (push) Canceled after 0s

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
swaitsandClaude Fable 5.1 committed 2026-10-29 12:45:54 -06:00
commit 39c76d3fac
15 files changed
+344 -68

No files matched your search

+1 -1
View File
@@ -160,7 +160,7 @@ clone, with status. Rules:
`company.timezone`: "today" is computed by the caller and passed in; `company.timezone`: "today" is computed by the caller and passed in;
the core still reads no clock). New dependencies need justification in the core still reads no clock). New dependencies need justification in
the commit message. No I/O, no networking, no time-source in the core. the commit message. No I/O, no networking, no time-source in the core.
- Future crates (`ledger-store`, `ledger-import`, `ledger-oracle`, `books-app`) - Future crates (`ledger-store`, `ledger-import`, `ledger-reader`, `ledger-oracle`, `books-app`)
depend inward toward the core, never the reverse. See `docs/architecture.md`. depend inward toward the core, never the reverse. See `docs/architecture.md`.
## Working here ## Working here
Generated
+14 -5
View File
@@ -3989,13 +3989,10 @@ dependencies = [
"chrono", "chrono",
"csv", "csv",
"deunicode", "deunicode",
"hayro 0.8.0",
"hayro-interpret 0.8.0",
"kurbo",
"ledger-core", "ledger-core",
"ledger-reader",
"proptest", "proptest",
"qrcode", "qrcode",
"quick-xml 0.38.4",
"rqrr", "rqrr",
"rust_decimal", "rust_decimal",
"rust_xlsxwriter", "rust_xlsxwriter",
@@ -4058,6 +4055,18 @@ dependencies = [
"zip 9.0.2", "zip 9.0.2",
] ]
[[package]]
name = "ledger-reader"
version = "0.1.0"
dependencies = [
"hayro 0.8.0",
"hayro-interpret 0.8.0",
"kurbo",
"quick-xml 0.38.4",
"thiserror 2.0.21",
"zip 9.0.2",
]
[[package]] [[package]]
name = "ledger-store" name = "ledger-store"
version = "0.1.0" version = "0.1.0"
@@ -4068,7 +4077,7 @@ dependencies = [
"getrandom 0.2.17", "getrandom 0.2.17",
"hex", "hex",
"ledger-core", "ledger-core",
"ledger-import", "ledger-reader",
"libc", "libc",
"proptest", "proptest",
"rusqlite", "rusqlite",
+1 -1
View File
@@ -1,6 +1,6 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = ["crates/ledger-core", "crates/ledger-store", "crates/ledger-config", "crates/ledger-mcp", "crates/ledger-import", "crates/ledger-cli", "crates/ledger-gui", "crates/ledger-vault", "crates/ledger-host", "crates/ledger-package"] members = ["crates/ledger-core", "crates/ledger-store", "crates/ledger-config", "crates/ledger-mcp", "crates/ledger-import", "crates/ledger-reader", "crates/ledger-cli", "crates/ledger-gui", "crates/ledger-vault", "crates/ledger-host", "crates/ledger-package"]
[workspace.package] [workspace.package]
edition = "2024" edition = "2024"
+3 -11
View File
@@ -18,17 +18,8 @@ typst = "0.15.1"
serde_json = "1" serde_json = "1"
typst-layout = "0.15.1" typst-layout = "0.15.1"
qrcode = { version = "0.14.1", default-features = false, features = ["svg"] } qrcode = { version = "0.14.1", default-features = false, features = ["svg"] }
# Extraction (extract.rs, plan P6): a PDF's text, a page's pixels, an # The reader (extract.rs moved out: D-61); re-exported as `extract`.
# XLSX's cells. hayro (the renderer) and hayro-interpret (the text ledger-reader = { path = "../ledger-reader" }
# interpreter) are pure Rust and already the viewer's PDF reader, so the
# server and the viewer read a receipt through one implementation rather
# than two; `zip` and `quick-xml` read a spreadsheet's XML. None of them
# touches money: a number is kept as the decimal string the XML holds.
hayro = { version = "0.8", features = ["embed-fonts"] }
hayro-interpret = "0.8"
kurbo = "0.13"
zip = { version = "9.0.1", default-features = false, features = ["deflate"] }
quick-xml = "0.38"
# BSD-3-Clause: the IIF's transliteration to ASCII (iif.rs, rules.md B-34). # BSD-3-Clause: the IIF's transliteration to ASCII (iif.rs, rules.md B-34).
# Pinned exactly: a new table would change what a CPA's file says. # Pinned exactly: a new table would change what a CPA's file says.
deunicode = { version = "=1.6.0", default-features = false } deunicode = { version = "=1.6.0", default-features = false }
@@ -38,4 +29,5 @@ workspace = true
[dev-dependencies] [dev-dependencies]
proptest = "1" proptest = "1"
zip = { version = "9.0.1", default-features = false, features = ["deflate"] }
rqrr = { version = "0.11.0", default-features = false } rqrr = { version = "0.11.0", default-features = false }
+2 -1
View File
@@ -16,7 +16,8 @@
pub mod asset_register; pub mod asset_register;
pub mod bank_csv; pub mod bank_csv;
pub mod chart_csv; pub mod chart_csv;
pub mod extract; /// The one reader, now the `ledger-reader` crate (D-61), kept at its old path.
pub use ledger_reader as extract;
pub mod fonts; pub mod fonts;
pub mod iif; pub mod iif;
pub mod invoice_pdf; pub mod invoice_pdf;
+1 -1
View File
@@ -1,7 +1,7 @@
//! The one extractor (plan P6 step 1): a PDF's text and pages, and an //! The one extractor (plan P6 step 1): a PDF's text and pages, and an
//! XLSX's cells as exact decimal strings. //! XLSX's cells as exact decimal strings.
//! //!
//! Expected behavior comes from `crates/ledger-import/src/extract.rs`'s //! Expected behavior comes from `crates/ledger-reader/src/lib.rs`'s
//! INVARIANTS, which come from plan/served.md P6 step 1 and D-15, D-32 and //! INVARIANTS, which come from plan/served.md P6 step 1 and D-15, D-32 and
//! D-53. //! D-53.
+23
View File
@@ -0,0 +1,23 @@
[package]
name = "ledger-reader"
version = "0.1.0"
edition = "2024"
license = "MIT OR Apache-2.0"
description = "The one reader: a PDF's text and pages, an XLSX's cells as exact decimal strings (plan P6, D-61)."
[dependencies]
thiserror = "2"
# Extraction (plan P6): a PDF's text, a page's pixels, an XLSX's cells.
# hayro (the renderer) and hayro-interpret (the text interpreter) are pure
# Rust and already the viewer's PDF reader, so the server and the viewer
# read a receipt through one implementation rather than two; `zip` and
# `quick-xml` read a spreadsheet's XML. None of them touches money: a
# number is kept as the decimal string the XML holds.
hayro = { version = "0.8", features = ["embed-fonts"] }
hayro-interpret = "0.8"
kurbo = "0.13"
zip = { version = "9.0.1", default-features = false, features = ["deflate"] }
quick-xml = "0.38"
[lints]
workspace = true
File renamed without changes.
+2 -2
View File
@@ -8,11 +8,11 @@ description = "Durable persistence for wanderbooks: a hash-chained JSONL journal
[dependencies] [dependencies]
ledger-core = { path = "../ledger-core" } ledger-core = { path = "../ledger-core" }
# The one extractor (plan P6 step 1, D-53): the writer journals a file's # The one reader (plan P6 step 1, D-53, D-61: its own crate): the writer journals a file's
# text at attach time and on `wanderbooks books extract`, so the extractor # text at attach time and on `wanderbooks books extract`, so the extractor
# must be callable here rather than only from the server. It is pure and # must be callable here rather than only from the server. It is pure and
# deterministic (no I/O, no network, no time source). # deterministic (no I/O, no network, no time source).
ledger-import = { path = "../ledger-import" } ledger-reader = { path = "../ledger-reader" }
rusqlite = { version = "0.32", features = ["bundled"] } rusqlite = { version = "0.32", features = ["bundled"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
+6 -6
View File
@@ -45,7 +45,7 @@ use ledger_core::{
payroll::{PayrollAccounts, payroll_entry_plan, payroll_remit_plan}, payroll::{PayrollAccounts, payroll_entry_plan, payroll_remit_plan},
reports, reports,
}; };
use ledger_import::extract::{EXTRACTOR as E_EXTRACTOR, VERSION as E_VERSION}; use ledger_reader::{EXTRACTOR as E_EXTRACTOR, VERSION as E_VERSION};
use rusqlite::{Connection, OptionalExtension, params}; use rusqlite::{Connection, OptionalExtension, params};
use rust_decimal::Decimal; use rust_decimal::Decimal;
use serde_json::{Value, json}; use serde_json::{Value, json};
@@ -368,7 +368,7 @@ pub struct Extraction {
pub attachment_id: u64, pub attachment_id: u64,
/// The text's blob (its SHA-256), when there is text. /// The text's blob (its SHA-256), when there is text.
pub text_sha256: Option<String>, pub text_sha256: Option<String>,
/// What read it (`ledger_import::extract::EXTRACTOR`). /// What read it (`ledger_reader::EXTRACTOR`).
pub extractor: String, pub extractor: String,
/// That extractor's version. /// That extractor's version.
pub version: String, pub version: String,
@@ -4620,7 +4620,7 @@ impl Store {
if let Some(already) = self.extraction_for(attachment_id, E_EXTRACTOR, E_VERSION)? { if let Some(already) = self.extraction_for(attachment_id, E_EXTRACTOR, E_VERSION)? {
return Ok(already); return Ok(already);
} }
let got = ledger_import::extract::extract(bytes, mime_type, file_name); let got = ledger_reader::extract(bytes, mime_type, file_name);
let text_sha = match &got.text { let text_sha = match &got.text {
Some(text) => Some(self.blobs.put(text.as_bytes())?.0), Some(text) => Some(self.blobs.put(text.as_bytes())?.0),
None => None, None => None,
@@ -4644,9 +4644,9 @@ impl Store {
version: E_VERSION.into(), version: E_VERSION.into(),
pages: got.pages, pages: got.pages,
status: match got.status { status: match got.status {
ledger_import::extract::ExtractStatus::Ok => ExtractionStatus::Ok, ledger_reader::ExtractStatus::Ok => ExtractionStatus::Ok,
ledger_import::extract::ExtractStatus::None => ExtractionStatus::None, ledger_reader::ExtractStatus::None => ExtractionStatus::None,
ledger_import::extract::ExtractStatus::Failed => ExtractionStatus::Failed, ledger_reader::ExtractStatus::Failed => ExtractionStatus::Failed,
}, },
seq: line.seq, seq: line.seq,
}) })
+2 -2
View File
@@ -1471,8 +1471,8 @@ fn extraction_is_journaled_once_per_version() {
let x = s.extraction(id).unwrap().expect("an extraction record"); let x = s.extraction(id).unwrap().expect("an extraction record");
assert_eq!(x.status, ExtractionStatus::Ok); assert_eq!(x.status, ExtractionStatus::Ok);
assert_eq!(x.pages, 1); assert_eq!(x.pages, 1);
assert_eq!(x.extractor, ledger_import::extract::EXTRACTOR); assert_eq!(x.extractor, ledger_reader::EXTRACTOR);
assert_eq!(x.version, ledger_import::extract::VERSION); assert_eq!(x.version, ledger_reader::VERSION);
assert_eq!( assert_eq!(
s.extraction_text(id).unwrap().as_deref(), s.extraction_text(id).unwrap().as_deref(),
Some(String::from_utf8_lossy(text).as_ref()) Some(String::from_utf8_lossy(text).as_ref())
+14 -14
View File
@@ -31,8 +31,9 @@ the specification in `bookspec/` (which never depends on the engine).
| `ledger-core` | the semantics engine: money, chart, journal, `Ledger` commands, reports, periods, assets, schedules, parties, A/R and A/P, payroll, projects, settings and conventions, the kernel and its Kani harnesses | nothing | | `ledger-core` | the semantics engine: money, chart, journal, `Ledger` commands, reports, periods, assets, schedules, parties, A/R and A/P, payroll, projects, settings and conventions, the kernel and its Kani harnesses | nothing |
| `ledger-vault` | the TIN vault: XChaCha20-Poly1305 records under the book's key | nothing | | `ledger-vault` | the TIN vault: XChaCha20-Poly1305 records under the book's key | nothing |
| `ledger-config` | the `wanderbooks.toml` codec (adoption in, `books.export_settings` out) and the receipt policy until P4 moves it into the core | `ledger-core` | | `ledger-config` | the `wanderbooks.toml` codec (adoption in, `books.export_settings` out) and the receipt policy until P4 moves it into the core | `ledger-core` |
| `ledger-store` | the source of truth: the hash-chained JSONL journal, its format gate, the SQLite projection (schema 18; a file per schema), the blob store, backups, the access log, `Snapshot` for readers | `ledger-core`, `ledger-import` (the file reader, P6; `ledger-reader` after U0d, D-61) | | `ledger-store` | the source of truth: the hash-chained JSONL journal, its format gate, the SQLite projection (schema 18; a file per schema), the blob store, backups, the access log, `Snapshot` for readers | `ledger-core`, `ledger-reader` (P6, D-61) |
| `ledger-import` | formats: QuickBooks CSV in; IIF, XLSX, Typst PDFs (statements, the invoice, labels) out | `ledger-core` | | `ledger-reader` | the one reader: a PDF's text and pages, an XLSX's cells as exact decimal strings, plain text (P6; a plain library, in-process, D-61); `ledger-import` re-exports it as `extract` | none (`hayro`, `zip`, `quick-xml`) |
| `ledger-import` | formats: QuickBooks CSV in; IIF, XLSX, Typst PDFs (statements, the invoice, labels) out | `ledger-core`, `ledger-reader` |
| `ledger-package` | the shared read-only view over `Store` or `Snapshot` (`PackageBooks`): the CPA package, the 1099 worksheet, the invoice document, Needs attention | `ledger-core`, `ledger-store`, `ledger-config`, `ledger-import`, `ledger-vault` | | `ledger-package` | the shared read-only view over `Store` or `Snapshot` (`PackageBooks`): the CPA package, the 1099 worksheet, the invoice document, Needs attention | `ledger-core`, `ledger-store`, `ledger-config`, `ledger-import`, `ledger-vault` |
| `ledger-mcp` | the books server: the MCP tools (122), resources, the handbook, scopes, the door and the access log, backups' scheduling | all of the above | | `ledger-mcp` | the books server: the MCP tools (122), resources, the handbook, scopes, the door and the access log, backups' scheduling | all of the above |
| `ledger-host` | the host and its leases, the agent bridge (`wanderbooks mcp`), tokens in the OS keyring, the owner's commands, uploads and downloads | `ledger-store`, `ledger-mcp`, `ledger-config` | | `ledger-host` | the host and its leases, the agent bridge (`wanderbooks mcp`), tokens in the OS keyring, the owner's commands, uploads and downloads | `ledger-store`, `ledger-mcp`, `ledger-config` |
@@ -50,14 +51,13 @@ test `crates/ledger-core/tests/oracle_gnucash.rs` with `oracle/gnucash/`
(`PackageBooks` is the shared view; extraction is in-process in (`PackageBooks` is the shared view; extraction is in-process in
`ledger-import`, P6). `ledger-import`, P6).
Decided (Q-9 → D-61, 2026-10-29): P6 made `ledger-store` depend on Decided and done (Q-9 → D-61, unit U0d): P6 had made `ledger-store` depend
`ledger-import` so the writer can extract a file's text at attach time on `ledger-import` so the writer can extract a file's text at attach time,
and on the backfill command. That keeps the direction inward which pulled Typst, the fonts and hayro into the store's dependency
(`ledger-import` depends on the core alone and must never depend on the closure. The reader now lives in the small library crate `ledger-reader`;
store) but pulls Typst, the fonts and hayro into the store's dependency `ledger-store` and `ledger-import` both depend on it (`ledger-import`
closure. Unit U0d (plan 8.2, after S4.5) moves the reader into a small re-exports it as `extract`), the store no longer builds the typesetter,
library crate, `ledger-reader`, that both depend on; until it lands the and `ledger-import` still never depends on the store.
arrow stands as drawn above.
`ledger-core` itself (`crates/ledger-core/src/`): `money`, `account`, `ledger-core` itself (`crates/ledger-core/src/`): `money`, `account`,
`journal`, `ledger` (the command surface and replay), `kernel` (every `journal`, `ledger` (the command surface and replay), `kernel` (every
@@ -117,13 +117,13 @@ the agent's cannot drift apart. The crate depends inward (`ledger-core`,
`ledger-store`, `ledger-config`, `ledger-import`, `ledger-vault`), never on `ledger-store`, `ledger-config`, `ledger-import`, `ledger-vault`), never on
`ledger-mcp` or `ledger-gui`. `ledger-mcp` or `ledger-gui`.
## The evidence pipeline (`ledger-import`, `ledger-core`, `ledger-store`, P6) ## The evidence pipeline (`ledger-reader`, `ledger-core`, `ledger-store`, P6)
One extractor lives in `ledger-import` (`src/extract.rs`, D-53: in-process, One extractor lives in `ledger-reader` (`src/lib.rs`, D-53, D-61: in-process,
no new crate): a PDF's text and pages, an XLSX's cells as exact decimal a plain library): a PDF's text and pages, an XLSX's cells as exact decimal
strings, and plain text; the caps are 25 MiB in, 30 pages and 2 MiB of strings, and plain text; the caps are 25 MiB in, 30 pages and 2 MiB of
text, and a reader panic is caught there. `ledger-store` depends on text, and a reader panic is caught there. `ledger-store` depends on
`ledger-import` so the writer can read a file as it is filed and journal `ledger-reader` so the writer can read a file as it is filed and journal
`attachment.text_extracted` (content-addressed text blob, extractor, `attachment.text_extracted` (content-addressed text blob, extractor,
version, pages, `ok`/`none`/`failed`), one record per attachment, extractor version, pages, `ok`/`none`/`failed`), one record per attachment, extractor
and version; `Store::verify` checks the text blob's hash and never and version; `Store::verify` checks the text blob's hash and never
+4 -4
View File
@@ -3273,8 +3273,8 @@ check, and the preview's clone; attention's year comparison and its
## P6: the evidence pipeline (2026-10-27) ## P6: the evidence pipeline (2026-10-27)
Extraction, dedup, annotations and company record categories (plan P6; Extraction, dedup, annotations and company record categories (plan P6;
rules.md B-44). One in-process extractor in `ledger-import` rules.md B-44). One in-process extractor in `ledger-reader`
(`src/extract.rs`): `pdf_text` and `pdf_lines` (promoted from the PDF (`crates/ledger-reader/src/lib.rs`): `pdf_text` and `pdf_lines` (promoted from the PDF
reader test), `page_count`, `page_png` (promoted from the viewer's private reader test), `page_count`, `page_png` (promoted from the viewer's private
rasterizer), `xlsx_text` (cells as the exact decimal strings the sheet XML rasterizer), `xlsx_text` (cells as the exact decimal strings the sheet XML
holds), `text_of` (moved from `tin_files`, now reading a PDF's text too) holds), `text_of` (moved from `tin_files`, now reading a PDF's text too)
@@ -3284,13 +3284,13 @@ reaching the server (D-32: no sandbox, and the plan says so). The viewer
and the server's filing scan both read through it: `ledger-gui/src/receipts.rs` and the server's filing scan both read through it: `ledger-gui/src/receipts.rs`
keeps no `hayro` use of its own. keeps no `hayro` use of its own.
- **ledger-import:** `tests/extract.rs` `extraction_deterministic` (same - **ledger-reader (tests stay in `crates/ledger-import/tests/extract.rs`, which builds its PDFs with `ledger-import`):** `extraction_deterministic` (same
bytes, same text and PNG bytes; plain text and an image's `none`), bytes, same text and PNG bytes; plain text and an image's `none`),
`xlsx_cells_are_decimal_strings` (proptest: the cell text is the decimal `xlsx_cells_are_decimal_strings` (proptest: the cell text is the decimal
string written, never a float), `a_pdf_that_crashes_the_reader_is_failed_not_fatal` string written, never a float), `a_pdf_that_crashes_the_reader_is_failed_not_fatal`
(garbage and truncated PDFs are `Err`, the too-large cap is refused (garbage and truncated PDFs are `Err`, the too-large cap is refused
before the reader, page 31 and a zero-size page are refused); unit tests before the reader, page 31 and a zero-size page are refused); unit tests
in `src/extract.rs` for the panic catch and the input cap. `tests/spec_import.rs` in `crates/ledger-reader/src/lib.rs` for the panic catch and the input cap. `tests/spec_import.rs`
keeps its script/RTL PDF test; its `pdf_lines` helper is now the keeps its script/RTL PDF test; its `pdf_lines` helper is now the
extractor's. extractor's.
- **ledger-core:** `src/evidence.rs` (`Annotation`, `AnnotationFields`; - **ledger-core:** `src/evidence.rs` (`Annotation`, `AnnotationFields`;
+270 -19
View File
@@ -29,6 +29,21 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
[[package]]
name = "alloc-stdlib"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195"
dependencies = [
"alloc-no-stdlib",
]
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.6" version = "0.1.6"
@@ -223,6 +238,27 @@ dependencies = [
"syn 3.0.9", "syn 3.0.9",
] ]
[[package]]
name = "brotli"
version = "8.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]]
name = "brotli-decompressor"
version = "5.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.20.3" version = "3.20.3"
@@ -753,6 +789,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97b65636e5b9ef369943878ac74335ba1c55c1cb6adbf1e2c293c624248d693" checksum = "b97b65636e5b9ef369943878ac74335ba1c55c1cb6adbf1e2c293c624248d693"
[[package]]
name = "fearless_simd"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4beca3cb2444e3304ac30843cc091f44ed58932353cd492ce740067bfce6b12"
[[package]] [[package]]
name = "fearless_simd" name = "fearless_simd"
version = "1.1.0" version = "1.1.0"
@@ -806,6 +848,15 @@ dependencies = [
"bytemuck", "bytemuck",
] ]
[[package]]
name = "font-types"
version = "0.12.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "835ae9d781464a79026cde387f9cbc650c9a4909d7cf7e658b127e3c35d2f35d"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "fontconfig-parser" name = "fontconfig-parser"
version = "0.5.8" version = "0.5.8"
@@ -902,8 +953,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"r-efi 6.0.0", "r-efi 6.0.0",
"wasm-bindgen",
] ]
[[package]] [[package]]
@@ -934,11 +987,28 @@ dependencies = [
"log", "log",
"peniko", "peniko",
"png", "png",
"skrifa", "skrifa 0.42.1",
"smallvec", "smallvec",
"vello_common 0.0.9", "vello_common 0.0.9",
] ]
[[package]]
name = "glifo"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1218dedd5348c1b166ec3b8b3cbca49d2f527bd43b3fa072ea5123c523ed3f"
dependencies = [
"bytemuck",
"foldhash",
"hashbrown 0.17.1",
"log",
"peniko",
"png",
"skrifa 0.44.0",
"smallvec",
"vello_common 0.3.0",
]
[[package]] [[package]]
name = "guillotiere" name = "guillotiere"
version = "0.7.0" version = "0.7.0"
@@ -1016,11 +1086,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4caa128ab87fd48ffb7490617cf93f77f606820dffbc9fd9ef6ab0ed077f56d" checksum = "c4caa128ab87fd48ffb7490617cf93f77f606820dffbc9fd9ef6ab0ed077f56d"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"hayro-interpret", "hayro-interpret 0.7.0",
"image", "image",
"kurbo", "kurbo",
"pic-scale", "pic-scale",
"vello_cpu", "vello_cpu 0.0.8",
]
[[package]]
name = "hayro"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4675ff5acd7ca41c0c34ccd2a3a154f5c86d8b5ec92bef0a114bfb194969e2af"
dependencies = [
"bytemuck",
"fearless_simd 1.1.0",
"hayro-interpret 0.8.0",
"image",
"kurbo",
"pic-scale",
"rustc-hash",
"vello_cpu 0.3.0",
] ]
[[package]] [[package]]
@@ -1041,6 +1127,7 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d285dc30731c8485de5fa732fbdf2b3affdf01e4da7c2135022ca6fa664bf6" checksum = "24d285dc30731c8485de5fa732fbdf2b3affdf01e4da7c2135022ca6fa664bf6"
dependencies = [ dependencies = [
"brotli",
"hayro-postscript", "hayro-postscript",
] ]
@@ -1052,13 +1139,32 @@ checksum = "f2613d0406898995042d0794c4245b2f2fba1246490c8ac593769bba6551129d"
dependencies = [ dependencies = [
"bitflags 2.13.2", "bitflags 2.13.2",
"hayro-cmap", "hayro-cmap",
"hayro-syntax", "hayro-syntax 0.7.2",
"kurbo", "kurbo",
"moxcms", "moxcms",
"phf", "phf",
"rustc-hash", "rustc-hash",
"siphasher", "siphasher",
"skrifa", "skrifa 0.42.1",
"smallvec",
"yoke",
]
[[package]]
name = "hayro-interpret"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac9df074ff163a63864cec9b0bf640ccbff8abf4e44481df7369ce0ac172ffbe"
dependencies = [
"bitflags 2.13.2",
"hayro-cmap",
"hayro-syntax 0.8.0",
"kurbo",
"moxcms",
"phf",
"rustc-hash",
"siphasher",
"skrifa 0.46.2",
"smallvec", "smallvec",
"yoke", "yoke",
] ]
@@ -1082,6 +1188,15 @@ dependencies = [
"fearless_simd 0.4.1", "fearless_simd 0.4.1",
] ]
[[package]]
name = "hayro-jpeg2000"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "122c5f834c37a2260b9c851bb8be685cf539b2a8b94b704a931ef7d2525d5835"
dependencies = [
"fearless_simd 1.1.0",
]
[[package]] [[package]]
name = "hayro-postscript" name = "hayro-postscript"
version = "0.1.0" version = "0.1.0"
@@ -1095,7 +1210,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7434c89e920d84e077214a29e69b462a6518754610f732f1124af3948410cb8c" checksum = "7434c89e920d84e077214a29e69b462a6518754610f732f1124af3948410cb8c"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"hayro-interpret", "hayro-interpret 0.7.0",
"image", "image",
"kurbo", "kurbo",
"siphasher", "siphasher",
@@ -1111,7 +1226,23 @@ dependencies = [
"flate2", "flate2",
"hayro-ccitt 0.3.0", "hayro-ccitt 0.3.0",
"hayro-jbig2", "hayro-jbig2",
"hayro-jpeg2000", "hayro-jpeg2000 0.3.5",
"memchr",
"rustc-hash",
"smallvec",
"zune-jpeg",
]
[[package]]
name = "hayro-syntax"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cdc818d8d265dcb42081d488f7599df4f73fb79e9d347ecedcd3c23809d2026"
dependencies = [
"flate2",
"hayro-ccitt 0.4.0",
"hayro-jbig2",
"hayro-jpeg2000 0.4.1",
"memchr", "memchr",
"rustc-hash", "rustc-hash",
"smallvec", "smallvec",
@@ -1125,7 +1256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0b3db92c4917aad24ff30d61413a9a7509ae88b27aee1ae34eff881e1d2723a" checksum = "a0b3db92c4917aad24ff30d61413a9a7509ae88b27aee1ae34eff881e1d2723a"
dependencies = [ dependencies = [
"flate2", "flate2",
"hayro-syntax", "hayro-syntax 0.7.2",
"pdf-writer", "pdf-writer",
] ]
@@ -1553,7 +1684,7 @@ dependencies = [
"rustc-hash", "rustc-hash",
"rustybuzz", "rustybuzz",
"siphasher", "siphasher",
"skrifa", "skrifa 0.42.1",
"smallvec", "smallvec",
"subsetter", "subsetter",
"tiny-skia-path", "tiny-skia-path",
@@ -1572,7 +1703,7 @@ dependencies = [
"fontdb", "fontdb",
"krilla", "krilla",
"resvg", "resvg",
"skrifa", "skrifa 0.42.1",
"smallvec", "smallvec",
"tiny-skia", "tiny-skia",
"usvg", "usvg",
@@ -1638,6 +1769,7 @@ dependencies = [
"csv", "csv",
"deunicode", "deunicode",
"ledger-core", "ledger-core",
"ledger-reader",
"qrcode", "qrcode",
"rust_decimal", "rust_decimal",
"rust_xlsxwriter", "rust_xlsxwriter",
@@ -1650,6 +1782,18 @@ dependencies = [
"typst-pdf", "typst-pdf",
] ]
[[package]]
name = "ledger-reader"
version = "0.1.0"
dependencies = [
"hayro 0.8.0",
"hayro-interpret 0.8.0",
"kurbo",
"quick-xml 0.38.4",
"thiserror",
"zip 9.1.3",
]
[[package]] [[package]]
name = "ledger-store" name = "ledger-store"
version = "0.1.0" version = "0.1.0"
@@ -1659,6 +1803,7 @@ dependencies = [
"getrandom 0.2.17", "getrandom 0.2.17",
"hex", "hex",
"ledger-core", "ledger-core",
"ledger-reader",
"libc", "libc",
"rusqlite", "rusqlite",
"rust_decimal", "rust_decimal",
@@ -1808,6 +1953,18 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af"
[[package]]
name = "nix"
version = "0.31.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d"
dependencies = [
"bitflags 2.13.2",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.4.8" version = "0.4.8"
@@ -2312,7 +2469,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4ed38b89c2c77ff968c524145ad65fb010f38af5c7a224b53b81d47ac2daa81" checksum = "c4ed38b89c2c77ff968c524145ad65fb010f38af5c7a224b53b81d47ac2daa81"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"font-types", "font-types 0.11.3",
]
[[package]]
name = "read-fonts"
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709"
dependencies = [
"bytemuck",
"font-types 0.12.7",
"once_cell",
]
[[package]]
name = "read-fonts"
version = "0.43.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "005c8acf251756c478b0bf402885bfd88a1476020c4c7e6060edc1aa68da38ea"
dependencies = [
"bytemuck",
"font-types 0.12.7",
"once_cell",
] ]
[[package]] [[package]]
@@ -2447,7 +2626,7 @@ version = "0.90.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2be778223b36bb449b2ef2df4856ced2d311680818a7310db5c5dc370170f935" checksum = "2be778223b36bb449b2ef2df4856ced2d311680818a7310db5c5dc370170f935"
dependencies = [ dependencies = [
"zip", "zip 4.6.1",
] ]
[[package]] [[package]]
@@ -2637,7 +2816,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c34617370ae968efb7161bb2beb517d9084659aae19e24b89e3db25b46e4564" checksum = "0c34617370ae968efb7161bb2beb517d9084659aae19e24b89e3db25b46e4564"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"read-fonts", "read-fonts 0.39.2",
]
[[package]]
name = "skrifa"
version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68"
dependencies = [
"bytemuck",
"read-fonts 0.41.0",
]
[[package]]
name = "skrifa"
version = "0.46.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4febebda507fb889c545a37a135517769d1391941013561292897961d1887d6"
dependencies = [
"bytemuck",
"read-fonts 0.43.3",
] ]
[[package]] [[package]]
@@ -2724,7 +2923,7 @@ checksum = "38803281d1c23166c5ebcb455439a5d2afe711cc909cf88af72448c297756ad6"
dependencies = [ dependencies = [
"kurbo", "kurbo",
"rustc-hash", "rustc-hash",
"skrifa", "skrifa 0.42.1",
"write-fonts", "write-fonts",
] ]
@@ -3015,6 +3214,12 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
[[package]]
name = "typed-path"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce898823d8cd0f028662d2f349694b1af0a5cc9be83f1d213a00d3147445e1e"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.20.1" version = "1.20.1"
@@ -3164,7 +3369,7 @@ dependencies = [
"fontdb", "fontdb",
"glidesort", "glidesort",
"hayagriva", "hayagriva",
"hayro-syntax", "hayro-syntax 0.7.2",
"icu_properties", "icu_properties",
"image", "image",
"indexmap", "indexmap",
@@ -3281,7 +3486,7 @@ dependencies = [
"comemo", "comemo",
"ecow", "ecow",
"flate2", "flate2",
"hayro", "hayro 0.7.1",
"hayro-svg", "hayro-svg",
"image", "image",
"indexmap", "indexmap",
@@ -3558,6 +3763,22 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "vello_common"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73c2fbe53548bcd25c8ff42f75a93770a8611a16b37bee56d7ef7b44e36027f1"
dependencies = [
"bytemuck",
"fearless_simd 0.7.0",
"guillotiere",
"log",
"peniko",
"png",
"smallvec",
"thiserror",
]
[[package]] [[package]]
name = "vello_cpu" name = "vello_cpu"
version = "0.0.8" version = "0.0.8"
@@ -3565,12 +3786,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d8ded630e8316bb94a55881256506d1f3b9947b5f66db8a7d32ca7ba02decd0" checksum = "6d8ded630e8316bb94a55881256506d1f3b9947b5f66db8a7d32ca7ba02decd0"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"glifo", "glifo 0.1.1",
"hashbrown 0.17.1", "hashbrown 0.17.1",
"png", "png",
"vello_common 0.0.8", "vello_common 0.0.8",
] ]
[[package]]
name = "vello_cpu"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed1fec424fbba6acf6cbc78bb526447eb77590d95f40419d598870fe37535212"
dependencies = [
"bytemuck",
"glifo 0.4.0",
"hashbrown 0.17.1",
"png",
"vello_common 0.3.0",
]
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@@ -3807,11 +4041,11 @@ version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb731d4c4d93eacc69a1ad2f270f905788a98e4a3438267bcafbe08d3431c8d8" checksum = "cb731d4c4d93eacc69a1ad2f270f905788a98e4a3438267bcafbe08d3431c8d8"
dependencies = [ dependencies = [
"font-types", "font-types 0.11.3",
"indexmap", "indexmap",
"kurbo", "kurbo",
"log", "log",
"read-fonts", "read-fonts 0.39.2",
] ]
[[package]] [[package]]
@@ -3962,6 +4196,23 @@ dependencies = [
"zopfli", "zopfli",
] ]
[[package]]
name = "zip"
version = "9.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1156b39d142a34b999c2310a50455e0cb3760bf7efad0122f796d813bee26ccb"
dependencies = [
"crc32fast",
"flate2",
"getrandom 0.4.3",
"indexmap",
"memchr",
"nix",
"typed-path",
"windows-sys",
"zopfli",
]
[[package]] [[package]]
name = "zlib-rs" name = "zlib-rs"
version = "0.6.8" version = "0.6.8"
+1 -1
View File
@@ -1299,7 +1299,7 @@ Each unit follows the 8.0.4 template. "A-n" is an owner answer numbered in 8.3.1
5. **Files:** owns `crates/ledger-reader/`; touches `crates/ledger-import/{Cargo.toml,src/lib.rs}`, `crates/ledger-store/Cargo.toml`, `crates/ledger-store/src/store.rs` (imports only), `Cargo.toml` (workspace members), the two fuzz targets' manifest, `docs/architecture.md`, `docs/testing.md`. 5. **Files:** owns `crates/ledger-reader/`; touches `crates/ledger-import/{Cargo.toml,src/lib.rs}`, `crates/ledger-store/Cargo.toml`, `crates/ledger-store/src/store.rs` (imports only), `Cargo.toml` (workspace members), the two fuzz targets' manifest, `docs/architecture.md`, `docs/testing.md`.
6. **Tests and proofs:** no new test: the move is behavior-preserving, and P6's `extraction_deterministic`, `xlsx_cells_are_decimal_strings`, `a_pdf_that_crashes_the_reader_is_failed_not_fatal` move with the module and pass unchanged; `fuzz_extract_pdf` and `fuzz_extract_xlsx` build. No kernel, Kani or Lean change. 6. **Tests and proofs:** no new test: the move is behavior-preserving, and P6's `extraction_deterministic`, `xlsx_cells_are_decimal_strings`, `a_pdf_that_crashes_the_reader_is_failed_not_fatal` move with the module and pass unchanged; `fuzz_extract_pdf` and `fuzz_extract_xlsx` build. No kernel, Kani or Lean change.
7. **Done when:** (1) `cargo tree -p ledger-store -e normal` shows no `typst*` and no `ledger-import`; (2) `cargo tree -p ledger-import -e normal` shows no `ledger-store` (as today); (3) the three P6 extraction tests and the two fuzz targets pass where they now live; (4) `docs/architecture.md` has no "under review" sentence; (5) the batch's gate (D-60) green. 7. **Done when:** (1) `cargo tree -p ledger-store -e normal` shows no `typst*` and no `ledger-import`; (2) `cargo tree -p ledger-import -e normal` shows no `ledger-store` (as today); (3) the three P6 extraction tests and the two fuzz targets pass where they now live; (4) `docs/architecture.md` has no "under review" sentence; (5) the batch's gate (D-60) green.
8. **As built:** none yet. 8. **As built:** `extract.rs` moved whole (`git mv`) to `crates/ledger-reader/src/lib.rs`; the new crate depends on `thiserror`, `hayro`, `hayro-interpret`, `kurbo`, `zip`, `quick-xml` only. `ledger-import` drops those (keeps `zip` as a dev-dependency for its test) and re-exports the crate with `pub use ledger_reader as extract;`, so `ledger_import::extract::*` still works for `ledger-gui`, `ledger-mcp`, `ledger-host` and the two fuzz targets (no caller changed). `ledger-store` depends on `ledger-reader` instead of `ledger-import`; `store.rs` and `tests/attachments.rs` use `ledger_reader::`. The three P6 tests stay in `crates/ledger-import/tests/extract.rs` (they build their PDFs with `ledger-import`'s statement writer; moving them would need a dev-dependency cycle) and pass through the re-export; the two unit tests moved with the module. `docs/architecture.md`, `docs/testing.md`, `AGENTS.md` updated.
#### P4b S4: `documents_v2` #### P4b S4: `documents_v2`