1use std::collections::HashMap;
3use std::hash::Hash;
4
5use pest::iterators::{Pair, Pairs};
6use crate::default_config;
7
8use crate::parser::ast::{AggregateDecl, AttributeDefinition, AuthorizationDecl, BoundedContextDecl, ComponentDecl, ContextMapDecl, ContextRelation, CustomDecl, DatasourceDecl, DomainEventDecl, EndpointDecl, EntityDecl, EnvDecl, FklDeclaration, FlowDecl, HttpRequestDecl, HttpResponseDecl, Identifier, ImplementationDecl, ImplementationTarget, ImplementationTargetType, IncludeDecl, LayerDecl, LayeredDecl, LayerRelationDecl, Loc, MessageDecl, MethodCallDecl, RelationDirection, ServerDecl, SourceSetDecl, SourceSetsDecl, StepDecl, StructDecl, UsedDomainObject, ValueObjectDecl, VariableDefinition};
9use crate::parser::parse_result::{ParseError, ParseResult};
10use crate::pest::Parser;
11
12#[derive(Parser)]
13#[grammar = "parser/fkl.pest"]
14pub struct FklParser;
15
16pub fn parse(code: &str) -> ParseResult<Vec<FklDeclaration>> {
17 match FklParser::parse(Rule::declarations, code) {
18 Err(e) => {
19 return Err(ParseError::msg(e.to_string()));
20 }
21 Ok(pairs) => {
22 Ok(consume_declarations(pairs))
23 }
24 }
25}
26
27fn consume_declarations(pairs: Pairs<Rule>) -> Vec<FklDeclaration> {
28 pairs.filter(|pair| {
29 return pair.as_rule() == Rule::declaration;
30 }).map(|pair| {
31 let mut decl: FklDeclaration = FklDeclaration::None;
32 for p in pair.into_inner() {
33 match p.as_rule() {
34 Rule::context_map_decl => {
35 decl = FklDeclaration::ContextMap(consume_context_map(p));
36 }
37 Rule::context_decl => {
38 decl = FklDeclaration::BoundedContext(consume_context(p));
39 }
40 Rule::aggregate_decl => {
41 decl = FklDeclaration::Aggregate(consume_aggregate(p));
42 }
43 Rule::entity_decl => {
44 decl = FklDeclaration::Entity(consume_entity(p));
45 }
46 Rule::component_decl => {
47 decl = FklDeclaration::Component(consume_component(p));
48 }
49 Rule::value_object_decl => {
50 decl = FklDeclaration::ValueObject(consume_value_object(p));
51 }
52 Rule::implementation_decl => {
53 decl = FklDeclaration::Implementation(consume_implementation(p));
54 }
55 Rule::struct_decl => {
56 decl = FklDeclaration::Struct(consume_struct(p));
57 }
58 Rule::layered_decl => {
59 decl = FklDeclaration::Layered(consume_layered(p));
60 }
61 Rule::source_sets_decl => {
62 decl = FklDeclaration::SourceSets(consume_source_sets(p));
63 }
64 Rule::include_decl => {
65 decl = FklDeclaration::Include(consume_include(p));
66 }
67 Rule::env_decl => {
68 decl = FklDeclaration::Env(consume_env(p));
69 }
70 _ => println!("unreachable declaration rule: {:?}", p.as_rule())
71 };
72 }
73 return decl;
74 }).collect::<Vec<FklDeclaration>>()
75}
76
77fn consume_include(pair: Pair<Rule>) -> IncludeDecl {
78 let mut path = String::new();
79 let loc = Loc::from_pair(pair.as_span());
80 for p in pair.into_inner() {
81 match p.as_rule() {
82 Rule::string => {
83 path = p.as_str().to_string();
84 }
85 _ => println!("unreachable content rule: {:?}", p.as_rule())
86 };
87 }
88
89 return IncludeDecl { path, loc };
90}
91
92fn consume_context_map(pair: Pair<Rule>) -> ContextMapDecl {
93 let mut context_decl_map: HashMap<String, BoundedContextDecl> = HashMap::new();
94 let mut identify = Identifier::default();
95 let mut relations: Vec<ContextRelation> = Vec::new();
96
97 for p in pair.into_inner() {
98 match p.as_rule() {
99 Rule::identifier => {
100 identify.name = p.as_str().to_string();
101 identify.loc = Loc::from_pair(p.as_span());
102 }
103 Rule::context_decl => {
104 let context_decl = consume_context(p);
105 context_decl_map.insert(context_decl.name.clone(), context_decl);
106 }
107 Rule::context_node_rel => {
108 let mut names: Vec<String> = vec![];
109 let mut direction: RelationDirection = RelationDirection::Undirected;
110 let mut source_type: Vec<String> = vec![];
111 let mut target_type: Vec<String> = vec![];
112
113 for p in p.into_inner() {
114 match p.as_rule() {
115 Rule::left_id | Rule::right_id => {
116 let context_name = p.as_str().to_string();
117 names.push(context_name.clone());
118 context_decl_map.insert(context_name.clone(), BoundedContextDecl {
119 name: context_name,
120 domain_events: vec![],
121 aggregates: vec![],
122 used_domain_objects: vec![],
123 });
124 }
125 Rule::rel_symbol => {
126 for p in p.into_inner() {
127 match p.as_rule() {
128 Rule::rs_both => {
129 direction = RelationDirection::BiDirected;
130 }
131 Rule::rs_left_to_right => {
132 direction = RelationDirection::PositiveDirected;
133 }
134 Rule::rs_right_to_left => {
135 direction = RelationDirection::NegativeDirected;
136 }
137 _ => println!("unreachable entity rule: {:?}", p.as_rule())
138 };
139 }
140 }
141 Rule::left_rel_defs => {
142 for p in p.into_inner() {
143 if p.as_rule() == Rule::rel_defs {
144 source_type = rel_defs(p);
145 }
146 }
147 }
148 Rule::right_rel_defs => {
149 for p in p.into_inner() {
150 if p.as_rule() == Rule::rel_defs {
151 target_type = rel_defs(p);
152 }
153 }
154 }
155 _ => println!("unreachable context rel rule: {:?}", p.as_rule())
156 };
157 }
158
159 relations.push(ContextRelation {
160 source: names[0].clone(),
161 target: names[1].clone(),
162 direction,
163 source_types: source_type,
164 target_types: target_type,
165 });
166 }
167 _ => println!("unreachable context_map rule: {:?}", p.as_rule())
168 };
169 }
170
171 let mut contexts = context_decl_map.into_iter().map(|(_, v)| v)
173 .collect::<Vec<BoundedContextDecl>>();
174
175 contexts.sort_by(|a, b| a.name.cmp(&b.name));
176
177 return ContextMapDecl {
178 name: identify,
179 contexts,
180 relations,
181 };
182}
183
184fn rel_defs(pair: Pair<Rule>) -> Vec<String> {
185 let mut types: Vec<String> = vec![];
186 for p in pair.into_inner() {
187 match p.as_rule() {
188 Rule::identifier => {
189 types.push(p.as_str().to_string());
190 }
191 _ => println!("unreachable rel_def rule: {:?}", p.as_rule())
192 };
193 }
194
195 return types;
196}
197
198fn consume_context(pair: Pair<Rule>) -> BoundedContextDecl {
199 let mut context = BoundedContextDecl::default();
200 for p in pair.into_inner() {
201 match p.as_rule() {
202 Rule::identifier => {
203 context.name = p.as_str().to_string();
204 }
205 Rule::aggregate_decl => {
206 context.aggregates.push(consume_aggregate(p));
207 }
208 Rule::used_domain_objects_decl => {
209 let vec = consume_use_domain_object(p);
210 context.used_domain_objects = [context.used_domain_objects, vec].concat();
211 }
212 _ => println!("unreachable context rule: {:?}", p.as_rule())
213 };
214 }
215 return context;
216}
217
218fn consume_aggregate(pair: Pair<Rule>) -> AggregateDecl {
219 let mut aggregate = AggregateDecl::default();
220 for p in pair.into_inner() {
221 match p.as_rule() {
222 Rule::identifier => {
223 aggregate.name = p.as_str().to_string();
224 }
225 Rule::inline_doc => {
226 aggregate.inline_doc = parse_inline_doc(p);
227 }
228 Rule::entity_decl => {
229 aggregate.entities.push(consume_entity(p));
230 }
231 Rule::used_domain_objects_decl => {
232 aggregate.used_domain_objects = [aggregate.used_domain_objects, consume_use_domain_object(p)].concat();
233 }
234 Rule::used_domain_event_decl => {
235 aggregate.domain_events = consume_use_domain_events(p);
236 }
237 Rule::struct_decl => {
238 let default_struct = consume_struct(p);
239 let fields = default_struct.fields;
240 aggregate.entities.push(EntityDecl {
241 name: aggregate.name.to_string(),
242 is_aggregate_root: false,
243 identify: Default::default(),
244 inline_doc: "".to_string(),
245 fields,
246 value_objects: vec![],
247 });
248 }
249 _ => println!("unreachable aggregate rule: {:?}", p.as_rule())
250 };
251 }
252
253 return aggregate;
254}
255
256pub fn consume_use_domain_events(pair: Pair<Rule>) -> Vec<DomainEventDecl> {
257 let mut domain_events: Vec<DomainEventDecl> = vec![];
258 for p in pair.into_inner() {
259 match p.as_rule() {
260 Rule::event_name => {
261 domain_events.push(DomainEventDecl {
262 name: p.as_str().to_string()
263 });
264 }
265 _ => println!("unreachable use_domain_events rule: {:?}", p.as_rule())
266 };
267 }
268
269 return domain_events;
270}
271
272fn consume_entity(pair: Pair<Rule>) -> EntityDecl {
273 let mut entity = EntityDecl::default();
274 for p in pair.into_inner() {
275 match p.as_rule() {
276 Rule::identifier => {
277 entity.name = p.as_str().to_string();
278 }
279 Rule::constructor_decl => {
280 entity.fields = consume_constructor_decl(p);
281 }
282 Rule::struct_decl => {
283 entity.fields = consume_struct_decl(p);
284 }
285 Rule::inline_doc => {
286 entity.inline_doc = parse_inline_doc(p);
287 }
288 Rule::value_object_decl => {
289 entity.value_objects.push(consume_value_object(p));
290 }
291 _ => println!("unreachable entity rule: {:?}", p.as_rule())
292 };
293 }
294 return entity;
295}
296
297fn consume_use_domain_object(pair: Pair<Rule>) -> Vec<UsedDomainObject> {
298 let mut used_domain_objects: Vec<UsedDomainObject> = vec![];
299 for p in pair.into_inner() {
300 match p.as_rule() {
301 Rule::identifier => {
302 used_domain_objects.push(UsedDomainObject {
303 name: p.as_str().to_string()
304 });
305 }
306 _ => println!("unreachable use_domain_object rule: {:?}", p.as_rule())
307 };
308 }
309
310 used_domain_objects
311}
312
313fn consume_constructor_decl(pair: Pair<Rule>) -> Vec<VariableDefinition> {
314 let mut fields: Vec<VariableDefinition> = vec![];
315 for p in pair.into_inner() {
316 match p.as_rule() {
317 Rule::parameters_decl => {
318 fields = consume_parameters(p);
319 }
320 _ => println!("unreachable constructor rule: {:?}", p.as_rule())
321 };
322 }
323 return fields;
324}
325
326fn consume_parameters(p: Pair<Rule>) -> Vec<VariableDefinition> {
327 let mut fields: Vec<VariableDefinition> = vec![];
328 for p in p.into_inner() {
329 match p.as_rule() {
330 Rule::name_type_def => {
331 fields.push(consume_parameter(p));
332 }
333 _ => println!("unreachable parameter_decl rule: {:?}", p.as_rule())
334 }
335 }
336
337 return fields;
338}
339
340fn consume_struct_decl(pair: Pair<Rule>) -> Vec<VariableDefinition> {
341 let mut fields: Vec<VariableDefinition> = vec![];
342 for p in pair.into_inner() {
343 match p.as_rule() {
344 Rule::fields_decl => {
345 fields = consume_fields_decl(p);
346 }
347 _ => println!("unreachable struct rule: {:?}", p.as_rule())
348 };
349 }
350 return fields;
351}
352
353fn consume_fields_decl(pair: Pair<Rule>) -> Vec<VariableDefinition> {
354 let mut fields: Vec<VariableDefinition> = vec![];
355 for p in pair.into_inner() {
356 match p.as_rule() {
357 Rule::name_type_def => {
358 fields.push(consume_parameter(p));
359 }
360 _ => println!("unreachable fields rule: {:?}", p.as_rule())
361 };
362 }
363 return fields;
364}
365
366fn consume_parameter(pair: Pair<Rule>) -> VariableDefinition {
367 let mut field = VariableDefinition::default();
368 for p in pair.into_inner() {
369 match p.as_rule() {
370 Rule::identifier => {
371 field.name = p.as_str().to_string();
372 }
373 Rule::param_type => {
374 field.type_type = p.as_str().to_string();
375 }
376 Rule::value => {
377 field.initializer = Some(p.as_str().to_string());
378 }
379 _ => println!("unreachable parameter rule: {:?}", p.as_rule())
380 };
381 }
382 return field;
383}
384
385fn consume_value_object(pair: Pair<Rule>) -> ValueObjectDecl {
386 let mut value_object = ValueObjectDecl::default();
387 for p in pair.into_inner() {
388 match p.as_rule() {
389 Rule::identifier => {
390 value_object.name = p.as_str().to_string();
391 }
392 Rule::constructor_decl => {
393 value_object.fields = consume_constructor_decl(p);
394 }
395 Rule::struct_decl => {
396 value_object.fields = consume_struct_decl(p);
397 }
398 _ => println!("unreachable value_object rule: {:?}", p.as_rule())
399 };
400 }
401 return value_object;
402}
403
404fn consume_component(pair: Pair<Rule>) -> ComponentDecl {
405 let mut component = ComponentDecl::default();
406 for p in pair.into_inner() {
407 match p.as_rule() {
408 Rule::identifier => {
409 component.name = p.as_str().to_string();
410 }
411 Rule::inline_doc => {
412 component.inline_doc = parse_inline_doc(p);
413 }
414 Rule::attr_decl => {
415 component.attributes.push(consume_attribute(p));
416 }
417 Rule::used_domain_objects_decl => {
418 component.used_domain_objects = [component.used_domain_objects, consume_use_domain_object(p)].concat();
419 }
420 _ => println!("unreachable component rule: {:?}", p.as_rule())
421 };
422 }
423 return component;
424}
425
426fn consume_attribute(pair: Pair<Rule>) -> AttributeDefinition {
427 let mut attribute = AttributeDefinition::default();
428 for p in pair.into_inner() {
429 match p.as_rule() {
430 Rule::identifier => {
431 attribute.key = p.as_str().to_string();
432 }
433 Rule::attr_value => {
434 attribute.value = consume_attr_value(p);
435 }
436 Rule::attr_list => {
437 for inner in p.into_inner() {
438 match inner.as_rule() {
439 Rule::attr_value => {
440 attribute.value = [attribute.value, consume_attr_value(inner)].concat();
441 }
442 _ => println!("unreachable attr_list rule: {:?}", inner.as_rule())
443 };
444 }
445 }
446 _ => println!("unreachable attribute rule: {:?}", p.as_rule())
447 };
448 }
449 return attribute;
450}
451
452fn consume_attr_value(pair: Pair<Rule>) -> Vec<String> {
453 let mut values = vec![];
454 for p in pair.into_inner() {
455 match p.as_rule() {
456 Rule::identifier => {
457 values.push(p.as_str().to_string());
458 }
459 Rule::string => {
460 values.push(parse_string(p.as_str()));
461 }
462 _ => println!("unreachable attr_value rule: {:?}", p.as_rule())
463 };
464 }
465 return values;
466}
467
468fn consume_implementation(pair: Pair<Rule>) -> ImplementationDecl {
469 let mut implementation = ImplementationDecl::default();
470 for p in pair.into_inner() {
471 match p.as_rule() {
472 Rule::identifier => {
473 implementation.name = p.as_str().to_string();
474 }
475 Rule::inline_doc => {
476 implementation.inline_doc = parse_inline_doc(p);
477 }
478 Rule::endpoint_decl => {
479 implementation.endpoint = consume_endpoint(p);
480 }
481 Rule::flow_decl => {
482 implementation.flow = consume_flow(p);
483 }
484 Rule::set_target_object => {
485 implementation.target = Some(consume_set_target_object(p));
486 }
487 _ => println!("unreachable implementation rule: {:?}", p.as_rule())
488 };
489 }
490 return implementation;
491}
492
493fn consume_set_target_object(pair: Pair<Rule>) -> ImplementationTarget {
494 let mut target = ImplementationTarget::default();
495 for p in pair.into_inner() {
496 match p.as_rule() {
497 Rule::set_aggregate_name => {
498 target.target_type = ImplementationTargetType::Aggregate;
499 target.name = p.as_str().to_string();
500 }
501 Rule::set_entity_name => {
502 target.target_type = ImplementationTargetType::Entity;
503 target.name = p.as_str().to_string();
504 }
505 _ => println!("unreachable set_target_object rule: {:?}", p.as_rule())
506 };
507 }
508 return target;
509}
510
511fn consume_endpoint(pair: Pair<Rule>) -> EndpointDecl {
512 let mut endpoint = EndpointDecl::default();
513 for p in pair.into_inner() {
514 match p.as_rule() {
515 Rule::identifier => {
516 endpoint.name = p.as_str().to_string();
517 }
518 Rule::http_request_decl => {
519 for inner in p.into_inner() {
520 match inner.as_rule() {
521 Rule::http_method => {
522 endpoint.method = inner.as_str().to_string();
523 }
524 Rule::uri => {
525 endpoint.uri = parse_string(inner.as_str());
526 }
527 _ => println!("unreachable http_request_decl rule: {:?}", inner.as_rule())
528 }
529 }
530 }
531 Rule::request_body => {
532 for inner in p.into_inner() {
533 match inner.as_rule() {
534 Rule::identifier => {
535 endpoint.request = Some(HttpRequestDecl {
536 name: inner.as_str().to_string(),
537 });
538 }
539 _ => println!("unreachable http_request_decl rule: {:?}", inner.as_rule())
540 }
541 }
542 }
543 Rule::authorization_decl => {
544 endpoint.authorization = Some(consume_authorization(p));
545 }
546 Rule::http_response_decl => {
547 endpoint.response = Some(consume_http_response(p));
548 }
549 _ => println!("unreachable endpoint rule: {:?}", p.as_rule())
550 };
551 }
552 return endpoint;
553}
554
555fn consume_struct(pair: Pair<Rule>) -> StructDecl {
556 let mut struct_decl = StructDecl::default();
557 for p in pair.into_inner() {
558 match p.as_rule() {
559 Rule::identifier => {
560 struct_decl.name = p.as_str().to_string();
561 }
562 Rule::inline_doc => {
563 struct_decl.inline_doc = parse_inline_doc(p);
564 }
565 Rule::fields_decl => {
566 struct_decl.fields = consume_fields_decl(p);
567 }
568 _ => println!("unreachable struct rule: {:?}", p.as_rule())
569 };
570 }
571 return struct_decl;
572}
573
574fn consume_authorization(pair: Pair<Rule>) -> AuthorizationDecl {
575 let mut authorization = AuthorizationDecl::default();
576 for p in pair.into_inner() {
577 match p.as_rule() {
578 Rule::authorization_type => {
579 authorization.auth_type = p.as_str().to_string();
580 }
581 Rule::username => {
582 authorization.username = Some(p.as_str().to_string());
583 }
584 Rule::password => {
585 authorization.password = Some(p.as_str().to_string());
586 }
587 _ => println!("unreachable authorization rule: {:?}", p.as_rule())
588 };
589 }
590 return authorization;
591}
592
593fn consume_http_response(pair: Pair<Rule>) -> HttpResponseDecl {
594 let mut response = HttpResponseDecl::default();
595 for p in pair.into_inner() {
596 match p.as_rule() {
597 Rule::identifier => {
598 response.name = p.as_str().to_string();
599 }
600 _ => println!("unreachable http_response rule: {:?}", p.as_rule())
601 };
602 }
603 return response;
604}
605
606fn consume_flow(pair: Pair<Rule>) -> Option<FlowDecl> {
607 let mut flow = FlowDecl::default();
608 for p in pair.into_inner() {
609 match p.as_rule() {
610 Rule::inline_doc => {
611 flow.inline_doc = parse_inline_doc(p);
612 }
613 Rule::via_method_decl => {
614 flow.steps.push(StepDecl::MethodCall(consume_via_method_decl(p)));
615 }
616 Rule::via_message_decl => {
617 flow.steps.push(StepDecl::Message(consume_via_message_decl(p)));
618 }
619 _ => println!("unreachable flow rule: {:?}", p.as_rule())
620 };
621 }
622 if flow.steps.len() == 0 {
623 return None;
624 }
625
626 return Some(flow);
627}
628
629fn consume_via_method_decl(pair: Pair<Rule>) -> MethodCallDecl {
630 let mut method_call = MethodCallDecl::default();
631 for p in pair.into_inner() {
632 match p.as_rule() {
633 Rule::identifier => {
634 method_call.name = p.as_str().to_string();
635 }
636 Rule::object_name => {
637 method_call.object = p.as_str().to_string();
638 }
639 Rule::method_name => {
640 method_call.method = p.as_str().to_string();
641 }
642 Rule::parameters_decl => {
643 method_call.arguments = consume_parameters(p);
644 }
645 Rule::receive_object => {
646 method_call.return_type = Some(consume_parameter(p.into_inner().next().unwrap()));
647 }
648 _ => println!("unreachable via_method_decl rule: {:?}", p.as_rule())
649 };
650 }
651 return method_call;
652}
653
654fn consume_via_message_decl(pair: Pair<Rule>) -> MessageDecl {
655 let mut message = MessageDecl::default();
656 for p in pair.into_inner() {
657 match p.as_rule() {
658 Rule::object_name => {
659 message.from = p.as_str().to_string();
660 }
661 Rule::topic_name => {
662 message.topic = p.as_str().to_string();
663 }
664 Rule::pass_object => {
665 message.message = p.as_str().to_string();
666 }
667 _ => println!("unreachable via_message_decl rule: {:?}", p.as_rule())
668 };
669 }
670 return message;
671}
672
673fn consume_layered(pair: Pair<Rule>) -> LayeredDecl {
674 let mut layered = LayeredDecl::default();
675 for p in pair.into_inner() {
676 match p.as_rule() {
677 Rule::identifier => {
678 layered.name = p.as_str().to_string();
679 }
680 Rule::inline_doc => {
681 layered.inline_doc = parse_inline_doc(p);
682 }
683 Rule::dependency_decl => {
684 layered.dependencies = consume_dependency_decl(p);
685 }
686 Rule::layer_decl => {
687 layered.layers.push(consume_layer_decl(p));
688 }
689 _ => println!("unreachable layered rule: {:?}", p.as_rule())
690 };
691 }
692
693 return layered;
694}
695
696fn consume_layer_decl(pair: Pair<Rule>) -> LayerDecl {
697 let mut layer = LayerDecl::default();
698 for p in pair.into_inner() {
699 match p.as_rule() {
700 Rule::identifier => {
701 layer.name = p.as_str().to_string();
702 }
703 Rule::inline_doc => {
704 layer.inline_doc = parse_inline_doc(p);
705 }
706 Rule::package_def => {
707 for p in p.into_inner() {
708 match p.as_rule() {
709 Rule::package => {
710 layer.package = parse_string(p.as_str());
711 }
712 _ => println!("unreachable package_def rule: {:?}", p.as_rule())
713 };
714 }
715 }
716 _ => println!("unreachable layer rule: {:?}", p.as_rule())
717 };
718 }
719
720 return layer;
721}
722
723fn consume_dependency_decl(pair: Pair<Rule>) -> Vec<LayerRelationDecl> {
724 let mut relations: Vec<LayerRelationDecl> = vec![];
725 for p in pair.into_inner() {
726 match p.as_rule() {
727 Rule::dependency_entry => {
728 relations.push(consume_dependency_entry(p));
729 }
730
731 _ => println!("unreachable dependency rule: {:?}", p.as_rule())
732 };
733 }
734 return relations;
735}
736
737fn consume_dependency_entry(pair: Pair<Rule>) -> LayerRelationDecl {
738 let mut relation = LayerRelationDecl::default();
739 for p in pair.into_inner() {
740 match p.as_rule() {
741 Rule::source => {
742 relation.source = parse_ident_or_string(p);
743 }
744 Rule::target => {
745 relation.target = parse_ident_or_string(p);
746 }
747 Rule::rs_left_to_right => {}
748 _ => println!("unreachable dependency entry: {:?}", p.as_rule())
749 };
750 }
751
752 relation
753}
754
755fn consume_source_sets(pair: Pair<Rule>) -> SourceSetsDecl {
756 let mut source_sets = SourceSetsDecl::default();
757 for p in pair.into_inner() {
758 match p.as_rule() {
759 Rule::identifier => {
760 source_sets.name = p.as_str().to_string();
761 }
762 Rule::source_set_decl => {
763 source_sets.source_sets.push(consume_source_set_decl(p));
764 }
765 _ => println!("unreachable source_sets rule: {:?}", p.as_rule())
766 };
767 }
768
769 source_sets
770}
771
772fn consume_source_set_decl(pair: Pair<Rule>) -> SourceSetDecl {
773 let mut source_set = SourceSetDecl::default();
774 for p in pair.into_inner() {
775 match p.as_rule() {
776 Rule::identifier => {
777 source_set.name = p.as_str().to_string();
778 }
779 Rule::attr_decl => {
780 source_set.attributes.push(consume_attribute(p));
781 }
782 _ => println!("unreachable source_set rule: {:?}", p.as_rule())
783 };
784 }
785
786 return source_set;
787}
788
789fn consume_env(pair: Pair<Rule>) -> EnvDecl {
790 let mut env = EnvDecl::default();
791 for p in pair.into_inner() {
792 match p.as_rule() {
793 Rule::identifier => {
794 env.name = p.as_str().to_string();
795 }
796 Rule::datasource_decl => {
797 env.datasource = Some(consume_datasource_decl(p));
798 }
799 Rule::server_decl => {
800 env.server = Some(consume_server_decl(p));
801 }
802 Rule::custom_decl => {
803 env.customs.push(consume_custom_decl(p));
804 }
805 _ => println!("unreachable env rule: {:?}", p.as_rule())
806 };
807 }
808
809 env
810}
811
812fn consume_datasource_decl(pair: Pair<Rule>) -> DatasourceDecl {
813 let mut attrs: HashMap<String, String> = HashMap::default();
814 for p in pair.into_inner() {
815 match p.as_rule() {
816 Rule::attr_decl => {
817 let attr = consume_attribute(p);
818 attrs.insert(attr.key.clone(), attr.value[0].clone());
819 }
820 _ => println!("unreachable datasource rule: {:?}", p.as_rule())
821 };
822 }
823
824 let mut decl = DatasourceDecl::default();
825 decl.url = attrs.get("url").unwrap_or(&"".to_string()).clone();
826
827 decl.driver = attrs.get("driver").unwrap_or(&"".to_string()).clone();
828 decl.port = attrs.get("port").unwrap_or(&"".to_string()).clone();
829 decl.host = attrs.get("host").unwrap_or(&"".to_string()).clone();
830 decl.database = attrs.get("database").unwrap_or(&"".to_string()).clone();
831 decl.username = attrs.get("username").unwrap_or(&"".to_string()).clone();
832 decl.password = attrs.get("password").unwrap_or(&"".to_string()).clone();
833
834 decl
835}
836
837fn consume_server_decl(pair: Pair<Rule>) -> ServerDecl {
838 let mut attrs: HashMap<String, String> = HashMap::default();
839 for p in pair.into_inner() {
840 match p.as_rule() {
841 Rule::attr_decl => {
842 let attr = consume_attribute(p);
843 attrs.insert(attr.key.clone(), attr.value[0].clone());
844 }
845 _ => println!("unreachable server rule: {:?}", p.as_rule())
846 };
847 }
848
849 let mut decl = ServerDecl::default();
850 decl.port = attrs.get("port")
851 .unwrap_or(&default_config::SERVER_PORT.to_string())
852 .parse()
853 .unwrap_or(default_config::SERVER_PORT)
854 .clone();
855 decl
856}
857
858fn consume_custom_decl(pair: Pair<Rule>) -> CustomDecl {
859 let mut decl = CustomDecl::default();
860 for p in pair.into_inner() {
861 match p.as_rule() {
862 Rule::identifier => {
863 decl.name = p.as_str().to_string();
864 }
865 Rule::attr_decl => {
866 decl.attributes.push(consume_attribute(p));
867 }
868 _ => println!("unreachable server rule: {:?}", p.as_rule())
869 };
870 }
871
872 decl
873}
874
875fn parse_ident_or_string(pair: Pair<Rule>) -> String {
876 let mut ident = String::new();
877 for p in pair.into_inner() {
878 match p.as_rule() {
879 Rule::identifier => {
880 ident = p.as_str().to_string();
881 }
882 Rule::string => {
883 ident = parse_string(p.as_str());
884 }
885 _ => println!("unreachable ident_or_string rule: {:?}", p.as_rule())
886 };
887 }
888
889 ident
890}
891
892fn parse_string(str: &str) -> String {
893 let mut s = str.to_string();
894 s.remove(0);
895 s.remove(s.len() - 1);
896
897 s
898}
899
900fn parse_inline_doc(pair: Pair<Rule>) -> String {
901 let len = "\"\"\"".len();
902
903 pair.as_str().chars().skip(len).take(pair.as_str().len() - len * 2).collect()
904}
905
906#[cfg(test)]
907mod tests {
908 use crate::parser::ast::*;
909 use crate::parser::ast::ImplementationTargetType::Aggregate;
910 use crate::parser::ast::RelationDirection::{BiDirected, PositiveDirected};
911 use crate::parser::ast::StepDecl::{Message, MethodCall};
912 use crate::parser::parser::parse;
913
914 #[test]
915 fn parse_context_map() {
916 let decls = parse(r#"
917ContextMap {
918 ShoppingCarContext -> MallContext;
919 ShoppingCarContext <-> MallContext;
920}
921
922Context ShoppingCarContext {
923
924}
925"#).unwrap();
926
927 assert_eq!(decls[0], FklDeclaration::ContextMap(ContextMapDecl {
928 name: Identifier {
929 name: "".to_string(),
930 loc: Default::default(),
931 },
932 contexts: vec![
933 BoundedContextDecl {
934 name: "MallContext".to_string(),
935 domain_events: vec![],
936 aggregates: vec![],
937 used_domain_objects: vec![],
938 },
939 BoundedContextDecl {
940 name: "ShoppingCarContext".to_string(),
941 domain_events: vec![],
942 aggregates: vec![],
943 used_domain_objects: vec![],
944 },
945 ],
946 relations: vec![
947 ContextRelation { source: "ShoppingCarContext".to_string(), target: "MallContext".to_string(), direction: PositiveDirected, source_types: vec![], target_types: vec![] },
948 ContextRelation { source: "ShoppingCarContext".to_string(), target: "MallContext".to_string(), direction: BiDirected, source_types: vec![], target_types: vec![] },
949 ],
950 }));
951 }
952
953 #[test]
954 fn long_string() {
955 let decls = parse(r#"
956Aggregate Sample {
957 """ inline doc sample
958just for test
959"""
960}
961"#).unwrap();
962
963 assert_eq!(decls[0], FklDeclaration::Aggregate(AggregateDecl {
964 name: "Sample".to_string(),
965 inline_doc: r#" inline doc sample
966just for test
967"#.to_string(),
968 used_domain_objects: vec![],
969 entities: vec![],
970 value_objects: vec![],
971 domain_events: vec![],
972 }));
973 }
974
975 #[test]
976 fn aggregate() {
977 let decls = parse(r#"
978Aggregate ShoppingCart {
979 Entity Product {
980 constructor(name: String, price: Money)
981 }
982}
983"#).unwrap();
984
985 assert_eq!(decls[0], FklDeclaration::Aggregate(AggregateDecl {
986 name: "ShoppingCart".to_string(),
987 inline_doc: "".to_string(),
988 used_domain_objects: vec![],
989 entities: vec![EntityDecl {
990 is_aggregate_root: false,
991 name: "Product".to_string(),
992 identify: Default::default(),
993 inline_doc: "".to_string(),
994 fields: vec![
995 VariableDefinition {
996 name: "name".to_string(),
997 type_type: "String".to_string(),
998 initializer: None,
999 },
1000 VariableDefinition {
1001 name: "price".to_string(),
1002 type_type: "Money".to_string(),
1003 initializer: None,
1004 }],
1005 value_objects: vec![],
1006 }],
1007 value_objects: vec![],
1008 domain_events: vec![],
1009 }))
1010 }
1011
1012 #[test]
1013 fn full_sample() {
1014 parse("
1015ContextMap {
1016 SalesContext <-> SalesContext;
1017}
1018
1019Context SalesContext {
1020 Aggregate SalesOrder {
1021 Entity SalesOrderLine {
1022 constructor(product: Product, quantity: Quantity)
1023 }
1024 }
1025}
1026
1027Entity Opportunity {
1028 constructor(
1029 id: String,
1030 name: String,
1031 description: String,
1032 status: OpportunityStatus,
1033 amount: Money,
1034 probability: Probability,
1035 closeDate: Date,
1036 contacts: Vec<Contact>,
1037 products: Vec<Product>,
1038 notes: Vec<Note>,
1039 attachments: Vec<Attachment>,
1040 activities: Vec<Activity>,
1041 tasks: Vec<Task>,
1042 events: Vec<Event>,
1043 created: DateTime,
1044 createdBy: String,
1045 modified: DateTime,
1046 modifiedBy: String
1047 )
1048}
1049
1050Entity Pipeline {
1051 constructor(
1052 id: String,
1053 name: String,
1054 description: String,
1055 stages: Vec<Stage>,
1056 opportunities: Vec<Opportunity>,
1057 created: DateTime,
1058 createdBy: String,
1059 modified: DateTime,
1060 modifiedBy: String
1061 )
1062}
1063
1064Entity Contact {
1065 constructor(
1066 id: String,
1067 firstName: String,
1068 lastName: String,
1069 email: String,
1070 phone: String,
1071 title: String,
1072 department: String,
1073 account: Account,
1074 address: Address,
1075 created: DateTime,
1076 createdBy: String,
1077 modified: DateTime,
1078 modifiedBy: String,
1079 )
1080}
1081
1082Entity Account {
1083 constructor(
1084 id: String,
1085 name: String,
1086 website: String,
1087 phone: String,
1088 industry: String,
1089 employees: String,
1090 annualRevenue: Money,
1091 billingAddress: Address,
1092 shippingAddress: Address,
1093 contacts: Vec<Contact>,
1094 created: DateTime,
1095 createdBy: String,
1096 modified: DateTime,
1097 modifiedBy: String,
1098 )
1099}
1100
1101Entity Product {
1102 constructor(
1103 id: String,
1104 name: String,
1105 description: String,
1106 price: Money,
1107 category: String,
1108 created: DateTime,
1109 createdBy: String,
1110 modified: DateTime,
1111 modifiedBy: String,
1112 )
1113}
1114
1115Entity Territory {
1116 constructor(
1117 id: String,
1118 name: String,
1119 description: String,
1120 created: DateTime,
1121 createdBy: String,
1122 modified: DateTime,
1123 modifiedBy: String,
1124 )
1125}
1126
1127Entity SalesPerson {
1128 constructor(
1129 id: String,
1130 firstName: String,
1131 lastName: String,
1132 email: String,
1133 phone: String,
1134 title: String,
1135 department: String,
1136 account: Account,
1137 address: Address,
1138 territories: Vec<Territory>,
1139 created: DateTime,
1140 createdBy: String,
1141 modified: DateTime,
1142 modifiedBy: String,
1143 )
1144}
1145").unwrap();
1146 }
1147
1148 #[test]
1149 fn basic_vo_inline_aggregate() {
1150 let decls = parse(r#"Context Cart {
1151 Aggregate Cart {
1152 Entity Cart {
1153 ValueObject CartId
1154 ValueObject CartStatus
1155 ValueObject CartItem
1156 ValueObject CartItemQuantity
1157 ValueObject CartItemPrice
1158 ValueObject CartItemTotal
1159 ValueObject CartTotal
1160 }
1161 }
1162}"#).unwrap();
1163
1164 assert_eq!(decls[0], FklDeclaration::BoundedContext(BoundedContextDecl {
1165 name: "Cart".to_string(),
1166 domain_events: vec![],
1167 aggregates: vec![
1168 AggregateDecl {
1169 name: "Cart".to_string(),
1170 inline_doc: "".to_string(),
1171 used_domain_objects: vec![],
1172 entities: vec![EntityDecl {
1173 is_aggregate_root: false,
1174 name: "Cart".to_string(),
1175 identify: Default::default(),
1176 inline_doc: "".to_string(),
1177 fields: vec![],
1178 value_objects: vec![
1179 ValueObjectDecl {
1180 name: "CartId".to_string(),
1181 inline_doc: "".to_string(),
1182 fields: vec![],
1183 },
1184 ValueObjectDecl {
1185 name: "CartStatus".to_string(),
1186 inline_doc: "".to_string(),
1187 fields: vec![],
1188 },
1189 ValueObjectDecl {
1190 name: "CartItem".to_string(),
1191 inline_doc: "".to_string(),
1192 fields: vec![],
1193 },
1194 ValueObjectDecl {
1195 name: "CartItemQuantity".to_string(),
1196 inline_doc: "".to_string(),
1197 fields: vec![],
1198 },
1199 ValueObjectDecl {
1200 name: "CartItemPrice".to_string(),
1201 inline_doc: "".to_string(),
1202 fields: vec![],
1203 },
1204 ValueObjectDecl {
1205 name: "CartItemTotal".to_string(),
1206 inline_doc: "".to_string(),
1207 fields: vec![],
1208 },
1209 ValueObjectDecl {
1210 name: "CartTotal".to_string(),
1211 inline_doc: "".to_string(),
1212 fields: vec![],
1213 },
1214 ],
1215 }],
1216 value_objects: vec![],
1217 domain_events: vec![],
1218 }
1219 ],
1220 used_domain_objects: vec![],
1221 }));
1222 }
1223
1224 #[test]
1225 fn bind_api() {
1226 let decls = parse(r#"
1227Component SalesComponent {
1228 name = 'Sample Phodal';
1229 type: Application;
1230 Aggregate SalesOrder;
1231}
1232"#);
1233
1234 assert_eq!(decls.unwrap()[0], FklDeclaration::Component(ComponentDecl {
1235 name: "SalesComponent".to_string(),
1236 inline_doc: "".to_string(),
1237 component_type: ComponentType::Application,
1238 attributes: vec![
1239 AttributeDefinition {
1240 key: "name".to_string(),
1241 value: vec!["Sample Phodal".to_string()],
1242 }, AttributeDefinition {
1243 key: "type".to_string(),
1244 value: vec!["Application".to_string()],
1245 },
1246 ],
1247 used_domain_objects: vec![
1248 UsedDomainObject { name: "SalesOrder".to_string() },
1249 ],
1250 }));
1251 }
1252
1253 #[test]
1254 fn rel_with_context_map() {
1255 let decls = parse(r#"ContextMap Mall {
1256 SalesContext [ OHS ] <-> OrderContext [ rel = "ACL, OHS" ];
1257}"#).unwrap();
1258
1259 let except = FklDeclaration::ContextMap(ContextMapDecl {
1260 name: Identifier {
1261 name: "Mall".to_string(),
1262 loc: Loc(11, 15),
1263 },
1264 contexts: vec![
1265 BoundedContextDecl { name: "OrderContext".to_string(), domain_events: vec![], aggregates: vec![], used_domain_objects: vec![] },
1266 BoundedContextDecl { name: "SalesContext".to_string(), domain_events: vec![], aggregates: vec![], used_domain_objects: vec![] },
1267 ],
1268 relations: vec![ContextRelation {
1269 source: "SalesContext".to_string(),
1270 target: "OrderContext".to_string(),
1271 direction: BiDirected,
1272 source_types: vec!["OHS".to_string()],
1273 target_types: vec!["ACL".to_string(), "OHS".to_string()],
1274 }],
1275 });
1276 assert_eq!(decls[0], except);
1277
1278 let order2 = parse(r#"ContextMap Mall {
1279 SalesContext [ OHS ] <-> [rel = "ACL, OHS" ] OrderContext;
1280}"#).unwrap();
1281 assert_eq!(order2[0], except);
1282 }
1283
1284 #[test]
1285 fn rel_with_context_map_with_inline_doc() {
1286 let decls = parse(r#"Entity Reservation {
1287 Struct {
1288 id: String;
1289 token: UUID;
1290 status: ReservationStatus = ReservationStatus.OPEN;
1291 expiresAt: LocalDateTime;
1292 createdAt: LocalDateTime;
1293 screeningId: String;
1294 screeningStartTime: LocalDateTime;
1295 name: String;
1296 surname: String;
1297 tickets: Set<Ticket>;
1298 totalPrice: BigDecimal;
1299 }
1300}"#).unwrap();
1301
1302 assert_eq!(decls[0], FklDeclaration::Entity(EntityDecl {
1303 is_aggregate_root: false,
1304 name: "Reservation".to_string(),
1305 identify: VariableDefinition {
1306 name: "".to_string(),
1307 type_type: "".to_string(),
1308 initializer: None,
1309 },
1310 inline_doc: "".to_string(),
1311 fields: vec![
1312 VariableDefinition { name: "id".to_string(), type_type: "String".to_string(), initializer: None },
1313 VariableDefinition { name: "token".to_string(), type_type: "UUID".to_string(), initializer: None },
1314 VariableDefinition { name: "status".to_string(), type_type: "ReservationStatus".to_string(), initializer: Some("ReservationStatus.OPEN".to_string()) },
1315 VariableDefinition { name: "expiresAt".to_string(), type_type: "LocalDateTime".to_string(), initializer: None },
1316 VariableDefinition { name: "createdAt".to_string(), type_type: "LocalDateTime".to_string(), initializer: None },
1317 VariableDefinition { name: "screeningId".to_string(), type_type: "String".to_string(), initializer: None },
1318 VariableDefinition { name: "screeningStartTime".to_string(), type_type: "LocalDateTime".to_string(), initializer: None },
1319 VariableDefinition { name: "name".to_string(), type_type: "String".to_string(), initializer: None },
1320 VariableDefinition { name: "surname".to_string(), type_type: "String".to_string(), initializer: None },
1321 VariableDefinition { name: "tickets".to_string(), type_type: "Set<Ticket>".to_string(), initializer: None },
1322 VariableDefinition { name: "totalPrice".to_string(), type_type: "BigDecimal".to_string(), initializer: None }],
1323 value_objects: vec![],
1324 }));
1325 }
1326
1327 #[test]
1328 fn use_vo() {
1329 let decls = parse(r#"Context Cinema {
1330 Aggregate Cinema;
1331}
1332
1333Aggregate Cinema {
1334 Entity Cinema, ScreeningRoom, Seat;
1335}
1336"#).unwrap();
1337
1338 assert_eq!(decls[1], FklDeclaration::Aggregate(
1339 AggregateDecl {
1340 name: "Cinema".to_string(),
1341 inline_doc: "".to_string(),
1342 used_domain_objects: vec![
1343 UsedDomainObject { name: "Cinema".to_string() },
1344 UsedDomainObject { name: "ScreeningRoom".to_string() },
1345 UsedDomainObject { name: "Seat".to_string() }],
1346 entities: vec![],
1347 value_objects: vec![],
1348 domain_events: vec![],
1349 })
1350 );
1351 }
1352
1353 #[test]
1354 fn aggregate_binding_syntax() {
1355 let result = parse(r#"
1356impl CinemaCreatedEvent {
1357 endpoint {
1358 GET "/book/{id}";
1359 authorization: Basic admin admin;
1360 response: Cinema;
1361 }
1362}
1363
1364struct Cinema {
1365 id: String;
1366 name: String;
1367 address: String;
1368 rooms: Set<ScreeningRoom>;
1369}
1370"#).unwrap();
1371
1372 assert_eq!(result[0], FklDeclaration::Implementation(ImplementationDecl {
1373 name: "CinemaCreatedEvent".to_string(),
1374 inline_doc: "".to_string(),
1375 qualified_name: "".to_string(),
1376 endpoint: EndpointDecl {
1377 name: "".to_string(),
1378 method: "GET".to_string(),
1379 uri: "/book/{id}".to_string(),
1380 authorization: Some(AuthorizationDecl {
1381 auth_type: "Basic".to_string(),
1382 username: Some("admin".to_string()),
1383 password: Some("admin".to_string()),
1384 }),
1385 request: None,
1386 response: Some(HttpResponseDecl {
1387 name: "Cinema".to_string()
1388 }),
1389 },
1390 target: None,
1391 flow: None,
1392 }));
1393
1394 assert_eq!(result[1], FklDeclaration::Struct(StructDecl {
1395 name: "Cinema".to_string(),
1396 inline_doc: "".to_string(),
1397 fields: vec![
1398 VariableDefinition { name: "id".to_string(), type_type: "String".to_string(), initializer: None },
1399 VariableDefinition { name: "name".to_string(), type_type: "String".to_string(), initializer: None },
1400 VariableDefinition { name: "address".to_string(), type_type: "String".to_string(), initializer: None },
1401 VariableDefinition { name: "rooms".to_string(), type_type: "Set<ScreeningRoom>".to_string(), initializer: None },
1402 ],
1403 }));
1404 }
1405
1406 #[test]
1407 fn error_handling() {
1408 let result = parse(r#"
1409imple CinemaCreatedEvent {
1410
1411}"#);
1412 match result {
1413 Err(e) => {
1414 let string = format!("{}", e);
1415 assert_eq!(string, r#" --> 2:1
1416 |
14172 | imple CinemaCreatedEvent {
1418 | ^---
1419 |
1420 = expected EOI or declaration"#);
1421 }
1422 _ => assert!(false),
1423 };
1424 }
1425
1426 #[test]
1427 fn impl_with_flow() {
1428 let decls = parse(r#"impl CinemaUpdated {
1429 aggregate: Cinema;
1430 endpoint {
1431 POST "/book/{id}";
1432 request: CinemaUpdatedRequest;
1433 authorization: Basic admin admin;
1434 response: Cinema;
1435 }
1436
1437 flow {
1438 via UserRepository::getUserById receive user: User
1439 via UserRepository::save(user: User) receive user: User;
1440 via MessageQueue send CinemaCreated to "CinemaCreated"
1441 }
1442}
1443"#).or_else(|e| {
1444 println!("{}", e);
1445 Err(e)
1446 }).unwrap();
1447
1448 assert_eq!(decls[0], FklDeclaration::Implementation(ImplementationDecl {
1449 name: "CinemaUpdated".to_string(),
1450 inline_doc: "".to_string(),
1451 qualified_name: "".to_string(),
1452 endpoint: EndpointDecl {
1453 name: "".to_string(),
1454 method: "POST".to_string(),
1455 uri: "/book/{id}".to_string(),
1456 authorization: Some(AuthorizationDecl {
1457 auth_type: "Basic".to_string(),
1458 username: Some("admin".to_string()),
1459 password: Some("admin".to_string()),
1460 }),
1461 request: Some(HttpRequestDecl {
1462 name: "CinemaUpdatedRequest".to_string()
1463 }),
1464 response: Some(HttpResponseDecl {
1465 name: "Cinema".to_string()
1466 }),
1467 },
1468 target: Some(ImplementationTarget {
1469 target_type: Aggregate,
1470 name: "Cinema".to_string(),
1471 }),
1472 flow: Some(FlowDecl {
1473 inline_doc: "".to_string(),
1474 steps: vec![
1475 MethodCall(MethodCallDecl {
1476 name: "".to_string(),
1477 object: "UserRepository".to_string(),
1478 method: "getUserById".to_string(),
1479 arguments: vec![],
1480 return_type: Some(VariableDefinition {
1481 name: "user".to_string(),
1482 type_type: "User".to_string(),
1483 initializer: None,
1484 }),
1485 }),
1486 MethodCall(MethodCallDecl {
1487 name: "".to_string(),
1488 object: "UserRepository".to_string(),
1489 method: "save".to_string(),
1490 arguments: vec![VariableDefinition {
1491 name: "user".to_string(),
1492 type_type: "User".to_string(),
1493 initializer: None,
1494 }],
1495 return_type: Some(VariableDefinition {
1496 name: "user".to_string(),
1497 type_type: "User".to_string(),
1498 initializer: None,
1499 }),
1500 }),
1501 Message(MessageDecl {
1502 from: "MessageQueue".to_string(),
1503 topic: "\"CinemaCreated\"".to_string(),
1504 message: "CinemaCreated".to_string(),
1505 }),
1506 ],
1507 }),
1508 }));
1509 }
1510
1511 #[test]
1512 fn layered_architecture() {
1513 let decls = parse(r#"layered DDD {
1514 dependency {
1515 "rest" -> "application"
1516 "rest" -> "domain"
1517 "domain" -> "application"
1518 "application" -> "infrastructure"
1519 "rest" -> "infrastructure"
1520 }
1521 layer rest {
1522 package: "com.example.book";
1523 }
1524 layer domain {
1525 package: "com.example.domain";
1526 }
1527 layer application {
1528 package: "com.example.application";
1529 }
1530 layer infrastructure {
1531 package: "com.example.infrastructure";
1532 }
1533}"#).or_else(|e| {
1534 println!("{}", e);
1535 Err(e)
1536 }).unwrap();
1537
1538 assert_eq!(decls[0], FklDeclaration::Layered(LayeredDecl {
1539 name: "DDD".to_string(),
1540 inline_doc: "".to_string(),
1541 dependencies: vec![
1542 LayerRelationDecl {
1543 source: "rest".to_string(),
1544 target: "application".to_string(),
1545 },
1546 LayerRelationDecl {
1547 source: "rest".to_string(),
1548 target: "domain".to_string(),
1549 },
1550 LayerRelationDecl {
1551 source: "domain".to_string(),
1552 target: "application".to_string(),
1553 },
1554 LayerRelationDecl {
1555 source: "application".to_string(),
1556 target: "infrastructure".to_string(),
1557 },
1558 LayerRelationDecl {
1559 source: "rest".to_string(),
1560 target: "infrastructure".to_string(),
1561 },
1562 ],
1563 layers: vec![
1564 LayerDecl {
1565 name: "rest".to_string(),
1566 inline_doc: "".to_string(),
1567 package: "com.example.book".to_string(),
1568 },
1569 LayerDecl {
1570 name: "domain".to_string(),
1571 inline_doc: "".to_string(),
1572 package: "com.example.domain".to_string(),
1573 },
1574 LayerDecl {
1575 name: "application".to_string(),
1576 inline_doc: "".to_string(),
1577 package: "com.example.application".to_string(),
1578 },
1579 LayerDecl {
1580 name: "infrastructure".to_string(),
1581 inline_doc: "".to_string(),
1582 package: "com.example.infrastructure".to_string(),
1583 },
1584 ],
1585 }));
1586 }
1587
1588 #[test]
1589 fn parse_source_set() {
1590 let decls = parse(r#"SourceSet sourceSet {
1591 feakin {
1592 srcDir: ["src/main/resources/uml"]
1593 }
1594 puml {
1595 parser: "PlantUML"
1596 srcDir: ["src/main/resources/uml"]
1597 }
1598}"#).or_else(|e| {
1599 println!("{}", e);
1600 Err(e)
1601 }).unwrap();
1602
1603 assert_eq!(decls[0], FklDeclaration::SourceSets(SourceSetsDecl {
1604 name: "sourceSet".to_string(),
1605 source_sets: vec![
1606 SourceSetDecl {
1607 name: "feakin".to_string(),
1608 attributes: vec![
1609 AttributeDefinition {
1610 key: "srcDir".to_string(),
1611 value: vec!["src/main/resources/uml".to_string()],
1612 }],
1613 },
1614 SourceSetDecl {
1615 name: "puml".to_string(),
1616 attributes: vec![
1617 AttributeDefinition {
1618 key: "parser".to_string(),
1619 value: vec!["PlantUML".to_string()],
1620 },
1621 AttributeDefinition {
1622 key: "srcDir".to_string(),
1623 value: vec!["src/main/resources/uml".to_string()],
1624 }],
1625 }],
1626 }));
1627 }
1628
1629 #[test]
1630 fn aggregate_domain_event() {
1631 let decls = parse(r#"Aggregate User {
1632 DomainEvent UserCreated, UserUpdated;
1633}"#).or_else(|e| {
1634 println!("{}", e);
1635 Err(e)
1636 }).unwrap();
1637
1638 assert_eq!(decls[0], FklDeclaration::Aggregate(AggregateDecl {
1639 name: "User".to_string(),
1640 inline_doc: "".to_string(),
1641 used_domain_objects: vec![],
1642 entities: vec![],
1643 value_objects: vec![],
1644 domain_events: vec![
1645 DomainEventDecl { name: "UserCreated".to_string() },
1646 DomainEventDecl { name: "UserUpdated".to_string() },
1647 ],
1648 }));
1649 }
1650
1651 #[test]
1652 fn env_database() {
1653 let decls = parse(r#"
1654env Local {
1655 datasource {
1656 url: "jdbc:postgresql://localhost:5432/yourdb"
1657 driver: "org.postgresql.Driver"
1658 username: "youruser"
1659 password: "yourpassword"
1660 }
1661}"#).or_else(|e| {
1662 println!("{}", e);
1663 Err(e)
1664 }).unwrap();
1665
1666 assert_eq!(decls[0], FklDeclaration::Env(EnvDecl {
1667 name: "Local".to_string(),
1668 inline_doc: "".to_string(),
1669 datasource: Some(DatasourceDecl {
1670 url: "jdbc:postgresql://localhost:5432/yourdb".to_string(),
1671 host: "".to_string(),
1672 port: "".to_string(),
1673 driver: "org.postgresql.Driver".to_string(),
1674 username: "youruser".to_string(),
1675 password: "yourpassword".to_string(),
1676 database: "".to_string(),
1677 }),
1678 message_broker: None,
1679 server: None,
1680 customs: vec![],
1681 }));
1682 }
1683
1684 #[test]
1685 fn env_server() {
1686 let decls = parse(r#"
1687env Local {
1688 server {
1689 port: 8899
1690 }
1691}"#).unwrap();
1692
1693 assert_eq!(decls[0], FklDeclaration::Env(EnvDecl {
1694 name: "Local".to_string(),
1695 inline_doc: "".to_string(),
1696 datasource: None,
1697 message_broker: None,
1698 server: Some(ServerDecl {
1699 port: 8899,
1700 attributes: vec![],
1701 }),
1702 customs: vec![],
1703 }));
1704 }
1705
1706 #[test]
1707 fn custom_env() {
1708 let decls = parse(r#"
1709env Local {
1710 kafka {
1711 host: "localhost"
1712 port: 9092
1713 }
1714}"#).unwrap();
1715
1716 assert_eq!(decls[0], FklDeclaration::Env(EnvDecl {
1717 name: "Local".to_string(),
1718 inline_doc: "".to_string(),
1719 datasource: None,
1720 message_broker: None,
1721 server: None,
1722 customs: vec![
1723 CustomDecl {
1724 name: "kafka".to_string(),
1725 inline_doc: "".to_string(),
1726 attributes: vec![
1727 AttributeDefinition {
1728 key: "host".to_string(),
1729 value: vec!["localhost".to_string()],
1730 },
1731 AttributeDefinition {
1732 key: "port".to_string(),
1733 value: vec!["9092".to_string()],
1734 }],
1735 }
1736 ],
1737 }));
1738 }
1739
1740 #[test]
1741 fn syntax_sugar() {
1742 let decls = parse(r#"ContextMap architecture {
1743 Context analyze {
1744 Aggregate ArchSystem {
1745 Struct {
1746 id: String;
1747 name: String;
1748 }
1749
1750 Entity ArchComponent {
1751 Struct {
1752 name: String;
1753 type: ArchComponentType
1754 }
1755 }
1756 }
1757 }
1758}
1759"#).or_else(|e| {
1760 println!("{}", e);
1761 Err(e)
1762 }).unwrap();
1763
1764 assert_eq!(decls[0], FklDeclaration::ContextMap(ContextMapDecl {
1765 name: Identifier {
1766 name: "architecture".to_string(),
1767 loc: Loc(11, 23),
1768 },
1769 contexts: vec![
1770 BoundedContextDecl {
1771 name: "analyze".to_string(),
1772 aggregates: vec![
1773 AggregateDecl {
1774 name: "ArchSystem".to_string(),
1775 inline_doc: "".to_string(),
1776 used_domain_objects: vec![],
1777 entities: vec![
1778 EntityDecl {
1779 name: "ArchSystem".to_string(),
1780 is_aggregate_root: false,
1781 identify: Default::default(),
1782 inline_doc: "".to_string(),
1783 fields: vec![
1784 VariableDefinition { name: "id".to_string(), type_type: "String".to_string(), initializer: None },
1785 VariableDefinition { name: "name".to_string(), type_type: "String".to_string(), initializer: None },
1786 ],
1787 value_objects: vec![],
1788 },
1789 EntityDecl {
1790 name: "ArchComponent".to_string(),
1791 is_aggregate_root: false,
1792 identify: Default::default(),
1793 inline_doc: "".to_string(),
1794 fields: vec![
1795 VariableDefinition {
1796 name: "name".to_string(),
1797 type_type: "String".to_string(),
1798 initializer: None,
1799 },
1800 VariableDefinition {
1801 name: "type".to_string(),
1802 type_type: "ArchComponentType".to_string(),
1803 initializer: None,
1804 },
1805 ],
1806 value_objects: vec![],
1807 },
1808 ],
1809 value_objects: vec![],
1810 domain_events: vec![],
1811 },
1812 ],
1813 domain_events: vec![],
1814 used_domain_objects: vec![],
1815 },
1816 ],
1817 relations: vec![],
1818 }));
1819 }
1820
1821 #[test]
1822 fn include_other_file() {
1823 let _decls = parse(r#"include "./layer.rs""#).or_else(|e| {
1824 println!("{}", e);
1825 Err(e)
1826 }).unwrap();
1827 }
1828}