1use anyhow::Result;
23use lazy_static::lazy_static;
24use regex::Regex;
25use serde_json::Value;
26
27use map_model::{Map, OriginalRoad, PermanentMapEdits, RoadID};
28
29use super::Proposal;
30use crate::save::Partitioning;
31
32pub fn to_permanent(map: &Map, proposal: &Proposal) -> Result<Value> {
33 let mut proposal_value = serde_json::to_value(proposal.edits.to_permanent(map))?;
34
35 let mut partitioning_value = serde_json::to_value(&proposal.partitioning)?;
37 walk("", &mut partitioning_value, &|path, value| {
38 if is_road_id(path) {
39 let replace_with = map.get_r(RoadID(value.as_u64().unwrap() as usize)).orig_id;
40 *value = serde_json::to_value(&replace_with)?;
41 }
42 Ok(())
43 })?;
44
45 proposal_value
46 .as_object_mut()
47 .unwrap()
48 .insert("partitioning".to_string(), partitioning_value);
49 Ok(proposal_value)
50}
51
52pub fn from_permanent(map: &Map, mut proposal_value: Value) -> Result<Proposal> {
53 let mut partitioning_value = proposal_value
55 .as_object_mut()
56 .unwrap()
57 .remove("partitioning")
58 .unwrap();
59 walk("", &mut partitioning_value, &|path, value| {
60 if is_road_id(path) {
61 let orig_id: OriginalRoad = serde_json::from_value(value.clone())?;
62 let replace_with = map.find_r_by_osm_id(orig_id)?;
63 *value = serde_json::to_value(&replace_with)?;
64 }
65 Ok(())
66 })?;
67 let partitioning: Partitioning = serde_json::from_value(partitioning_value)?;
68
69 let perma_edits: PermanentMapEdits = serde_json::from_value(proposal_value)?;
72 let edits = perma_edits.into_edits_permissive(map);
73
74 Ok(Proposal {
75 edits,
76 partitioning,
77 })
78}
79
80fn is_road_id(path: &str) -> bool {
81 lazy_static! {
82 static ref PATTERNS: Vec<Regex> = vec![
83 Regex::new(r"^/partitioning/single_blocks/\d+/perimeter/interior/\d+$").unwrap(),
85 Regex::new(r"^/partitioning/single_blocks/\d+/perimeter/roads/\d+/road$").unwrap(),
86 Regex::new(r"^/partitioning/neighbourhoods/\d+/0/perimeter/interior/\d+$").unwrap(),
88 Regex::new(r"^/partitioning/neighbourhoods/\d+/0/perimeter/roads/\d+/road$").unwrap(),
89 ];
90 }
91
92 PATTERNS.iter().any(|re| re.is_match(path))
93}
94
95fn walk<F: Fn(&str, &mut Value) -> Result<()>>(
98 path: &str,
99 value: &mut Value,
100 transform: &F,
101) -> Result<()> {
102 match value {
103 Value::Array(list) => {
104 for (idx, x) in list.into_iter().enumerate() {
105 walk(&format!("{}/{}", path, idx), x, transform)?;
106 }
107 transform(path, value)?;
108 }
109 Value::Object(map) => {
110 for (key, val) in map {
111 walk(&format!("{}/{}", path, key), val, transform)?;
112 }
113 transform(path, value)?;
116 }
117 _ => {
118 transform(path, value)?;
119 }
121 }
122 Ok(())
123}