1use std::marker::PhantomData;
14
15use geop_core_math::geop_error::{GeopError, GeopResult};
16use serde::{Deserialize, Deserializer, Serialize, Serializer};
17
18use crate::{placed::Located, target::TargetReference};
19
20pub trait Kind: 'static {
24 const NAME: &'static str;
26}
27
28pub trait HasElements<K: Kind>: 'static {
34 type Element<'a>;
36
37 const SINGLETON: Option<&'static str> = None;
41
42 fn element(&self, name: &str) -> GeopResult<Self::Element<'_>>;
44
45 fn undefined() -> Option<&'static Self> {
49 None
50 }
51
52 fn names_target(name: &str) -> bool {
57 Self::SINGLETON.is_none_or(|singleton| singleton == name)
58 }
59}
60
61pub struct ElementReference<T, K> {
68 target: TargetReference<T>,
69 name: String,
70 kind: PhantomData<fn() -> K>,
71}
72
73impl<T, K> Clone for ElementReference<T, K> {
76 fn clone(&self) -> Self {
77 Self::new(self.target.clone(), self.name.clone())
78 }
79}
80
81impl<T, K> PartialEq for ElementReference<T, K> {
82 fn eq(&self, other: &Self) -> bool {
83 self.target == other.target && self.name == other.name
84 }
85}
86
87impl<T, K> Eq for ElementReference<T, K> {}
88
89impl<T, K: Kind> std::fmt::Debug for ElementReference<T, K> {
90 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
91 write!(
92 f,
93 "{}({:?}, {:?})",
94 K::NAME,
95 self.target.location(),
96 self.name
97 )
98 }
99}
100
101impl<T, K> ElementReference<T, K> {
102 pub fn new(target: TargetReference<T>, name: impl Into<String>) -> Self {
103 Self {
104 target,
105 name: name.into(),
106 kind: PhantomData,
107 }
108 }
109
110 pub fn target(&self) -> &TargetReference<T> {
112 &self.target
113 }
114
115 pub fn name(&self) -> &str {
117 &self.name
118 }
119
120 pub fn instance(&self) -> &str {
123 self.target.instance()
124 }
125
126 pub fn local(&self) -> Self {
129 Self::new(self.target.local(), self.name.clone())
130 }
131
132 pub fn behind(&self, instance: &str) -> Self {
136 Self::new(self.target.behind(instance), self.name.clone())
137 }
138}
139
140impl<T: HasElements<K>, K: Kind> ElementReference<T, K> {
141 pub fn whole(target: TargetReference<T>) -> Self {
145 Self::new(target, "")
146 }
147
148 pub fn in_singleton(name: impl Into<String>) -> GeopResult<Self> {
150 let target = T::SINGLETON.ok_or_else(|| {
151 GeopError::new(format!(
152 "a `{}` is no part's only target of its type: name the target of {}",
153 std::any::type_name::<T>(),
154 K::NAME
155 ))
156 })?;
157 Ok(Self::new(TargetReference::new(target), name))
158 }
159
160 pub fn from_picked(picked: &PickedElement) -> Option<Self> {
164 (picked.kind == K::NAME && T::names_target(picked.target.name())).then(|| {
165 Self::new(
166 TargetReference::located(picked.target.clone()),
167 &picked.name,
168 )
169 })
170 }
171
172 pub fn picked(&self) -> PickedElement {
174 PickedElement {
175 target: self.target.location().clone(),
176 kind: K::NAME.to_string(),
177 name: self.name.clone(),
178 }
179 }
180}
181
182pub fn whole<'a, T>(target: &'a T, name: &str) -> GeopResult<&'a T> {
185 match name {
186 "" => Ok(target),
187 _ => Err(GeopError::new(format!(
188 "`{}` is read whole, not by `{name}`",
189 std::any::type_name::<T>()
190 ))),
191 }
192}
193
194#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
198pub struct PickedElement {
199 pub target: Located,
202 pub kind: String,
204 pub name: String,
206}
207
208impl PickedElement {
209 pub fn label(&self) -> String {
211 format!("{} {}", self.kind, self.name)
212 }
213
214 pub fn instance(&self) -> &str {
216 self.target.path()
217 }
218
219 pub fn local(&self) -> Self {
221 Self {
222 target: self.target.local(),
223 ..self.clone()
224 }
225 }
226
227 pub fn behind(&self, instance: &str) -> Self {
229 Self {
230 target: self.target.behind(instance),
231 ..self.clone()
232 }
233 }
234}
235
236#[derive(Serialize, Deserialize)]
239#[serde(untagged)]
240enum Saved {
241 Name(String),
242 InTarget { target: Located, name: String },
243 Whole { target: Located },
244}
245
246impl<T: HasElements<K>, K: Kind> Serialize for ElementReference<T, K> {
247 fn serialize<Se: Serializer>(&self, serializer: Se) -> Result<Se::Ok, Se::Error> {
248 let target = self.target.location().clone();
249 let saved = if target.path().is_empty() && T::SINGLETON == Some(target.name()) {
250 Saved::Name(self.name.clone())
251 } else if self.name.is_empty() {
252 Saved::Whole { target }
253 } else {
254 Saved::InTarget {
255 target,
256 name: self.name.clone(),
257 }
258 };
259 saved.serialize(serializer)
260 }
261}
262
263impl<'de, T: HasElements<K>, K: Kind> Deserialize<'de> for ElementReference<T, K> {
264 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
265 match Saved::deserialize(deserializer)? {
266 Saved::Name(name) => Self::in_singleton(name).map_err(serde::de::Error::custom),
267 Saved::InTarget { target, name } => {
268 Ok(Self::new(TargetReference::located(target), name))
269 }
270 Saved::Whole { target } => Ok(Self::whole(TargetReference::located(target))),
271 }
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use std::collections::BTreeMap;
278
279 use super::*;
280 use crate::{
281 operation_registry::OperationRegistry, part::Part, runner::Runner, target::Target,
282 };
283
284 #[derive(Clone, PartialEq)]
286 struct Lengths(BTreeMap<String, f64>);
287
288 impl Target for Lengths {}
289
290 #[derive(Clone, PartialEq)]
292 struct Stock(Lengths);
293
294 struct Length;
295
296 impl Kind for Length {
297 const NAME: &'static str = "length";
298 }
299
300 impl HasElements<Length> for Lengths {
301 type Element<'a> = f64;
302
303 fn element(&self, name: &str) -> GeopResult<f64> {
304 (self.0.get(name).copied()).ok_or_else(|| GeopError::new(format!("no length {name}")))
305 }
306 }
307
308 impl HasElements<Length> for Stock {
309 type Element<'a> = f64;
310 const SINGLETON: Option<&'static str> = Some("stock");
311
312 fn element(&self, name: &str) -> GeopResult<f64> {
313 self.0.element(name)
314 }
315 }
316
317 fn lengths(entries: &[(&str, f64)]) -> Lengths {
318 Lengths(entries.iter().map(|&(n, v)| (n.to_string(), v)).collect())
319 }
320
321 #[test]
324 fn a_step_reading_an_element_is_rebuilt_when_its_target_changes() {
325 let runner = Runner::new(OperationRegistry::default());
326 let mut part = Part::default();
327 let total = TargetReference::<f64>::new("total");
328 let run = |part: &mut Part, a: f64| {
329 let defined = TargetReference::<Lengths>::new("s1");
330 (runner.run_step(part, "s1", |ctx| {
331 ctx.produce(&defined, &a, |_, &a| Ok(lengths(&[("a", a)])))
332 }))
333 .unwrap();
334 let a = ElementReference::<Lengths, Length>::new(defined, "a");
335 (runner.run_step(part, "s2", |ctx| {
336 ctx.produce(
337 &total,
338 &a,
339 |reader, a| Ok(2.0 * reader.retrieve_element(a)?),
340 )
341 }))
342 .unwrap();
343 };
344 run(&mut part, 1.0);
345 assert_eq!(part.current().target(&total).unwrap(), &2.0);
346 run(&mut part, 3.0);
347 assert_eq!(part.current().target(&total).unwrap(), &6.0);
348 }
349
350 #[test]
351 fn a_missing_element_says_which() {
352 let runner = Runner::new(OperationRegistry::default());
353 let mut part = Part::default();
354 let defined = TargetReference::<Lengths>::new("s1");
355 (runner.run_step(&mut part, "s1", |ctx| {
356 ctx.produce(&defined, &(), |_, _| Ok(lengths(&[("a", 1.0)])))
357 }))
358 .unwrap();
359 let b = ElementReference::<Lengths, Length>::new(defined, "b");
360 let error = part.current().retrieve_element(&b).unwrap_err().to_string();
361 assert!(error.contains("no length b"), "{error}");
362 assert!(error.contains("length(\"/:s1\", \"b\")"), "{error}");
363 }
364
365 #[test]
368 fn saved_forms() {
369 let in_stock = ElementReference::<Stock, Length>::in_singleton("a").unwrap();
370 let saved = serde_json::to_value(&in_stock).unwrap();
371 assert_eq!(saved, serde_json::json!("a"));
372 let read: ElementReference<Stock, Length> = serde_json::from_value(saved).unwrap();
373 assert_eq!(read, in_stock);
374
375 let in_s1 = ElementReference::<Lengths, Length>::new(TargetReference::new("s1"), "a");
376 let saved = serde_json::to_value(&in_s1).unwrap();
377 assert_eq!(saved, serde_json::json!({"target": "/:s1", "name": "a"}));
378 let read: ElementReference<Lengths, Length> = serde_json::from_value(saved).unwrap();
379 assert_eq!(read, in_s1);
380
381 let behind = in_stock.behind("p");
383 let saved = serde_json::to_value(&behind).unwrap();
384 assert_eq!(
385 saved,
386 serde_json::json!({"target": "/p:stock", "name": "a"})
387 );
388 let read: ElementReference<Stock, Length> = serde_json::from_value(saved).unwrap();
389 assert_eq!(read, behind);
390 assert_eq!(read.local(), in_stock);
391
392 assert!(serde_json::from_value::<ElementReference<Lengths, Length>>("a".into()).is_err());
395 let bare = serde_json::json!({"target": "s1", "name": "a"});
396 assert!(serde_json::from_value::<ElementReference<Lengths, Length>>(bare).is_err());
397 }
398
399 #[test]
401 fn a_pick_becomes_a_reference_of_its_kind() {
402 let picked = PickedElement {
403 target: Located::new("s1"),
404 kind: "length".into(),
405 name: "a".into(),
406 };
407 let reference = ElementReference::<Lengths, Length>::from_picked(&picked).unwrap();
408 assert_eq!(reference.picked(), picked);
409 let other = PickedElement {
410 kind: "edge".into(),
411 ..picked
412 };
413 assert!(ElementReference::<Lengths, Length>::from_picked(&other).is_none());
414 }
415}