1use crate::graph::Graph;
2use pest_derive::Parser;
3use std::path::PathBuf;
4
5#[derive(Parser)]
6#[grammar = "grammar/sea.pest"]
7pub struct SeaParser;
8
9pub mod ast;
10pub mod ast_convert;
11pub mod ast_schema;
12pub mod error;
13pub mod lint;
14pub mod printer;
15pub mod profiles;
16pub mod string_utils;
17
18pub use ast::parse_expression_from_str;
19pub use ast::parse_source;
20pub use ast::Ast;
21pub use ast::AstNode;
22pub use error::{ParseError, ParseResult};
23pub use lint::*;
24pub use printer::PrettyPrinter;
25pub use profiles::{Profile, ProfileRegistry};
26pub use string_utils::unescape_string;
27
28#[derive(Debug, Clone, Default)]
33pub struct ParseOptions {
34 pub default_namespace: Option<String>,
37 pub namespace_registry: Option<crate::registry::NamespaceRegistry>,
39 pub entry_path: Option<PathBuf>,
41 pub active_profile: Option<String>,
43 pub tolerate_profile_warnings: bool,
45}
46
47pub fn parse(source: &str) -> ParseResult<Ast> {
49 ast::parse_source(source)
50}
51
52pub fn parse_to_graph(source: &str) -> ParseResult<Graph> {
54 let ast = parse(source)?;
55 ast::ast_to_graph_with_options(ast, &ParseOptions::default())
56}
57
58pub fn parse_to_graph_with_options(source: &str, options: &ParseOptions) -> ParseResult<Graph> {
81 match (&options.namespace_registry, &options.entry_path) {
82 (Some(registry), Some(path)) => {
83 let mut resolver = crate::module::resolver::ModuleResolver::new(registry)?;
84 let ast = resolver.validate_entry(path, source)?;
85 resolver.validate_dependencies(path, &ast)?;
86 ast::ast_to_graph_with_options(ast, options)
87 }
88 (Some(_), None) => Err(ParseError::Validation(
89 "Namespace registry provided without entry path".to_string(),
90 )),
91 _ => {
92 if let Some(path) = &options.entry_path {
93 log::warn!(
94 "Entry path '{}' provided without namespace registry; module resolution skipped",
95 path.display()
96 );
97 }
98 let ast = parse(source)?;
99 ast::ast_to_graph_with_options(ast, options)
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn test_entity_declaration_syntax() {
110 let source = r#"
111 Entity "Warehouse A" in logistics
112 "#;
113
114 let result = parse(source);
115 assert!(result.is_ok(), "Failed to parse: {:?}", result.err());
116 }
117
118 #[test]
119 fn test_resource_declaration_syntax() {
120 let source = r#"
121 Resource "Camera Units" units in inventory
122 "#;
123
124 let result = parse(source);
125 assert!(result.is_ok(), "Failed to parse: {:?}", result.err());
126 }
127
128 #[test]
129 fn test_flow_declaration_syntax() {
130 let source = r#"
131 Flow "Camera Units" from "Warehouse" to "Factory" quantity 100
132 "#;
133
134 let result = parse(source);
135 assert!(result.is_ok(), "Failed to parse: {:?}", result.err());
136 }
137
138 #[test]
139 fn test_policy_simple_syntax() {
140 let source = r#"
141 Policy check_quantity as: Flow.quantity > 0
142 "#;
143
144 let result = parse(source);
145 assert!(result.is_ok(), "Failed to parse: {:?}", result.err());
146 }
147
148 #[test]
149 fn test_complex_policy_syntax() {
150 let source = r#"
151 Policy flow_constraints as:
152 (Flow.quantity > 0) and (Entity.name != "")
153 "#;
154
155 let result = parse(source);
156 assert!(result.is_ok(), "Failed to parse: {:?}", result.err());
157 }
158
159 #[test]
160 fn test_nested_expressions() {
161 let source = r#"
162 Policy multi_condition as:
163 (A or B) and (C or (D and E))
164 "#;
165
166 let result = parse(source);
167 assert!(result.is_ok(), "Failed to parse: {:?}", result.err());
168 }
169
170 #[test]
171 fn test_comments_ignored() {
172 let source = r#"
173 // This is a comment
174 Entity "Test" in domain
175 // Another comment
176 "#;
177
178 let result = parse(source);
179 assert!(result.is_ok(), "Failed to parse: {:?}", result.err());
180 }
181
182 #[test]
183 fn test_multiple_declarations() {
184 let source = r#"
185 Entity "Warehouse" in logistics
186 Resource "Cameras" units
187 Flow "Cameras" from "Warehouse" to "Factory" quantity 50
188 "#;
189
190 let result = parse(source);
191 assert!(result.is_ok(), "Failed to parse: {:?}", result.err());
192 }
193}