axioval_engine/
triangle_count.rs1use std::sync::Arc;
17
18use axioval_ir::{Evidence, ObjectId};
19use thiserror::Error;
20
21#[derive(Clone, Debug, Error, PartialEq, Eq)]
23pub enum TriangleCountError {
24 #[error("no geometry for `{0}`")]
26 UnknownObject(ObjectId),
27 #[error("triangle count unavailable: {0}")]
30 Unavailable(String),
31 #[error("triangle count is invalid")]
33 InvalidMeasurement,
34}
35
36#[derive(Clone, Debug, PartialEq, Eq)]
38pub struct TriangleCount {
39 object: ObjectId,
40 triangles: u64,
41 evidence: Evidence,
42}
43
44impl TriangleCount {
45 pub fn try_new(
49 object: ObjectId,
50 triangles: u64,
51 evidence: Evidence,
52 ) -> Result<Self, TriangleCountError> {
53 if evidence.locator.trim().is_empty() {
54 return Err(TriangleCountError::InvalidMeasurement);
55 }
56 Ok(Self {
57 object,
58 triangles,
59 evidence,
60 })
61 }
62
63 #[must_use]
65 pub fn object(&self) -> &ObjectId {
66 &self.object
67 }
68
69 #[must_use]
72 pub fn triangles(&self) -> u64 {
73 self.triangles
74 }
75
76 #[must_use]
79 pub fn is_exact(&self) -> bool {
80 self.evidence.exact
81 }
82
83 #[must_use]
85 pub fn evidence(&self) -> &Evidence {
86 &self.evidence
87 }
88}
89
90pub trait TriangleCountService: Send + Sync + 'static {
92 fn count_triangles(&self, object: &ObjectId) -> Result<TriangleCount, TriangleCountError>;
94}
95
96#[derive(Clone)]
98pub struct TriangleCountServiceHandle(Arc<dyn TriangleCountService>);
99
100impl TriangleCountServiceHandle {
101 #[must_use]
103 pub fn new(service: Arc<dyn TriangleCountService>) -> Self {
104 Self(service)
105 }
106
107 pub fn count_triangles(&self, object: &ObjectId) -> Result<TriangleCount, TriangleCountError> {
110 let count = self.0.count_triangles(object)?;
111 if count.object() != object {
112 return Err(TriangleCountError::InvalidMeasurement);
113 }
114 Ok(count)
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use axioval_ir::SourceId;
122
123 fn id(local: &str) -> ObjectId {
124 ObjectId::new(SourceId::new("cad", "m").unwrap(), local).unwrap()
125 }
126
127 fn evidence() -> Evidence {
128 Evidence::exact(SourceId::new("cad", "m").unwrap(), "triangle-count:a")
129 }
130
131 #[test]
132 fn a_count_needs_a_locator() {
133 assert!(TriangleCount::try_new(id("a"), 12, evidence()).is_ok());
134 let unlocated = Evidence::exact(SourceId::new("cad", "m").unwrap(), " ");
135 assert_eq!(
136 TriangleCount::try_new(id("a"), 12, unlocated),
137 Err(TriangleCountError::InvalidMeasurement)
138 );
139 }
140
141 struct Other;
142 impl TriangleCountService for Other {
143 fn count_triangles(&self, _: &ObjectId) -> Result<TriangleCount, TriangleCountError> {
144 TriangleCount::try_new(id("b"), 12, evidence())
145 }
146 }
147
148 #[test]
149 fn a_count_of_another_object_is_refused() {
150 let handle = TriangleCountServiceHandle::new(Arc::new(Other));
151 assert_eq!(
152 handle.count_triangles(&id("a")),
153 Err(TriangleCountError::InvalidMeasurement)
154 );
155 assert_eq!(handle.count_triangles(&id("b")).unwrap().triangles(), 12);
156 }
157}