1use super::tokenizer::Span;
12
13#[derive(Debug, Clone, PartialEq)]
17pub struct ContractNode {
18 pub identity: IdentityNode,
19 pub purpose_statement: PurposeStatementNode,
20 pub data_semantics: DataSemanticsNode,
21 pub behavioral_semantics: BehavioralSemanticsNode,
22 pub execution_constraints: ExecutionConstraintsNode,
23 pub human_machine_contract: HumanMachineContractNode,
24 pub extensions: Option<ExtensionsNode>,
25 pub span: Span,
26}
27
28#[derive(Debug, Clone, PartialEq)]
31pub struct IdentityNode {
32 pub stable_id: SpannedValue<String>,
33 pub version: SpannedValue<i64>,
34 pub created_timestamp: SpannedValue<String>,
35 pub owner: SpannedValue<String>,
36 pub semantic_hash: SpannedValue<String>,
37 pub span: Span,
38}
39
40#[derive(Debug, Clone, PartialEq)]
43pub struct PurposeStatementNode {
44 pub narrative: SpannedValue<String>,
45 pub intent_source: SpannedValue<String>,
46 pub confidence_level: SpannedValue<f64>,
47 pub span: Span,
48}
49
50#[derive(Debug, Clone, PartialEq)]
53pub struct DataSemanticsNode {
54 pub state: Vec<StateFieldNode>,
55 pub invariants: Vec<SpannedValue<String>>,
56 pub span: Span,
57}
58
59#[derive(Debug, Clone, PartialEq)]
61pub struct StateFieldNode {
62 pub name: SpannedValue<String>,
63 pub type_expr: TypeExpression,
64 pub default_value: Option<LiteralValue>,
65 pub span: Span,
66}
67
68#[derive(Debug, Clone, PartialEq)]
72pub enum TypeExpression {
73 Primitive(PrimitiveType, Span),
75 Array(Box<TypeExpression>, Span),
77 Map(Box<TypeExpression>, Box<TypeExpression>, Span),
79 Object(Vec<StateFieldNode>, Span),
81 Enum(Vec<SpannedValue<String>>, Span),
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum PrimitiveType {
88 Integer,
89 Float,
90 String,
91 Boolean,
92 Iso8601,
93 Uuid,
94}
95
96#[derive(Debug, Clone, PartialEq)]
98pub enum LiteralValue {
99 String(String, Span),
100 Integer(i64, Span),
101 Float(f64, Span),
102 Boolean(bool, Span),
103 Array(Vec<LiteralValue>, Span),
104}
105
106#[derive(Debug, Clone, PartialEq)]
109pub struct BehavioralSemanticsNode {
110 pub operations: Vec<OperationNode>,
111 pub span: Span,
112}
113
114#[derive(Debug, Clone, PartialEq)]
115pub struct OperationNode {
116 pub name: SpannedValue<String>,
117 pub precondition: SpannedValue<String>,
118 pub parameters: Vec<StateFieldNode>,
119 pub postcondition: SpannedValue<String>,
120 pub side_effects: Vec<SpannedValue<String>>,
121 pub idempotence: SpannedValue<String>,
122 pub span: Span,
123}
124
125#[derive(Debug, Clone, PartialEq)]
128pub struct ExecutionConstraintsNode {
129 pub trigger_types: Vec<SpannedValue<String>>,
130 pub resource_limits: ResourceLimitsNode,
131 pub external_permissions: Vec<SpannedValue<String>>,
132 pub sandbox_mode: SpannedValue<String>,
133 pub span: Span,
134}
135
136#[derive(Debug, Clone, PartialEq)]
137pub struct ResourceLimitsNode {
138 pub max_memory_bytes: SpannedValue<i64>,
139 pub computation_timeout_ms: SpannedValue<i64>,
140 pub max_state_size_bytes: SpannedValue<i64>,
141 pub span: Span,
142}
143
144#[derive(Debug, Clone, PartialEq)]
147pub struct HumanMachineContractNode {
148 pub system_commitments: Vec<SpannedValue<String>>,
149 pub system_refusals: Vec<SpannedValue<String>>,
150 pub user_obligations: Vec<SpannedValue<String>>,
151 pub span: Span,
152}
153
154#[derive(Debug, Clone, PartialEq)]
157pub struct ExtensionsNode {
158 pub systems: Vec<SystemExtensionNode>,
159 pub span: Span,
160}
161
162#[derive(Debug, Clone, PartialEq)]
163pub struct SystemExtensionNode {
164 pub name: SpannedValue<String>,
165 pub fields: Vec<CustomFieldNode>,
166 pub span: Span,
167}
168
169#[derive(Debug, Clone, PartialEq)]
170pub struct CustomFieldNode {
171 pub name: SpannedValue<String>,
172 pub value: LiteralValue,
173 pub span: Span,
174}
175
176#[derive(Debug, Clone, PartialEq)]
180pub struct SpannedValue<T> {
181 pub value: T,
182 pub span: Span,
183}
184
185impl<T> SpannedValue<T> {
186 pub fn new(value: T, span: Span) -> Self {
187 SpannedValue { value, span }
188 }
189}
190
191impl std::fmt::Display for PrimitiveType {
194 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
195 match self {
196 PrimitiveType::Integer => write!(f, "Integer"),
197 PrimitiveType::Float => write!(f, "Float"),
198 PrimitiveType::String => write!(f, "String"),
199 PrimitiveType::Boolean => write!(f, "Boolean"),
200 PrimitiveType::Iso8601 => write!(f, "ISO8601"),
201 PrimitiveType::Uuid => write!(f, "UUID"),
202 }
203 }
204}
205
206impl std::fmt::Display for TypeExpression {
207 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
208 match self {
209 TypeExpression::Primitive(p, _) => write!(f, "{}", p),
210 TypeExpression::Array(inner, _) => write!(f, "Array<{}>", inner),
211 TypeExpression::Map(k, v, _) => write!(f, "Map<{}, {}>", k, v),
212 TypeExpression::Object(fields, _) => {
213 write!(f, "Object {{ ")?;
214 for (i, field) in fields.iter().enumerate() {
215 if i > 0 {
216 write!(f, ", ")?;
217 }
218 write!(f, "{}: {}", field.name.value, field.type_expr)?;
219 }
220 write!(f, " }}")
221 }
222 TypeExpression::Enum(variants, _) => {
223 write!(f, "Enum [")?;
224 for (i, v) in variants.iter().enumerate() {
225 if i > 0 {
226 write!(f, ", ")?;
227 }
228 write!(f, "\"{}\"", v.value)?;
229 }
230 write!(f, "]")
231 }
232 }
233 }
234}
235
236impl std::fmt::Display for LiteralValue {
237 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
238 match self {
239 LiteralValue::String(s, _) => write!(f, "\"{}\"", s),
240 LiteralValue::Integer(n, _) => write!(f, "{}", n),
241 LiteralValue::Float(n, _) => write!(f, "{}", n),
242 LiteralValue::Boolean(b, _) => write!(f, "{}", b),
243 LiteralValue::Array(items, _) => {
244 write!(f, "[")?;
245 for (i, item) in items.iter().enumerate() {
246 if i > 0 {
247 write!(f, ", ")?;
248 }
249 write!(f, "{}", item)?;
250 }
251 write!(f, "]")
252 }
253 }
254 }
255}
256
257impl std::fmt::Display for ContractNode {
258 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
259 write!(
260 f,
261 "Contract(id={}, v={})",
262 self.identity.stable_id.value, self.identity.version.value
263 )
264 }
265}
266
267impl TypeExpression {
270 pub fn span(&self) -> &Span {
271 match self {
272 TypeExpression::Primitive(_, s) => s,
273 TypeExpression::Array(_, s) => s,
274 TypeExpression::Map(_, _, s) => s,
275 TypeExpression::Object(_, s) => s,
276 TypeExpression::Enum(_, s) => s,
277 }
278 }
279}
280
281impl LiteralValue {
282 pub fn span(&self) -> &Span {
283 match self {
284 LiteralValue::String(_, s) => s,
285 LiteralValue::Integer(_, s) => s,
286 LiteralValue::Float(_, s) => s,
287 LiteralValue::Boolean(_, s) => s,
288 LiteralValue::Array(_, s) => s,
289 }
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296
297 #[test]
298 fn test_primitive_type_display() {
299 assert_eq!(PrimitiveType::Integer.to_string(), "Integer");
300 assert_eq!(PrimitiveType::Float.to_string(), "Float");
301 assert_eq!(PrimitiveType::String.to_string(), "String");
302 assert_eq!(PrimitiveType::Boolean.to_string(), "Boolean");
303 assert_eq!(PrimitiveType::Iso8601.to_string(), "ISO8601");
304 assert_eq!(PrimitiveType::Uuid.to_string(), "UUID");
305 }
306
307 #[test]
308 fn test_type_expression_display() {
309 let span = Span {
310 line: 1,
311 column: 1,
312 offset: 0,
313 };
314
315 let int_ty = TypeExpression::Primitive(PrimitiveType::Integer, span.clone());
316 assert_eq!(int_ty.to_string(), "Integer");
317
318 let arr_ty = TypeExpression::Array(
319 Box::new(TypeExpression::Primitive(
320 PrimitiveType::String,
321 span.clone(),
322 )),
323 span.clone(),
324 );
325 assert_eq!(arr_ty.to_string(), "Array<String>");
326
327 let map_ty = TypeExpression::Map(
328 Box::new(TypeExpression::Primitive(
329 PrimitiveType::String,
330 span.clone(),
331 )),
332 Box::new(TypeExpression::Primitive(
333 PrimitiveType::Integer,
334 span.clone(),
335 )),
336 span.clone(),
337 );
338 assert_eq!(map_ty.to_string(), "Map<String, Integer>");
339 }
340
341 #[test]
342 fn test_enum_display() {
343 let span = Span {
344 line: 1,
345 column: 1,
346 offset: 0,
347 };
348 let enum_ty = TypeExpression::Enum(
349 vec![
350 SpannedValue::new("active".to_string(), span.clone()),
351 SpannedValue::new("inactive".to_string(), span.clone()),
352 ],
353 span.clone(),
354 );
355 assert_eq!(enum_ty.to_string(), r#"Enum ["active", "inactive"]"#);
356 }
357
358 #[test]
359 fn test_literal_display() {
360 let span = Span {
361 line: 1,
362 column: 1,
363 offset: 0,
364 };
365 assert_eq!(
366 LiteralValue::String("hello".to_string(), span.clone()).to_string(),
367 "\"hello\""
368 );
369 assert_eq!(LiteralValue::Integer(42, span.clone()).to_string(), "42");
370 #[allow(clippy::approx_constant)]
371 let float_val = LiteralValue::Float(3.14, span.clone());
372 assert_eq!(float_val.to_string(), "3.14");
373 assert_eq!(
374 LiteralValue::Boolean(true, span.clone()).to_string(),
375 "true"
376 );
377 }
378
379 #[test]
380 fn test_spanned_value() {
381 let span = Span {
382 line: 5,
383 column: 10,
384 offset: 50,
385 };
386 let sv = SpannedValue::new("test".to_string(), span.clone());
387 assert_eq!(sv.value, "test");
388 assert_eq!(sv.span, span);
389 }
390
391 #[test]
392 fn test_type_expression_span() {
393 let span = Span {
394 line: 3,
395 column: 7,
396 offset: 30,
397 };
398 let ty = TypeExpression::Primitive(PrimitiveType::Boolean, span.clone());
399 assert_eq!(ty.span(), &span);
400 }
401
402 #[test]
403 fn test_object_display() {
404 let span = Span {
405 line: 1,
406 column: 1,
407 offset: 0,
408 };
409 let obj = TypeExpression::Object(
410 vec![
411 StateFieldNode {
412 name: SpannedValue::new("x".to_string(), span.clone()),
413 type_expr: TypeExpression::Primitive(PrimitiveType::Integer, span.clone()),
414 default_value: None,
415 span: span.clone(),
416 },
417 StateFieldNode {
418 name: SpannedValue::new("y".to_string(), span.clone()),
419 type_expr: TypeExpression::Primitive(PrimitiveType::Float, span.clone()),
420 default_value: None,
421 span: span.clone(),
422 },
423 ],
424 span.clone(),
425 );
426 assert_eq!(obj.to_string(), "Object { x: Integer, y: Float }");
427 }
428
429 #[test]
430 fn test_nested_type_display() {
431 let span = Span {
432 line: 1,
433 column: 1,
434 offset: 0,
435 };
436 let inner = TypeExpression::Map(
438 Box::new(TypeExpression::Primitive(
439 PrimitiveType::String,
440 span.clone(),
441 )),
442 Box::new(TypeExpression::Primitive(
443 PrimitiveType::Integer,
444 span.clone(),
445 )),
446 span.clone(),
447 );
448 let outer = TypeExpression::Array(Box::new(inner), span.clone());
449 assert_eq!(outer.to_string(), "Array<Map<String, Integer>>");
450 }
451}