Skip to main content

determa_state/
loader.rs

1//! Loading Determa State YAML: multi-document machine files (§9: first doc is the root)
2//! and contract files (§7).
3
4use crate::model::RawMachine;
5use crate::validate::Contract;
6use serde::Deserialize;
7
8/// A validation error with a structural path.
9#[derive(Debug, Clone, Default)]
10pub struct LoadError {
11    pub path: String,
12    pub message: String,
13}
14
15impl std::fmt::Display for LoadError {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        if self.path.is_empty() {
18            write!(f, "{}", self.message)
19        } else {
20            write!(f, "{}: {}", self.path, self.message)
21        }
22    }
23}
24
25/// Parse one or more `---`-separated machine definitions from YAML text.
26pub fn load_machines(src: &str) -> Result<Vec<RawMachine>, Vec<LoadError>> {
27    let de = serde_yaml::Deserializer::from_str(src);
28    let mut out = Vec::new();
29    let mut errs = Vec::new();
30    let mut doc_index = 0;
31    for doc in de {
32        match RawMachine::deserialize(doc) {
33            Ok(m) => out.push(m),
34            Err(e) => {
35                let pos = e.location().map(|l| l.line()).unwrap_or(0);
36                let path = if out.is_empty() {
37                    format!("doc[{}]", doc_index)
38                } else {
39                    format!("doc[{}]", doc_index)
40                };
41                errs.push(LoadError {
42                    path: format!("{path}:line {pos}"),
43                    message: format!("{e}"),
44                });
45            }
46        }
47        doc_index += 1;
48    }
49    if !errs.is_empty() {
50        return Err(errs);
51    }
52    if out.is_empty() {
53        return Err(vec![LoadError {
54            path: "machine".to_string(),
55            message: "no machine definitions found".to_string(),
56        }]);
57    }
58    Ok(out)
59}
60
61/// Parse a single contract file.
62pub fn load_contract(src: &str) -> Result<Contract, LoadError> {
63    let de = serde_yaml::Deserializer::from_str(src);
64    Contract::deserialize(de).map_err(|e| LoadError {
65        path: "contract".to_string(),
66        message: format!("{e}"),
67    })
68}
69
70pub fn load_contracts(srcs: &[(&str, &str)]) -> Result<Vec<Contract>, LoadError> {
71    let mut out = Vec::new();
72    for (_name, src) in srcs {
73        out.push(load_contract(src)?);
74    }
75    Ok(out)
76}