routee_compass/plugin/output/default/traversal/
plugin.rs

1use 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        // output route if configured
47        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, &self.geoms)
53                    construct_route_output(route, si, &route_args)
54                })
55                .collect::<Result<Vec<_>, _>>()
56                .map_err(OutputPluginError::OutputPluginFailed)?;
57
58            // vary the type of value stored at the route key. if there is
59            // no route, store 'null'. if one, store an output object. if
60            // more, store an array of objects.
61            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        // output tree(s) if configured
70        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(tree, &self.geoms)
76                    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
95/// creates the JSON output for a route.
96fn 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    // Compute total route cost by summing all edge costs
119    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}