use std::fmt::Display;
use serde::Deserialize;
use serde::Serialize;
use crate::mir::{BoundedContext, ConnectionDirection, ContextRelation, LayeredArchitecture, SourceSets, Step};
use crate::mir::environment::Environment;
use crate::mir::implementation::Implementation;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct ContextMap {
pub name: String,
pub state: ContextState,
pub contexts: Vec<BoundedContext>,
pub relations: Vec<ContextRelation>,
pub implementations: Vec<Implementation>,
pub layered: Option<LayeredArchitecture>,
pub source_sets: Option<SourceSets>,
pub envs: Vec<Environment>
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum ContextState {
AsIs,
ToBe,
}
impl Default for ContextState {
fn default() -> Self {
ContextState::ToBe
}
}
impl Display for ContextMap {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "ContextMap({})", self.name)?;
for relation in &self.relations {
let rel = match relation.connection_type {
ConnectionDirection::Undirected => "-",
ConnectionDirection::PositiveDirected => "->",
ConnectionDirection::NegativeDirected => "<-",
ConnectionDirection::BiDirected => "<->",
};
writeln!(f, " Relation({} {} {}) ", relation.source, rel, relation.target)?;
}
for context in &self.contexts {
writeln!(f, " BoundedContext({})", context.name)?;
for aggregate in &context.aggregates {
writeln!(f, " Aggregate({})", aggregate.name)?;
for entity in &aggregate.entities {
writeln!(f, " Entity({})", entity.name)?;
entity.fields.iter().for_each(|field| {
writeln!(f, " Field({})", field.name).unwrap();
});
}
}
}
for imp in &self.implementations {
match imp {
Implementation::PublishHttpApi(api) => {
writeln!(f, " PublishHttpApi({})", api.name)?;
writeln!(f, " {:?} Path({})", api.endpoint.method, api.endpoint.path)?;
if let Some(request) = &api.endpoint.request {
writeln!(f, " Request: {}", request.name)?;
}
if let Some(response) = &api.endpoint.response {
writeln!(f, " Response: {}", response.name)?;
}
api.flow.iter().for_each(|flow| {
writeln!(f, " Flow").unwrap();
flow.steps.iter().for_each(|step| {
match step {
Step::MethodCall(call) => {
writeln!(f, " MethodCall({})", call.name).unwrap();
}
Step::Message(msg) => {
writeln!(f, " Message({})", msg.from).unwrap();
}
Step::RpcCall(_) => {}
}
});
});
}
Implementation::PublishEvent => {}
Implementation::PublishMessage => {}
}
}
self.layered.as_ref().map(|layered| {
writeln!(f, " LayeredArchitecture({})", layered.name).unwrap();
for layer in &layered.layers {
writeln!(f, " Layer {} (\"{}\")", layer.name, layer.package).unwrap();
}
});
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::ContextMap;
use crate::mir::{Aggregate, BoundedContext, Entity};
#[test]
fn display_context_map() {
let context_map = ContextMap {
name: "Ticket".to_string(),
state: Default::default(),
contexts: vec![BoundedContext {
name: "TicketContext".to_string(),
aggregates: vec![
Aggregate {
name: "TicketAggregate".to_string(),
description: "".to_string(),
entities: vec![Entity {
name: "TicketEntity".to_string(),
description: "".to_string(),
is_aggregate_root: false,
identify: Default::default(),
fields: vec![],
}],
}
],
}],
relations: vec![],
implementations: vec![],
layered: None,
source_sets: None,
envs: vec![]
};
let output = format!("{}", context_map);
assert_eq!(output, r#"ContextMap(Ticket)
BoundedContext(TicketContext)
Aggregate(TicketAggregate)
Entity(TicketEntity)
"#);
}
}