fkl_parser/parser/
parser.rs

1// use core::panicking::panic;
2use 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  // sort context map by name
172  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}