1use crate::engine::line_graph::{InputMode, LineGraph};
2use crate::{
3 Error, Layer, LayerId, Rule, RuleId, RuleSpec, Violation, ViolationKind, model::Parts, rules,
4};
5use rayon::prelude::*;
6use serde::Serialize;
7use std::{
8 collections::BTreeMap,
9 fmt,
10 sync::{Arc, Mutex, OnceLock},
11};
12type GraphSlot = Arc<OnceLock<Result<Arc<LineGraph>, Error>>>;
13
14#[derive(Debug, Clone, Serialize)]
16#[non_exhaustive]
17pub struct RuleReport {
18 pub spec: RuleSpec,
20 pub layer: String,
22 pub other: Option<String>,
24 pub counts: BTreeMap<ViolationKind, usize>,
26 #[serde(rename = "errors")]
29 pub violations: Vec<Violation>,
30}
31impl RuleReport {
32 #[must_use]
34 pub fn layer_name(&self, id: LayerId) -> Option<&str> {
35 if id == self.spec.layer {
36 Some(&self.layer)
37 } else if Some(id) == self.spec.other {
38 self.other.as_deref()
39 } else {
40 None
41 }
42 }
43}
44#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
46#[serde(tag = "kind", rename_all = "kebab-case")]
47#[non_exhaustive]
48pub enum Warning {
49 DegenerateParts {
51 layer: LayerId,
53 name: String,
55 skipped: usize,
57 },
58}
59impl fmt::Display for Warning {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 match self {
62 Self::DegenerateParts { name, skipped, .. } => {
63 write!(
64 f,
65 "layer {name}: skipped {skipped} degenerate parts or rings"
66 )
67 }
68 }
69 }
70}
71#[derive(Debug, Clone, Serialize)]
73#[non_exhaustive]
74pub struct Report {
75 pub tolerance: f64,
77 pub warnings: Vec<Warning>,
79 pub rules: Vec<RuleReport>,
82}
83impl Report {
84 #[must_use]
86 pub fn is_valid(&self) -> bool {
87 self.rules.iter().all(|r| r.violations.is_empty())
88 }
89 #[must_use]
91 pub fn violation_count(&self) -> usize {
92 self.rules.iter().map(|r| r.violations.len()).sum()
93 }
94 pub fn violations(&self) -> impl Iterator<Item = &Violation> {
96 self.rules.iter().flat_map(|r| &r.violations)
97 }
98 #[must_use]
101 pub fn rule(&self, id: RuleId) -> Option<&RuleReport> {
102 self.rules.get(id.0)
103 }
104}
105#[derive(Debug)]
107pub enum LayerInput {
108 Existing(LayerId),
110 New(Box<Layer>),
112}
113
114#[derive(Debug)]
116pub struct Topology {
117 tolerance: f64,
118 layers: Vec<Arc<Layer>>,
119 specs: Vec<RuleSpec>,
120 graphs: Mutex<BTreeMap<Vec<(LayerId, InputMode)>, GraphSlot>>,
121}
122impl Topology {
123 pub fn new(tolerance: f64) -> Result<Self, Error> {
129 if !tolerance.is_finite() || tolerance < 0. {
130 return Err(Error::Tolerance(tolerance));
131 }
132 Ok(Self {
133 tolerance,
134 layers: Vec::new(),
135 specs: Vec::new(),
136 graphs: Mutex::new(BTreeMap::new()),
137 })
138 }
139 #[must_use]
141 pub fn tolerance(&self) -> f64 {
142 self.tolerance
143 }
144 pub fn add_layer(&mut self, layer: impl Into<Arc<Layer>>) -> LayerId {
147 let id = LayerId(self.layers.len());
148 self.layers.push(layer.into());
149 id
150 }
151 pub fn layer(&self, id: LayerId) -> Result<&Layer, Error> {
156 self.layers
157 .get(id.0)
158 .map(AsRef::as_ref)
159 .ok_or(Error::UnknownLayer(id))
160 }
161 pub fn layers(&self) -> impl Iterator<Item = (LayerId, &Layer)> {
163 self.layers
164 .iter()
165 .enumerate()
166 .map(|(i, l)| (LayerId(i), l.as_ref()))
167 }
168 pub fn rules(&self) -> impl ExactSizeIterator<Item = RuleSpec> {
170 self.specs.iter().copied()
171 }
172 pub fn clear_rules(&mut self) {
176 self.specs.clear();
177 }
178 pub(crate) fn parts(&self, id: LayerId) -> Result<&Parts, Error> {
179 let layer = self.layer(id)?;
180 layer.parts().map_err(|e| e.at(id, layer.name()))
181 }
182 pub(crate) fn graph(&self, layers: Vec<LayerId>) -> Result<Arc<LineGraph>, Error> {
183 self.graph_inputs(
184 layers
185 .into_iter()
186 .map(|id| (id, InputMode::Linework))
187 .collect(),
188 )
189 }
190 pub(crate) fn graph_inputs(
191 &self,
192 mut layers: Vec<(LayerId, InputMode)>,
193 ) -> Result<Arc<LineGraph>, Error> {
194 layers.sort_unstable();
195 layers.dedup();
196 let slot = Arc::clone(
199 self.graphs
200 .lock()
201 .unwrap_or_else(std::sync::PoisonError::into_inner)
202 .entry(layers.clone())
203 .or_default(),
204 );
205 slot.get_or_init(|| LineGraph::build(self, &layers).map(Arc::new))
206 .as_ref()
207 .map(Arc::clone)
208 .map_err(Clone::clone)
209 }
210 pub fn add_rule(
219 &mut self,
220 rule: Rule,
221 layer: LayerId,
222 other: Option<LayerId>,
223 ) -> Result<RuleSpec, Error> {
224 self.register_rule(
225 rule,
226 LayerInput::Existing(layer),
227 other.map(LayerInput::Existing),
228 )
229 }
230 pub fn register_rule(
238 &mut self,
239 rule: Rule,
240 layer: LayerInput,
241 other: Option<LayerInput>,
242 ) -> Result<RuleSpec, Error> {
243 let (primary_kind, other_kind) = rule.inputs();
244 rule.check_input_count(other.is_some())?;
245 if let (LayerInput::Existing(a), Some(LayerInput::Existing(b))) = (&layer, &other)
246 && a == b
247 {
248 return Err(Error::SameLayer { rule, layer: *a });
249 }
250 let mut next = self.layers.len();
251 let mut check = |input: &LayerInput, kind| -> Result<LayerId, Error> {
252 let (id, value) = match input {
253 LayerInput::Existing(id) => (*id, self.layer(*id)?),
254 LayerInput::New(value) => {
255 let id = LayerId(next);
256 next += 1;
257 (id, value.as_ref())
258 }
259 };
260 value.check_kind(id, kind)?;
261 value.parts().map_err(|e| e.at(id, value.name()))?;
262 Ok(id)
263 };
264 let primary_id = check(&layer, primary_kind)?;
265 let other_id = other
266 .as_ref()
267 .zip(other_kind)
268 .map(|(input, kind)| check(input, kind))
269 .transpose()?;
270 for input in [Some(layer), other].into_iter().flatten() {
271 if let LayerInput::New(value) = input {
272 self.layers.push(Arc::from(value));
273 }
274 }
275 let spec = RuleSpec {
276 id: RuleId(self.specs.len()),
277 rule,
278 layer: primary_id,
279 other: other_id,
280 };
281 self.specs.push(spec);
282 Ok(spec)
283 }
284 pub fn warnings(&self) -> Result<Vec<Warning>, Error> {
290 let mut warnings = Vec::new();
291 for (id, layer) in self.layers() {
292 let skipped = self.parts(id)?.skipped;
293 if skipped > 0 {
294 warnings.push(Warning::DegenerateParts {
295 layer: id,
296 name: layer.name().into(),
297 skipped,
298 });
299 }
300 }
301 Ok(warnings)
302 }
303 pub fn validate(&self) -> Result<Report, Error> {
310 let warnings = self.warnings()?;
311 let rules = self
312 .specs
313 .par_iter()
314 .map(|&spec| {
315 let violations = rules::run(self, spec)?;
316 let mut counts = BTreeMap::new();
317 for v in &violations {
318 *counts.entry(v.kind).or_default() += 1;
319 }
320 Ok(RuleReport {
321 spec,
322 layer: self.layer(spec.layer)?.name().into(),
323 other: spec
324 .other
325 .map(|id| self.layer(id).map(|l| l.name().to_string()))
326 .transpose()?,
327 counts,
328 violations,
329 })
330 })
331 .collect::<Result<Vec<_>, Error>>()?;
332 Ok(Report {
333 tolerance: self.tolerance,
334 warnings,
335 rules,
336 })
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use geo::{LineString, Point};
344
345 #[test]
346 fn transactions_reject_invalid_layers_without_consuming_ids_or_rules() {
347 for bad in [
348 Layer::from_features("duplicates", vec![(4, Point::new(0., 0.).into()); 2]),
349 Layer::from_geometries("nonfinite", [Point::new(f64::INFINITY, 0.).into()]),
350 Layer::from_geometries(
351 "wrong kind",
352 [LineString::from(vec![(0., 0.), (1., 0.)]).into()],
353 ),
354 ] {
355 let mut topology = Topology::new(0.).unwrap();
356 let original = topology.add_layer(Layer::from_geometries("original", []));
357 let new = LayerInput::New(Box::new(Layer::from_geometries(
358 "pending",
359 [Point::new(0., 0.).into()],
360 )));
361 assert!(
362 topology
363 .register_rule(
364 Rule::PointMustNotOverlapWith,
365 new,
366 Some(LayerInput::New(Box::new(bad))),
367 )
368 .is_err()
369 );
370 assert_eq!(topology.layers().count(), 1);
371 assert!(topology.validate().unwrap().rules.is_empty());
372 assert!(topology.warnings().unwrap().is_empty());
373 let spec = topology
374 .register_rule(
375 Rule::PointMustNotOverlapWith,
376 LayerInput::Existing(original),
377 Some(LayerInput::New(Box::new(Layer::from_geometries(
378 "next",
379 [],
380 )))),
381 )
382 .unwrap();
383 assert_eq!(spec.other.unwrap().index(), 1);
384 }
385 }
386
387 #[test]
388 fn transaction_caches_keep_final_ids_for_each_input_order() {
389 for reuse_primary in [false, true] {
390 let mut topology = Topology::new(0.).unwrap();
391 topology.add_layer(Layer::from_geometries("unused", []));
392 let existing = topology.add_layer(Layer::from_geometries(
393 "existing",
394 [Point::new(0., 0.).into()],
395 ));
396 let fresh = LayerInput::New(Box::new(Layer::from_geometries(
397 "fresh",
398 [Point::new(0., 0.).into()],
399 )));
400 let (primary, other) = if reuse_primary {
401 (LayerInput::Existing(existing), fresh)
402 } else {
403 (fresh, LayerInput::Existing(existing))
404 };
405 let spec = topology
406 .register_rule(Rule::PointMustNotOverlapWith, primary, Some(other))
407 .unwrap();
408 let report = topology.validate().unwrap();
409 let error = &report.rules[0].violations[0];
410 assert_eq!(error.origin.unwrap().layer, spec.layer);
411 assert_eq!(error.destination.unwrap().layer, spec.other.unwrap());
412 assert_eq!(topology.layers().count(), 3);
413 }
414 }
415}