apr_format/lib.rs
1// Tests legitimately use expect/unwrap/panic and exact-float asserts on known
2// values; mirror the workspace convention (aprender-core/src/lib.rs) of allowing
3// these — including the `.clippy.toml` disallowed-methods `.unwrap()` ban — in
4// test code only.
5#![cfg_attr(
6 test,
7 allow(
8 clippy::expect_used,
9 clippy::unwrap_used,
10 clippy::panic,
11 clippy::float_cmp,
12 clippy::disallowed_methods
13 )
14)]
15
16//! # apr-format — sovereign `.apr` model container format
17//!
18//! Minimal, dependency-light read/write for the `.apr` model container (v1
19//! `APRN` + v2 `APR\0`), extracted from `aprender-core` so that downstream
20//! consumers (realizar inference, xpile, external tooling) can read and write
21//! `.apr` files **without** pulling the full ML/GPU/tokenizer/quantization stack.
22//! See issue #2231 — "depend on the *format*, not the *framework*."
23//!
24//! ## Status: Stage 1 foundation + cut-feasibility spike
25//!
26//! This crate currently ships:
27//! - The sovereign error seam ([`error::AprFormatError`], wrapped by
28//! `aprender-core` via `impl From`).
29//! - The single deduplicated [`crc32::crc32`] and [`f16`] conversions.
30//! - A representative v1 (`APRN`) slice — [`types`] (header/metadata/flags) and
31//! [`core_io`] (save/load) — proving the error-seam compiles at a crate
32//! boundary.
33//! - The byte-only structural validator split ([`validate`]), demonstrating the
34//! Structure-vs-Physics separation.
35//!
36//! The bulk `git-mv` of the rest of `format/` (v2 container, mmap, spec, …) lands
37//! in Stage 2.
38//!
39//! ## Locked design decisions (issue #2231)
40//! 1. The GGUF/SafeTensors/ONNX **converter stays in `aprender-core`** (it needs
41//! `f32` physics + the ML stack); only the container moves.
42//! 2. **std-only** for v1 (no `no_std` yet — the std surface is kept thin).
43//! 3. **Wrapper error seam**: the leaf owns `AprFormatError`; core From-wraps it.
44//! 4. **mmap is feature-gated** (`mmap` feature, off by default).
45
46pub mod core_io;
47pub mod crc32;
48pub mod error;
49pub mod f16;
50pub mod falsifiers;
51pub mod model_card;
52pub mod types;
53pub mod v2;
54pub mod validate;
55
56// --- Convenience re-exports (the public surface aprender-core re-exports) ---
57pub use core_io::{
58 inspect, inspect_bytes, load, load_auto, load_from_bytes, load_mmap, save, MMAP_THRESHOLD,
59};
60pub use crc32::crc32;
61pub use error::{AprFormatError, Result};
62pub use f16::{f16_to_f32, f32_to_f16};
63pub use model_card::{ModelCard, TrainingDataInfo};
64pub use types::{
65 Compression, DistillMethod, DistillationInfo, DistillationParams, Flags, Header, LayerMapping,
66 LicenseInfo, LicenseTier, Metadata, ModelInfo, ModelType, SaveOptions, TeacherProvenance,
67 TrainingInfo, FORMAT_VERSION, HEADER_SIZE, MAGIC, MAX_UNCOMPRESSED_SIZE,
68};
69pub use validate::{validate_structure, StructureCheck};
70
71#[cfg(test)]
72mod sovereignty_tests {
73 /// FALSIFY-APRF-SOV-STD-ONLY: the leaf is std-only (v1) — it links and runs
74 /// against std (fs/io save+load round-trip works), and carries no `#![no_std]`.
75 /// `no_std` is an explicit deferred decision; this pins that the std surface
76 /// stays available so the deferral cannot silently regress into a half-`no_std`
77 /// state. (A genuine `no_std` build would fail to link `std::fs::File` here.)
78 #[test]
79 fn test_std_only_surface_available() {
80 use crate::types::{ModelType, SaveOptions};
81 let dir = std::env::temp_dir();
82 let path = dir.join("apr_format_std_probe.apr");
83 // Uses std::fs via the leaf's save/load — proves the std surface links.
84 crate::save(
85 &vec![1.0_f32, 2.0],
86 ModelType::LinearRegression,
87 &path,
88 SaveOptions::default(),
89 )
90 .expect("std-only save must work");
91 let back: Vec<f32> =
92 crate::load(&path, ModelType::LinearRegression).expect("std-only load must work");
93 assert_eq!(back, vec![1.0, 2.0]);
94 let _ = std::fs::remove_file(&path);
95 }
96}