Skip to main content

nomograph_workflow/
helpers.rs

1//! Cross-tool helpers.
2//!
3//! Previously duplicated in synthesist/store.rs and lattice/store.rs;
4//! consolidated here so a fix (or the addition of a new helper) lands
5//! in one place.
6
7use anyhow::{Context, Result};
8use serde_json::Value;
9
10/// Split a `tree/spec` identifier into its two parts. Prescriptive
11/// error on malformed input so CLI users see the expected shape.
12pub fn parse_tree_spec(input: &str) -> Result<(String, String)> {
13    let (tree, spec) = input
14        .split_once('/')
15        .context("identifier must be <tree>/<spec>, e.g. keaton/graphs")?;
16    if tree.is_empty() || spec.is_empty() {
17        anyhow::bail!("identifier must be <tree>/<spec>, e.g. keaton/graphs");
18    }
19    Ok((tree.to_string(), spec.to_string()))
20}
21
22/// Today's date as `YYYY-MM-DD` in local time. Used by commands that
23/// default a `date` / `event_date` prop to "today".
24pub fn today() -> String {
25    use time::macros::format_description;
26    let fmt = format_description!("[year]-[month]-[day]");
27    time::OffsetDateTime::now_local()
28        .unwrap_or_else(|_| time::OffsetDateTime::now_utc())
29        .format(&fmt)
30        .unwrap_or_else(|_| "1970-01-01".into())
31}
32
33/// Render a JSON value as a single line on stdout. Every synthesist
34/// and lattice command emits JSON by convention; routing through this
35/// helper keeps the output shape consistent.
36pub fn json_out(v: &Value) -> Result<()> {
37    println!("{}", serde_json::to_string(v).context("serialize output")?);
38    Ok(())
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn parse_tree_spec_ok() {
47        let (t, s) = parse_tree_spec("keaton/graphs").unwrap();
48        assert_eq!(t, "keaton");
49        assert_eq!(s, "graphs");
50    }
51
52    #[test]
53    fn parse_tree_spec_errors_on_missing_slash() {
54        assert!(parse_tree_spec("keaton").is_err());
55    }
56
57    #[test]
58    fn parse_tree_spec_errors_on_empty_halves() {
59        assert!(parse_tree_spec("/graphs").is_err());
60        assert!(parse_tree_spec("keaton/").is_err());
61    }
62
63    #[test]
64    fn today_shape_is_yyyy_mm_dd() {
65        let s = today();
66        assert_eq!(s.len(), 10);
67        assert_eq!(s.chars().nth(4), Some('-'));
68        assert_eq!(s.chars().nth(7), Some('-'));
69    }
70}