polydat_grammar/comprehension/spec/
text.rs1use super::serde_form::{ComprehensionSpec, SpecConvertError};
18use crate::comprehension::ast::Comprehension as AlgebraAst;
19
20pub fn parse_text(text: &str) -> Result<AlgebraAst, TextParseError> {
26 let spec = deserialize_spec(text)?;
27 let algebra = spec.into_algebra().map_err(TextParseError::Convert)?;
28 Ok(algebra)
29}
30
31fn deserialize_spec(text: &str) -> Result<ComprehensionSpec, TextParseError> {
32 let trimmed = text.trim_start();
33 if trimmed.starts_with('{') {
34 serde_json::from_str(text).map_err(|e| TextParseError::Json(e.to_string()))
35 } else {
36 serde_yaml::from_str(text).map_err(|e| TextParseError::Yaml(e.to_string()))
37 }
38}
39
40#[derive(Debug, Clone)]
42pub enum TextParseError {
43 Yaml(String),
45 Json(String),
47 Convert(SpecConvertError),
50}
51
52impl std::fmt::Display for TextParseError {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 match self {
55 TextParseError::Yaml(msg) => write!(f, "YAML parse error: {msg}"),
56 TextParseError::Json(msg) => write!(f, "JSON parse error: {msg}"),
57 TextParseError::Convert(e) => write!(f, "spec conversion error: {e}"),
58 }
59 }
60}
61
62impl std::error::Error for TextParseError {}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use crate::comprehension::strategy::StrategyName;
68
69 #[test]
70 fn yaml_text_block() {
71 let text = r#"
72 for: "k in 1..10, limit in [10, 100]"
73 where: "{k} > 0"
74 order: "halton/20"
75 "#;
76 let algebra = parse_text(text).unwrap();
77 match algebra {
78 AlgebraAst::Order {
79 strategy: StrategyName::Halton,
80 truncation: Some(20),
81 ..
82 } => {}
83 other => panic!("expected Order(Halton, Some(20)), got {other:?}"),
84 }
85 }
86
87 #[test]
88 fn json_text_block() {
89 let text = r#"
90 {
91 "for": "k in 1..10",
92 "order": "lex/5"
93 }
94 "#;
95 let algebra = parse_text(text).unwrap();
96 match algebra {
97 AlgebraAst::Order {
98 strategy: StrategyName::Lex,
99 truncation: Some(5),
100 ..
101 } => {}
102 other => panic!("expected Order(Lex, Some(5)), got {other:?}"),
103 }
104 }
105
106 #[test]
107 fn json_union_form() {
108 let text = r#"{
109 "for": [
110 ["k in 10", "limit in [1, 2, 3]"],
111 ["k in 100", "limit in [10, 20, 30]"]
112 ]
113 }"#;
114 let algebra = parse_text(text).unwrap();
115 match algebra {
116 AlgebraAst::Union { children } => assert_eq!(children.len(), 2),
117 other => panic!("expected Union, got {other:?}"),
118 }
119 }
120
121 #[test]
122 fn malformed_yaml_surfaces_error() {
123 let text = r#"
124 for: [unterminated
125 "#;
126 let err = parse_text(text).unwrap_err();
127 assert!(matches!(err, TextParseError::Yaml(_)));
128 }
129
130 #[test]
131 fn malformed_json_surfaces_error() {
132 let text = r#"{ "for": "k in 1..10", oops }"#;
133 let err = parse_text(text).unwrap_err();
134 assert!(matches!(err, TextParseError::Json(_)));
135 }
136
137 #[test]
138 fn convert_error_surfaces() {
139 let text = r#"
140 for: "k in 1..10"
141 order: "garbage(((not valid"
142 "#;
143 let err = parse_text(text).unwrap_err();
144 assert!(matches!(err, TextParseError::Convert(_)));
145 }
146}