Skip to main content

stratifykit_core/
quota.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::BucketKey;
6
7/// A hand-editable quota file: observed (or desired) per-bucket target counts. `quotas`' keys are
8/// caller-defined bucket-key strings, reused verbatim (not trimmed/reparsed) so there is never a
9/// second representation of "what bucket is this" that could drift from the first. `by` makes the
10/// file self-describing: a caller reconstructs its own bucketing dimensions from this field alone,
11/// never from separately-passed flags, so an apply-time mismatch between flags and file is
12/// structurally impossible.
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct QuotaSpec {
15    // Why: kept as `stratify_format_version` (not the generic `format_version`) so the on-disk
16    // JSON this crate reads/writes stays byte-for-byte compatible with shogiesa's existing
17    // `stratify --write-template`/`--quota` file format.
18    pub stratify_format_version: u32,
19    pub input: String,
20    pub by: Vec<String>,
21    pub quotas: BTreeMap<BucketKey, usize>,
22}
23
24#[cfg(test)]
25mod tests {
26    use super::*;
27
28    #[test]
29    fn round_trips_through_json() {
30        let mut quotas = BTreeMap::new();
31        quotas.insert("phase:opening:".to_string(), 100);
32        let spec = QuotaSpec {
33            stratify_format_version: 1,
34            input: "in.jsonl".to_string(),
35            by: vec!["phase".to_string()],
36            quotas,
37        };
38        let json = serde_json::to_string(&spec).unwrap();
39        let back: QuotaSpec = serde_json::from_str(&json).unwrap();
40        assert_eq!(back.quotas.get("phase:opening:"), Some(&100));
41    }
42}