1use crate::ast::*;
18use crate::evaluator::{EvaluationError, RestrictedEvaluator};
19use crate::extensions::Extensions;
20use crate::parser::err::ParseErrors;
21use crate::transitive_closure::TCNode;
22use crate::FromNormalizedStr;
23use itertools::Itertools;
24use serde::{Deserialize, Serialize};
25use serde_with::{serde_as, TryFromInto};
26use smol_str::SmolStr;
27use std::collections::{HashMap, HashSet};
28use thiserror::Error;
29
30#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
34#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
35pub enum EntityType {
36 Specified(Name),
38 Unspecified,
40}
41
42impl EntityType {
43 pub fn is_action(&self) -> bool {
45 match self {
46 Self::Specified(name) => name.basename() == &Id::new_unchecked("Action"),
47 Self::Unspecified => false,
48 }
49 }
50}
51
52impl std::fmt::Display for EntityType {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 match self {
57 Self::Unspecified => write!(f, "<Unspecified>"),
58 Self::Specified(name) => write!(f, "{}", name),
59 }
60 }
61}
62
63#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
65#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
66pub struct EntityUID {
67 ty: EntityType,
69 eid: Eid,
71}
72
73impl StaticallyTyped for EntityUID {
74 fn type_of(&self) -> Type {
75 Type::Entity {
76 ty: self.ty.clone(),
77 }
78 }
79}
80
81impl EntityUID {
82 #[cfg(test)]
85 pub(crate) fn with_eid(eid: &str) -> Self {
86 Self {
87 ty: Self::test_entity_type(),
88 eid: Eid(eid.into()),
89 }
90 }
91 #[cfg(test)]
99 pub(crate) fn test_entity_type() -> EntityType {
100 let name = Name::parse_unqualified_name("test_entity_type")
101 .expect("test_entity_type should be a valid identifier");
102 EntityType::Specified(name)
103 }
104 pub fn with_eid_and_type(typename: &str, eid: &str) -> Result<Self, ParseErrors> {
112 Ok(Self {
113 ty: EntityType::Specified(Name::parse_unqualified_name(typename)?),
114 eid: Eid(eid.into()),
115 })
116 }
117
118 pub fn components(self) -> (EntityType, Eid) {
121 (self.ty, self.eid)
122 }
123
124 pub fn from_components(name: Name, eid: Eid) -> Self {
126 Self {
127 ty: EntityType::Specified(name),
128 eid,
129 }
130 }
131
132 pub fn unspecified_from_eid(eid: Eid) -> Self {
134 Self {
135 ty: EntityType::Unspecified,
136 eid,
137 }
138 }
139
140 pub fn entity_type(&self) -> &EntityType {
142 &self.ty
143 }
144
145 pub fn eid(&self) -> &Eid {
147 &self.eid
148 }
149
150 pub fn is_action(&self) -> bool {
152 self.entity_type().is_action()
153 }
154}
155
156impl std::fmt::Display for EntityUID {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 write!(f, "{}::\"{}\"", self.entity_type(), self.eid)
159 }
160}
161
162impl std::str::FromStr for EntityUID {
164 type Err = ParseErrors;
165
166 fn from_str(s: &str) -> Result<Self, Self::Err> {
167 crate::parser::parse_euid(s)
168 }
169}
170
171impl FromNormalizedStr for EntityUID {
172 fn describe_self() -> &'static str {
173 "Entity UID"
174 }
175}
176
177#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
179pub struct Eid(SmolStr);
180
181impl Eid {
182 pub fn new(eid: impl Into<SmolStr>) -> Self {
184 Eid(eid.into())
185 }
186}
187
188impl AsRef<SmolStr> for Eid {
189 fn as_ref(&self) -> &SmolStr {
190 &self.0
191 }
192}
193
194impl AsRef<str> for Eid {
195 fn as_ref(&self) -> &str {
196 &self.0
197 }
198}
199
200#[cfg(feature = "arbitrary")]
201impl<'a> arbitrary::Arbitrary<'a> for Eid {
202 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
203 let x: String = u.arbitrary()?;
204 Ok(Self(x.into()))
205 }
206}
207
208impl std::fmt::Display for Eid {
209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
210 write!(f, "{}", self.0.escape_debug())
211 }
212}
213
214#[derive(Debug, Clone, Serialize)]
216pub struct Entity {
217 uid: EntityUID,
219
220 attrs: HashMap<SmolStr, PartialValueSerializedAsExpr>,
225
226 ancestors: HashSet<EntityUID>,
229}
230
231impl Entity {
232 pub fn new(
234 uid: EntityUID,
235 attrs: HashMap<SmolStr, RestrictedExpr>,
236 ancestors: HashSet<EntityUID>,
237 extensions: &Extensions<'_>,
238 ) -> Result<Self, EntityAttrEvaluationError> {
239 let evaluator = RestrictedEvaluator::new(extensions);
240 let evaluated_attrs = attrs
241 .into_iter()
242 .map(|(k, v)| {
243 let attr_val = evaluator
244 .partial_interpret(v.as_borrowed())
245 .map_err(|err| EntityAttrEvaluationError {
246 uid: uid.clone(),
247 attr: k.clone(),
248 err,
249 })?;
250 Ok((k, attr_val.into()))
251 })
252 .collect::<Result<_, EntityAttrEvaluationError>>()?;
253 Ok(Entity {
254 uid,
255 attrs: evaluated_attrs,
256 ancestors,
257 })
258 }
259
260 pub fn new_with_attr_partial_value(
265 uid: EntityUID,
266 attrs: HashMap<SmolStr, PartialValue>,
267 ancestors: HashSet<EntityUID>,
268 ) -> Self {
269 Entity {
270 uid,
271 attrs: attrs.into_iter().map(|(k, v)| (k, v.into())).collect(), ancestors,
273 }
274 }
275
276 pub fn new_with_attr_partial_value_serialized_as_expr(
281 uid: EntityUID,
282 attrs: HashMap<SmolStr, PartialValueSerializedAsExpr>,
283 ancestors: HashSet<EntityUID>,
284 ) -> Self {
285 Entity {
286 uid,
287 attrs,
288 ancestors,
289 }
290 }
291
292 pub fn uid(&self) -> EntityUID {
294 self.uid.clone()
295 }
296
297 pub fn get(&self, attr: &str) -> Option<&PartialValue> {
299 self.attrs.get(attr).map(|v| v.as_ref())
300 }
301
302 pub fn is_descendant_of(&self, e: &EntityUID) -> bool {
304 self.ancestors.contains(e)
305 }
306
307 pub fn ancestors(&self) -> impl Iterator<Item = &EntityUID> {
309 self.ancestors.iter()
310 }
311
312 pub fn attrs(&self) -> impl Iterator<Item = (&SmolStr, &PartialValue)> {
314 self.attrs.iter().map(|(k, v)| (k, v.as_ref()))
315 }
316
317 pub fn with_uid(uid: EntityUID) -> Self {
319 Self {
320 uid,
321 attrs: HashMap::new(),
322 ancestors: HashSet::new(),
323 }
324 }
325
326 pub(crate) fn deep_eq(&self, other: &Self) -> bool {
330 self.uid == other.uid && self.attrs == other.attrs && self.ancestors == other.ancestors
331 }
332
333 #[cfg(any(test, fuzzing))]
336 pub fn set_attr(
337 &mut self,
338 attr: SmolStr,
339 val: RestrictedExpr,
340 extensions: &Extensions<'_>,
341 ) -> Result<(), EvaluationError> {
342 let val = RestrictedEvaluator::new(extensions).partial_interpret(val.as_borrowed())?;
343 self.attrs.insert(attr, val.into());
344 Ok(())
345 }
346
347 #[cfg(not(fuzzing))]
350 pub(crate) fn add_ancestor(&mut self, uid: EntityUID) {
351 self.ancestors.insert(uid);
352 }
353 #[cfg(fuzzing)]
355 pub fn add_ancestor(&mut self, uid: EntityUID) {
356 self.ancestors.insert(uid);
357 }
358}
359
360impl PartialEq for Entity {
361 fn eq(&self, other: &Self) -> bool {
362 self.uid() == other.uid()
363 }
364}
365
366impl Eq for Entity {}
367
368impl StaticallyTyped for Entity {
369 fn type_of(&self) -> Type {
370 self.uid.type_of()
371 }
372}
373
374impl TCNode<EntityUID> for Entity {
375 fn get_key(&self) -> EntityUID {
376 self.uid()
377 }
378
379 fn add_edge_to(&mut self, k: EntityUID) {
380 self.add_ancestor(k)
381 }
382
383 fn out_edges(&self) -> Box<dyn Iterator<Item = &EntityUID> + '_> {
384 Box::new(self.ancestors())
385 }
386
387 fn has_edge_to(&self, e: &EntityUID) -> bool {
388 self.is_descendant_of(e)
389 }
390}
391
392impl std::fmt::Display for Entity {
393 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394 write!(
395 f,
396 "{}:\n attrs:{}\n ancestors:{}",
397 self.uid,
398 self.attrs
399 .iter()
400 .map(|(k, v)| format!("{}: {}", k, v))
401 .join("; "),
402 self.ancestors.iter().join(", ")
403 )
404 }
405}
406
407#[serde_as]
412#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
413pub struct PartialValueSerializedAsExpr(
414 #[serde_as(as = "TryFromInto<RestrictedExpr>")] PartialValue,
415);
416
417impl AsRef<PartialValue> for PartialValueSerializedAsExpr {
418 fn as_ref(&self) -> &PartialValue {
419 &self.0
420 }
421}
422
423impl std::ops::Deref for PartialValueSerializedAsExpr {
424 type Target = PartialValue;
425 fn deref(&self) -> &Self::Target {
426 &self.0
427 }
428}
429
430impl From<PartialValue> for PartialValueSerializedAsExpr {
431 fn from(value: PartialValue) -> PartialValueSerializedAsExpr {
432 PartialValueSerializedAsExpr(value)
433 }
434}
435
436impl From<PartialValueSerializedAsExpr> for PartialValue {
437 fn from(value: PartialValueSerializedAsExpr) -> PartialValue {
438 value.0
439 }
440}
441
442impl std::fmt::Display for PartialValueSerializedAsExpr {
443 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444 write!(f, "{}", self.0)
445 }
446}
447
448#[derive(Debug, Error)]
452#[error("failed to evaluate attribute `{attr}` of `{uid}`: {err}")]
453pub struct EntityAttrEvaluationError {
454 pub uid: EntityUID,
456 pub attr: SmolStr,
458 pub err: EvaluationError,
460}
461
462#[cfg(test)]
463mod test {
464 use super::*;
465
466 #[test]
467 fn display() {
468 let e = EntityUID::with_eid("eid");
469 assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
470 }
471
472 #[test]
473 fn test_euid_equality() {
474 let e1 = EntityUID::with_eid("foo");
475 let e2 = EntityUID::from_components(
476 Name::parse_unqualified_name("test_entity_type").expect("should be a valid identifier"),
477 Eid("foo".into()),
478 );
479 let e3 = EntityUID::unspecified_from_eid(Eid("foo".into()));
480 let e4 = EntityUID::unspecified_from_eid(Eid("bar".into()));
481 let e5 = EntityUID::from_components(
482 Name::parse_unqualified_name("Unspecified").expect("should be a valid identifier"),
483 Eid("foo".into()),
484 );
485
486 assert_eq!(e1, e1);
488 assert_eq!(e2, e2);
489 assert_eq!(e3, e3);
490
491 assert_eq!(e1, e2);
493
494 assert!(e1 != e3);
496 assert!(e1 != e4);
497 assert!(e1 != e5);
498 assert!(e3 != e4);
499 assert!(e3 != e5);
500 assert!(e4 != e5);
501
502 assert!(format!("{e3}") != format!("{e5}"));
504 }
505}