routee_compass/plugin/output/default/traversal/
plugin.rs1use super::json_extensions::TraversalJsonField;
2use super::traversal_output_format::TraversalOutputFormat;
3use crate::app::compass::CompassAppError;
4use crate::app::search::SearchAppResult;
5use crate::plugin::output::output_plugin::OutputPlugin;
6use crate::plugin::output::OutputPluginError;
7use routee_compass_core::algorithm::search::EdgeTraversal;
8use routee_compass_core::algorithm::search::SearchInstance;
9use routee_compass_core::model::cost::TraversalCost;
10use serde_json::json;
11
12pub struct TraversalPlugin {
13 route: Option<TraversalOutputFormat>,
14 tree: Option<TraversalOutputFormat>,
15 route_key: String,
16 tree_key: String,
17}
18
19impl TraversalPlugin {
20 pub fn new(
21 route: Option<TraversalOutputFormat>,
22 tree: Option<TraversalOutputFormat>,
23 ) -> Result<TraversalPlugin, OutputPluginError> {
24 let route_key = TraversalJsonField::RouteOutput.to_string();
25 let tree_key = TraversalJsonField::TreeOutput.to_string();
26 Ok(TraversalPlugin {
27 route,
28 tree,
29 route_key,
30 tree_key,
31 })
32 }
33}
34
35impl OutputPlugin for TraversalPlugin {
36 fn process(
37 &self,
38 output: &mut serde_json::Value,
39 search_result: &Result<(SearchAppResult, SearchInstance), CompassAppError>,
40 ) -> Result<(), OutputPluginError> {
41 let (result, si) = match search_result {
42 Err(_) => return Ok(()),
43 Ok((result, si)) => (result, si),
44 };
45
46 if let Some(route_args) = self.route {
48 let routes_serialized = result
49 .routes
50 .iter()
51 .map(|route| {
52 construct_route_output(route, si, &route_args)
54 })
55 .collect::<Result<Vec<_>, _>>()
56 .map_err(OutputPluginError::OutputPluginFailed)?;
57
58 let routes_json = match routes_serialized.as_slice() {
62 [] => serde_json::Value::Null,
63 [route] => route.to_owned(),
64 _ => json![routes_serialized],
65 };
66 output[&self.route_key] = routes_json;
67 }
68
69 if let Some(tree_args) = self.tree {
71 let trees_serialized = result
72 .trees
73 .iter()
74 .map(|tree| {
75 tree_args.generate_tree_output(
77 tree,
78 si.map_model.clone(),
79 si.state_model.clone(),
80 )
81 })
82 .collect::<Result<Vec<_>, _>>()?;
83 let trees_json = match trees_serialized.as_slice() {
84 [] => serde_json::Value::Null,
85 [tree] => tree.to_owned(),
86 _ => json![trees_serialized],
87 };
88 output[&self.tree_key] = json![trees_json];
89 }
90
91 Ok(())
92 }
93}
94
95fn construct_route_output(
97 route: &Vec<EdgeTraversal>,
98 si: &SearchInstance,
99 output_format: &TraversalOutputFormat,
100) -> Result<serde_json::Value, String> {
101 let last_edge = route
102 .last()
103 .ok_or_else(|| String::from("cannot find result route state when route is empty"))?;
104 let path_json = output_format
105 .generate_route_output(route, si.map_model.clone(), si.state_model.clone())
106 .map_err(|e| e.to_string())?;
107 let traversal_summary = si
108 .state_model
109 .serialize_state(&last_edge.result_state, true)
110 .map_err(|e| format!("failed serializing final trip state: {e}"))?;
111
112 log::debug!("state model: {:?}", si.state_model);
113 log::debug!("traversal summary: {traversal_summary:?}");
114 log::debug!("result state: {:?}", last_edge.result_state);
115
116 let state_model = si.state_model.serialize_state_model();
117
118 let route_cost = route
120 .iter()
121 .fold(TraversalCost::default(), |mut acc, edge| {
122 acc.total_cost += edge.cost.total_cost;
123 acc.objective_cost += edge.cost.objective_cost;
124 for (name, cost) in &edge.cost.cost_component {
125 acc.cost_component
126 .entry(name.clone())
127 .and_modify(|v| *v += *cost)
128 .or_insert(*cost);
129 }
130 acc
131 });
132
133 let cost = json![route_cost];
134 let cost_model = si
135 .cost_model
136 .serialize_cost_info()
137 .map_err(|e| e.to_string())?;
138 let result = serde_json::json![{
139 "traversal_summary": traversal_summary,
140 "state_model": state_model,
141 "cost_model": cost_model,
142 "cost": cost,
143 "path": path_json
144 }];
145 Ok(result)
146}