metrale_bench/param_text.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-26: Text ↔ [`ParamValues`]: the conversions a run record
4//! (`history.rs`) and a `--param` flag need.
5//!
6//! A stored run describes its whole configuration as text. Text goes back
7//! through [`crate::params::ParamKind::parse`], the domain check the edit box
8//! uses, so an old value that is no longer legal is reported, not accepted.
9//!
10//! Owner: bench (parameters).
11//! Invariants:
12//! - [`ParamValues::from_overrides`] refuses an unknown key and an
13//! out-of-domain value; it never returns a partial set.
14
15use std::collections::BTreeMap;
16
17use anyhow::{Result, bail};
18
19use crate::params::{ParamSpec, ParamValue, ParamValues};
20
21impl ParamValues {
22 /// 2026-09-26: Every `(key, value)` pair, in key order (the map is a
23 /// `BTreeMap`), so records built from the same values diff cleanly.
24 pub fn iter(&self) -> impl Iterator<Item = (&str, &ParamValue)> {
25 self.0.iter().map(|(k, v)| (k.as_str(), v))
26 }
27
28 /// 2026-09-26: The whole configuration as text, for persistence.
29 pub fn to_strings(&self) -> BTreeMap<String, String> {
30 self.iter()
31 .map(|(k, v)| (k.to_string(), v.to_edit_string()))
32 .collect()
33 }
34
35 /// 2026-09-26: Schema defaults with `overrides` applied on top.
36 ///
37 /// Every value is routed through [`ParamKind::parse`], so an out-of-domain
38 /// override fails here rather than mid-run. An unknown key is an error
39 /// naming the valid ones: an ignored `--param` typo would measure
40 /// something other than what was asked for.
41 ///
42 /// [`ParamKind::parse`]: crate::params::ParamKind::parse
43 pub fn from_overrides<'a>(
44 specs: &[ParamSpec],
45 overrides: impl IntoIterator<Item = (&'a str, &'a str)>,
46 ) -> Result<Self> {
47 let mut values = Self::defaults(specs);
48 for (key, raw) in overrides {
49 let Some(spec) = specs.iter().find(|s| s.key == key) else {
50 let known: Vec<&str> = specs.iter().map(|s| s.key).collect();
51 bail!(
52 "unknown parameter {key:?} — this benchmark takes: {}",
53 known.join(", ")
54 );
55 };
56 let value = spec
57 .kind
58 .parse(raw)
59 .map_err(|e| anyhow::anyhow!("{}: {e}", spec.label))?;
60 values.set(key, value);
61 }
62 Ok(values)
63 }
64}
65
66#[cfg(test)]
67#[path = "param_text_tests.rs"]
68mod tests;