sea_core/parser/
ast_convert.rs

1//! Conversion functions from internal AST types to schema types.
2//!
3//! This module provides `From` implementations to convert between
4//! the internal AST representation and the serializable schema types.
5
6use crate::parser::ast::{self, Spanned};
7use crate::parser::ast_schema as schema;
8use crate::policy::Expression as CoreExpression;
9
10// =========================================================================
11// FileMetadata Conversion
12// =========================================================================
13
14impl From<&ast::FileMetadata> for schema::FileMetadata {
15    fn from(m: &ast::FileMetadata) -> Self {
16        schema::FileMetadata {
17            namespace: m.namespace.clone(),
18            version: m.version.clone(),
19            owner: m.owner.clone(),
20            profile: m.profile.clone(),
21            imports: m.imports.iter().map(|i| i.into()).collect(),
22        }
23    }
24}
25
26impl From<&ast::ImportDecl> for schema::ImportDecl {
27    fn from(i: &ast::ImportDecl) -> Self {
28        schema::ImportDecl {
29            specifier: (&i.specifier).into(),
30            from_module: i.from_module.clone(),
31        }
32    }
33}
34
35impl From<&ast::ImportSpecifier> for schema::ImportSpecifier {
36    fn from(s: &ast::ImportSpecifier) -> Self {
37        match s {
38            ast::ImportSpecifier::Named(items) => schema::ImportSpecifier::Named {
39                items: items.iter().map(|i| i.into()).collect(),
40            },
41            ast::ImportSpecifier::Wildcard(alias) => schema::ImportSpecifier::Wildcard {
42                alias: alias.clone(),
43            },
44        }
45    }
46}
47
48impl From<&ast::ImportItem> for schema::ImportItem {
49    fn from(i: &ast::ImportItem) -> Self {
50        schema::ImportItem {
51            name: i.name.clone(),
52            alias: i.alias.clone(),
53        }
54    }
55}
56
57// =========================================================================
58// Policy Metadata Conversion
59// =========================================================================
60
61impl From<&ast::PolicyMetadata> for schema::PolicyMetadata {
62    fn from(m: &ast::PolicyMetadata) -> Self {
63        schema::PolicyMetadata {
64            kind: m.kind.as_ref().map(|k| k.into()),
65            modality: m.modality.as_ref().map(|m| m.into()),
66            priority: m.priority,
67            rationale: m.rationale.clone(),
68            tags: m.tags.clone(),
69        }
70    }
71}
72
73impl From<&ast::PolicyKind> for schema::PolicyKind {
74    fn from(k: &ast::PolicyKind) -> Self {
75        match k {
76            ast::PolicyKind::Constraint => schema::PolicyKind::Constraint,
77            ast::PolicyKind::Derivation => schema::PolicyKind::Derivation,
78            ast::PolicyKind::Obligation => schema::PolicyKind::Obligation,
79        }
80    }
81}
82
83impl From<&ast::PolicyModality> for schema::PolicyModality {
84    fn from(m: &ast::PolicyModality) -> Self {
85        match m {
86            ast::PolicyModality::Obligation => schema::PolicyModality::Obligation,
87            ast::PolicyModality::Prohibition => schema::PolicyModality::Prohibition,
88            ast::PolicyModality::Permission => schema::PolicyModality::Permission,
89        }
90    }
91}
92
93// =========================================================================
94// Metric Metadata Conversion
95// =========================================================================
96
97impl From<&ast::MetricMetadata> for schema::MetricMetadata {
98    fn from(m: &ast::MetricMetadata) -> Self {
99        schema::MetricMetadata {
100            refresh_interval: m
101                .refresh_interval
102                .map(|d| format!("PT{}S", d.num_seconds())),
103            unit: m.unit.clone(),
104            threshold: m.threshold.map(|d| d.to_string()),
105            severity: m.severity.as_ref().map(|s| s.into()),
106            target: m.target.map(|d| d.to_string()),
107            window: m.window.map(|d| format!("PT{}S", d.num_seconds())),
108        }
109    }
110}
111
112impl From<&crate::primitives::Severity> for schema::Severity {
113    fn from(s: &crate::primitives::Severity) -> Self {
114        match s {
115            crate::primitives::Severity::Info => schema::Severity::Info,
116            crate::primitives::Severity::Warning => schema::Severity::Warning,
117            crate::primitives::Severity::Error => schema::Severity::Error,
118            // Handle any additional variants
119            _ => schema::Severity::Error,
120        }
121    }
122}
123
124// =========================================================================
125// Target Format Conversion
126// =========================================================================
127
128impl From<&ast::TargetFormat> for schema::TargetFormat {
129    fn from(t: &ast::TargetFormat) -> Self {
130        match t {
131            ast::TargetFormat::Calm => schema::TargetFormat::Calm,
132            ast::TargetFormat::Kg => schema::TargetFormat::Kg,
133            ast::TargetFormat::Sbvr => schema::TargetFormat::Sbvr,
134            ast::TargetFormat::Protobuf => schema::TargetFormat::Protobuf,
135        }
136    }
137}
138
139impl From<&ast::MappingRule> for schema::MappingRule {
140    fn from(r: &ast::MappingRule) -> Self {
141        schema::MappingRule {
142            primitive_type: r.primitive_type.clone(),
143            primitive_name: r.primitive_name.clone(),
144            target_type: r.target_type.clone(),
145            fields: r.fields.clone(),
146        }
147    }
148}
149
150impl From<&ast::ProjectionOverride> for schema::ProjectionOverride {
151    fn from(o: &ast::ProjectionOverride) -> Self {
152        schema::ProjectionOverride {
153            primitive_type: o.primitive_type.clone(),
154            primitive_name: o.primitive_name.clone(),
155            fields: o.fields.clone(),
156        }
157    }
158}
159
160// =========================================================================
161// Expression Conversion
162// =========================================================================
163
164impl From<&CoreExpression> for schema::Expression {
165    fn from(e: &CoreExpression) -> Self {
166        match e {
167            CoreExpression::Literal(v) => schema::Expression::Literal { value: v.clone() },
168            CoreExpression::QuantityLiteral { value, unit } => {
169                schema::Expression::QuantityLiteral {
170                    value: value.to_string(),
171                    unit: unit.clone(),
172                }
173            }
174            CoreExpression::TimeLiteral(ts) => schema::Expression::TimeLiteral {
175                timestamp: ts.clone(),
176            },
177            CoreExpression::IntervalLiteral { start, end } => schema::Expression::IntervalLiteral {
178                start: start.clone(),
179                end: end.clone(),
180            },
181            CoreExpression::Variable(name) => schema::Expression::Variable { name: name.clone() },
182            CoreExpression::GroupBy {
183                variable,
184                collection,
185                filter,
186                key,
187                condition,
188            } => schema::Expression::GroupBy {
189                variable: variable.clone(),
190                collection: Box::new(collection.as_ref().into()),
191                filter: filter.as_ref().map(|f| Box::new(f.as_ref().into())),
192                key: Box::new(key.as_ref().into()),
193                condition: Box::new(condition.as_ref().into()),
194            },
195            CoreExpression::Binary { op, left, right } => schema::Expression::Binary {
196                op: op.into(),
197                left: Box::new(left.as_ref().into()),
198                right: Box::new(right.as_ref().into()),
199            },
200            CoreExpression::Unary { op, operand } => schema::Expression::Unary {
201                op: op.into(),
202                operand: Box::new(operand.as_ref().into()),
203            },
204            CoreExpression::Cast {
205                operand,
206                target_type,
207            } => schema::Expression::Cast {
208                operand: Box::new(operand.as_ref().into()),
209                target_type: target_type.clone(),
210            },
211            CoreExpression::Quantifier {
212                quantifier,
213                variable,
214                collection,
215                condition,
216            } => schema::Expression::Quantifier {
217                quantifier: quantifier.into(),
218                variable: variable.clone(),
219                collection: Box::new(collection.as_ref().into()),
220                condition: Box::new(condition.as_ref().into()),
221            },
222            CoreExpression::MemberAccess { object, member } => schema::Expression::MemberAccess {
223                object: object.clone(),
224                member: member.clone(),
225            },
226            CoreExpression::Aggregation {
227                function,
228                collection,
229                field,
230                filter,
231            } => schema::Expression::Aggregation {
232                function: function.into(),
233                collection: Box::new(collection.as_ref().into()),
234                field: field.clone(),
235                filter: filter.as_ref().map(|f| Box::new(f.as_ref().into())),
236            },
237            CoreExpression::AggregationComprehension {
238                function,
239                variable,
240                collection,
241                window,
242                predicate,
243                projection,
244                target_unit,
245            } => schema::Expression::AggregationComprehension {
246                function: function.into(),
247                variable: variable.clone(),
248                collection: Box::new(collection.as_ref().into()),
249                window: window.as_ref().map(|w| w.into()),
250                predicate: Box::new(predicate.as_ref().into()),
251                projection: Box::new(projection.as_ref().into()),
252                target_unit: target_unit.clone(),
253            },
254        }
255    }
256}
257
258impl From<&crate::policy::BinaryOp> for schema::BinaryOp {
259    fn from(op: &crate::policy::BinaryOp) -> Self {
260        match op {
261            crate::policy::BinaryOp::And => schema::BinaryOp::And,
262            crate::policy::BinaryOp::Or => schema::BinaryOp::Or,
263            crate::policy::BinaryOp::Equal => schema::BinaryOp::Equal,
264            crate::policy::BinaryOp::NotEqual => schema::BinaryOp::NotEqual,
265            crate::policy::BinaryOp::GreaterThan => schema::BinaryOp::GreaterThan,
266            crate::policy::BinaryOp::LessThan => schema::BinaryOp::LessThan,
267            crate::policy::BinaryOp::GreaterThanOrEqual => schema::BinaryOp::GreaterThanOrEqual,
268            crate::policy::BinaryOp::LessThanOrEqual => schema::BinaryOp::LessThanOrEqual,
269            crate::policy::BinaryOp::Plus => schema::BinaryOp::Plus,
270            crate::policy::BinaryOp::Minus => schema::BinaryOp::Minus,
271            crate::policy::BinaryOp::Multiply => schema::BinaryOp::Multiply,
272            crate::policy::BinaryOp::Divide => schema::BinaryOp::Divide,
273            crate::policy::BinaryOp::Contains => schema::BinaryOp::Contains,
274            crate::policy::BinaryOp::StartsWith => schema::BinaryOp::StartsWith,
275            crate::policy::BinaryOp::EndsWith => schema::BinaryOp::EndsWith,
276            crate::policy::BinaryOp::Matches => schema::BinaryOp::Matches,
277            crate::policy::BinaryOp::HasRole => schema::BinaryOp::HasRole,
278            crate::policy::BinaryOp::Before => schema::BinaryOp::Before,
279            crate::policy::BinaryOp::After => schema::BinaryOp::After,
280            crate::policy::BinaryOp::During => schema::BinaryOp::During,
281        }
282    }
283}
284
285impl From<&crate::policy::UnaryOp> for schema::UnaryOp {
286    fn from(op: &crate::policy::UnaryOp) -> Self {
287        match op {
288            crate::policy::UnaryOp::Not => schema::UnaryOp::Not,
289            crate::policy::UnaryOp::Negate => schema::UnaryOp::Negate,
290        }
291    }
292}
293
294impl From<&crate::policy::Quantifier> for schema::Quantifier {
295    fn from(q: &crate::policy::Quantifier) -> Self {
296        match q {
297            crate::policy::Quantifier::ForAll => schema::Quantifier::ForAll,
298            crate::policy::Quantifier::Exists => schema::Quantifier::Exists,
299            crate::policy::Quantifier::ExistsUnique => schema::Quantifier::ExistsUnique,
300        }
301    }
302}
303
304impl From<&crate::policy::AggregateFunction> for schema::AggregateFunction {
305    fn from(f: &crate::policy::AggregateFunction) -> Self {
306        match f {
307            crate::policy::AggregateFunction::Count => schema::AggregateFunction::Count,
308            crate::policy::AggregateFunction::Sum => schema::AggregateFunction::Sum,
309            crate::policy::AggregateFunction::Min => schema::AggregateFunction::Min,
310            crate::policy::AggregateFunction::Max => schema::AggregateFunction::Max,
311            crate::policy::AggregateFunction::Avg => schema::AggregateFunction::Avg,
312        }
313    }
314}
315
316impl From<&crate::policy::WindowSpec> for schema::WindowSpec {
317    fn from(w: &crate::policy::WindowSpec) -> Self {
318        schema::WindowSpec {
319            duration: w.duration,
320            unit: "seconds".to_string(),
321        }
322    }
323}
324
325// =========================================================================
326// AST Node Conversion
327// =========================================================================
328
329impl From<&Spanned<ast::AstNode>> for schema::SpannedAstNode {
330    fn from(s: &Spanned<ast::AstNode>) -> Self {
331        schema::SpannedAstNode {
332            node: (&s.node).into(),
333            line: s.line,
334            column: s.column,
335        }
336    }
337}
338
339impl From<&ast::AstNode> for schema::AstNode {
340    fn from(n: &ast::AstNode) -> Self {
341        match n {
342            ast::AstNode::Export(inner) => schema::AstNode::Export {
343                declaration: Box::new(inner.as_ref().into()),
344            },
345            ast::AstNode::Entity {
346                name,
347                version,
348                annotations,
349                domain,
350            } => schema::AstNode::Entity {
351                name: name.clone(),
352                version: version.clone(),
353                annotations: annotations.clone(),
354                domain: domain.clone(),
355            },
356            ast::AstNode::Resource {
357                name,
358                annotations,
359                unit_name,
360                domain,
361            } => schema::AstNode::Resource {
362                name: name.clone(),
363                annotations: annotations.clone(),
364                unit_name: unit_name.clone(),
365                domain: domain.clone(),
366            },
367            ast::AstNode::Flow {
368                resource_name,
369                annotations,
370                from_entity,
371                to_entity,
372                quantity,
373            } => schema::AstNode::Flow {
374                resource_name: resource_name.clone(),
375                annotations: annotations.clone(),
376                from_entity: from_entity.clone(),
377                to_entity: to_entity.clone(),
378                quantity: *quantity,
379            },
380            ast::AstNode::Pattern { name, regex } => schema::AstNode::Pattern {
381                name: name.clone(),
382                regex: regex.clone(),
383            },
384            ast::AstNode::Role { name, domain } => schema::AstNode::Role {
385                name: name.clone(),
386                domain: domain.clone(),
387            },
388            ast::AstNode::Relation {
389                name,
390                subject_role,
391                predicate,
392                object_role,
393                via_flow,
394            } => schema::AstNode::Relation {
395                name: name.clone(),
396                subject_role: subject_role.clone(),
397                predicate: predicate.clone(),
398                object_role: object_role.clone(),
399                via_flow: via_flow.clone(),
400            },
401            ast::AstNode::Dimension { name } => schema::AstNode::Dimension { name: name.clone() },
402            ast::AstNode::UnitDeclaration {
403                symbol,
404                dimension,
405                factor,
406                base_unit,
407            } => schema::AstNode::UnitDeclaration {
408                symbol: symbol.clone(),
409                dimension: dimension.clone(),
410                factor: factor.to_string(),
411                base_unit: base_unit.clone(),
412            },
413            ast::AstNode::Policy {
414                name,
415                version,
416                metadata,
417                expression,
418            } => schema::AstNode::Policy {
419                name: name.clone(),
420                version: version.clone(),
421                metadata: metadata.into(),
422                expression: expression.into(),
423            },
424            ast::AstNode::Instance {
425                name,
426                entity_type,
427                fields,
428            } => schema::AstNode::Instance {
429                name: name.clone(),
430                entity_type: entity_type.clone(),
431                fields: fields.iter().map(|(k, v)| (k.clone(), v.into())).collect(),
432            },
433            ast::AstNode::ConceptChange {
434                name,
435                from_version,
436                to_version,
437                migration_policy,
438                breaking_change,
439            } => schema::AstNode::ConceptChange {
440                name: name.clone(),
441                from_version: from_version.clone(),
442                to_version: to_version.clone(),
443                migration_policy: migration_policy.clone(),
444                breaking_change: *breaking_change,
445            },
446            ast::AstNode::Metric {
447                name,
448                expression,
449                metadata,
450            } => schema::AstNode::Metric {
451                name: name.clone(),
452                expression: expression.into(),
453                metadata: metadata.into(),
454            },
455            ast::AstNode::MappingDecl {
456                name,
457                target,
458                rules,
459            } => schema::AstNode::MappingDecl {
460                name: name.clone(),
461                target: target.into(),
462                rules: rules.iter().map(|r| r.into()).collect(),
463            },
464            ast::AstNode::ProjectionDecl {
465                name,
466                target,
467                overrides,
468            } => schema::AstNode::ProjectionDecl {
469                name: name.clone(),
470                target: target.into(),
471                overrides: overrides.iter().map(|o| o.into()).collect(),
472            },
473        }
474    }
475}
476
477// =========================================================================
478// Root AST Conversion
479// =========================================================================
480
481impl From<&ast::Ast> for schema::Ast {
482    fn from(a: &ast::Ast) -> Self {
483        schema::Ast {
484            metadata: (&a.metadata).into(),
485            declarations: a.declarations.iter().map(|d| d.into()).collect(),
486        }
487    }
488}
489
490impl From<ast::Ast> for schema::Ast {
491    fn from(a: ast::Ast) -> Self {
492        (&a).into()
493    }
494}