1use std::collections::BTreeMap;
4use std::fmt;
5
6use serde::de::{self, SeqAccess, Visitor};
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use svugy_model::builder::ModelBuilder;
9use svugy_model::legend::Legend;
10use svugy_model::model::{ByteRange, Edge, EdgeId, Element, Model, Traveler};
11use svugy_model::path::ElementPath;
12use svugy_model::unique_key_map::UniqueKeyMapVisitor;
13use svugy_model::validate::UncheckedModel;
14
15use crate::diagnostic::{Import, NoWarning, SourceError, SourceLanguage};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct JsonLanguage;
20
21pub type JsonError = SourceError<JsonLanguage>;
23
24pub type JsonImport = Import<JsonLanguage>;
26
27pub struct JsonPrinter;
29
30#[derive(Serialize, Deserialize)]
32#[serde(deny_unknown_fields)]
33struct ModelDocument {
34 #[serde(default, skip_serializing_if = "Option::is_none")]
35 title: Option<String>,
36 #[serde(default, skip_serializing_if = "Option::is_none")]
37 subtitle: Option<String>,
38 #[serde(default, skip_serializing_if = "Option::is_none")]
39 description: Option<String>,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
41 legend: Option<Legend>,
42 #[serde(default, deserialize_with = "UniqueKeyMapVisitor::deserialize")]
43 elements: BTreeMap<ElementPath, Element>,
44 #[serde(
45 default,
46 serialize_with = "EdgeRecord::serialize_all",
47 deserialize_with = "UniqueEdgeListVisitor::deserialize"
48 )]
49 edges: BTreeMap<EdgeId, Edge>,
50}
51
52#[derive(Serialize, Deserialize)]
54#[serde(deny_unknown_fields)]
55struct EdgeRecord {
56 from: ElementPath,
57 to: ElementPath,
58 #[serde(default)]
59 ordinal: u32,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
61 label: Option<String>,
62 #[serde(default, skip_serializing_if = "Option::is_none")]
63 description: Option<String>,
64 #[serde(
65 default,
66 skip_serializing_if = "BTreeMap::is_empty",
67 deserialize_with = "UniqueKeyMapVisitor::deserialize"
68 )]
69 style: BTreeMap<String, String>,
70 #[serde(default, skip_serializing_if = "Option::is_none")]
71 traveler: Option<Traveler>,
72 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
73 badge: bool,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
75 span: Option<ByteRange>,
76}
77
78struct UniqueEdgeListVisitor;
80
81impl SourceLanguage for JsonLanguage {
82 const ERROR_CODE: &'static str = "svugy::malformed_json";
83 type Warning = NoWarning;
84
85 fn import(source_text: &str) -> JsonImport {
86 let read_document = ModelDocument::read(source_text);
87 let document = match read_document {
88 Ok(document) => document,
89 Err(serde_error) => return Self::reject_malformed(source_text, &serde_error),
90 };
91 let unchecked_model = document.into_unchecked_model();
92 let admission = unchecked_model.admit();
93 JsonImport {
94 admission,
95 errors: Vec::new(),
96 warnings: Vec::new(),
97 }
98 }
99}
100
101impl JsonLanguage {
102 fn reject_malformed(json: &str, serde_error: &serde_json::Error) -> JsonImport {
104 let empty_builder = ModelBuilder::new();
105 let empty_admission = empty_builder.build();
106 let line = serde_error.line();
107 let column = serde_error.column();
108 let offset = Self::byte_offset_at(json, line, column);
109 let message = serde_error.to_string();
110 let malformed_json_error = JsonError::new(message, offset..offset);
111 let errors = vec![malformed_json_error];
112 JsonImport {
113 admission: empty_admission,
114 errors,
115 warnings: Vec::new(),
116 }
117 }
118
119 fn byte_offset_at(json: &str, line: usize, column: usize) -> usize {
121 let preceding_line_count = line.saturating_sub(1);
122 let preceding_lines = json.split_inclusive('\n').take(preceding_line_count);
123 let line_start: usize = preceding_lines.map(str::len).sum();
124 let column_offset = column.saturating_sub(1);
125 let offset = line_start + column_offset;
126 offset.min(json.len())
127 }
128}
129
130impl JsonPrinter {
131 pub fn print(model: &Model) -> String {
133 let document = ModelDocument::of(model);
134 serde_json::to_string_pretty(&document)
135 .expect("a model serializes: every map key is a string")
136 }
137}
138
139impl ModelDocument {
140 fn of(model: &Model) -> Self {
142 let title = model.title().map(str::to_owned);
143 let subtitle = model.subtitle().map(str::to_owned);
144 let description = model.description().map(str::to_owned);
145 let legend = model.legend().cloned();
146 let elements = model.elements().clone();
147 let edges = model.edges().clone();
148 Self {
149 title,
150 subtitle,
151 description,
152 legend,
153 elements,
154 edges,
155 }
156 }
157
158 fn read(json: &str) -> Result<Self, serde_json::Error> {
160 let mut deserializer = serde_json::Deserializer::from_str(json);
161 let document = Self::deserialize(&mut deserializer)?;
162 deserializer.end()?;
163 Ok(document)
164 }
165
166 fn into_unchecked_model(self) -> UncheckedModel {
168 let Self {
169 title,
170 subtitle,
171 description,
172 legend,
173 elements,
174 edges,
175 } = self;
176 let unchecked_model = UncheckedModel::from_parts(title, subtitle, legend, elements, edges);
177 match description {
178 Some(description) => unchecked_model.with_description(description),
179 None => unchecked_model,
180 }
181 }
182}
183
184impl EdgeRecord {
185 fn serialize_all<S: Serializer>(
187 edges: &BTreeMap<EdgeId, Edge>,
188 serializer: S,
189 ) -> Result<S::Ok, S::Error> {
190 let records = edges
191 .iter()
192 .map(|(id, edge)| EdgeRecord::from_parts(id, edge));
193 serializer.collect_seq(records)
194 }
195
196 fn from_parts(id: &EdgeId, edge: &Edge) -> Self {
198 let from = id.from().clone();
199 let to = id.to().clone();
200 let ordinal = id.ordinal();
201 let label = edge.label().map(str::to_owned);
202 let description = edge.description().map(str::to_owned);
203 let style = edge.style().clone();
204 let traveler = edge.traveler();
205 let badge = edge.is_badge();
206 let span = edge.span();
207 Self {
208 from,
209 to,
210 ordinal,
211 label,
212 description,
213 style,
214 traveler,
215 badge,
216 span,
217 }
218 }
219
220 fn into_parts(self) -> (EdgeId, Edge) {
222 let id = EdgeId::new(self.from, self.to, self.ordinal);
223 let mut edge = Edge::new();
224 if let Some(label) = self.label {
225 edge = edge.with_label(label);
226 }
227 if let Some(description) = self.description {
228 edge = edge.with_description(description);
229 }
230 for (key, value) in self.style {
231 edge = edge.with_style(key, value);
232 }
233 if let Some(traveler) = self.traveler {
234 edge = edge.with_traveler(traveler);
235 }
236 if self.badge {
237 edge = edge.with_badge();
238 }
239 if let Some(span) = self.span {
240 edge = edge.with_span(span);
241 }
242 (id, edge)
243 }
244}
245
246impl UniqueEdgeListVisitor {
247 fn deserialize<'de, D: Deserializer<'de>>(
249 deserializer: D,
250 ) -> Result<BTreeMap<EdgeId, Edge>, D::Error> {
251 deserializer.deserialize_seq(Self)
252 }
253}
254
255impl<'de> Visitor<'de> for UniqueEdgeListVisitor {
256 type Value = BTreeMap<EdgeId, Edge>;
257
258 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
259 formatter.write_str("a list of edges with unique (from, to, ordinal)")
260 }
261
262 fn visit_seq<A: SeqAccess<'de>>(self, mut records: A) -> Result<Self::Value, A::Error> {
263 let mut edges = BTreeMap::new();
264 while let Some(record) = records.next_element::<EdgeRecord>()? {
265 let (id, edge) = record.into_parts();
266 let is_duplicate = edges.contains_key(&id);
267 if is_duplicate {
268 let duplicate_edge_message = format!("duplicate edge `{id}`");
269 let duplicate_edge_error = de::Error::custom(duplicate_edge_message);
270 return Err(duplicate_edge_error);
271 }
272 edges.insert(id, edge);
273 }
274 Ok(edges)
275 }
276}