1use crate::ast::*;
18use crate::evaluator::{EvaluationError, RestrictedEvaluator};
19use crate::extensions::Extensions;
20use crate::parser::err::ParseErrors;
21use crate::parser::Loc;
22use crate::transitive_closure::TCNode;
23use crate::FromNormalizedStr;
24use itertools::Itertools;
25use miette::Diagnostic;
26use serde::{Deserialize, Serialize};
27use serde_with::{serde_as, TryFromInto};
28use smol_str::SmolStr;
29use std::collections::{BTreeMap, HashMap, HashSet};
30use thiserror::Error;
31
32#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
36#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
37pub enum EntityType {
38 Specified(Name),
40 Unspecified,
42}
43
44impl EntityType {
45 pub fn is_action(&self) -> bool {
47 match self {
48 Self::Specified(name) => name.basename() == &Id::new_unchecked("Action"),
49 Self::Unspecified => false,
50 }
51 }
52}
53
54impl std::fmt::Display for EntityType {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 match self {
59 Self::Unspecified => write!(f, "<Unspecified>"),
60 Self::Specified(name) => write!(f, "{}", name),
61 }
62 }
63}
64
65#[derive(Serialize, Deserialize, Debug, Clone)]
67pub struct EntityUID {
68 ty: EntityType,
70 eid: Eid,
72 #[serde(skip)]
74 loc: Option<Loc>,
75}
76
77impl PartialEq for EntityUID {
79 fn eq(&self, other: &Self) -> bool {
80 self.ty == other.ty && self.eid == other.eid
81 }
82}
83impl Eq for EntityUID {}
84
85impl std::hash::Hash for EntityUID {
86 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
87 self.ty.hash(state);
90 self.eid.hash(state);
91 }
92}
93
94impl PartialOrd for EntityUID {
95 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
96 Some(self.cmp(other))
97 }
98}
99impl Ord for EntityUID {
100 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
101 self.ty.cmp(&other.ty).then(self.eid.cmp(&other.eid))
102 }
103}
104
105impl StaticallyTyped for EntityUID {
106 fn type_of(&self) -> Type {
107 Type::Entity {
108 ty: self.ty.clone(),
109 }
110 }
111}
112
113impl EntityUID {
114 #[cfg(test)]
117 pub(crate) fn with_eid(eid: &str) -> Self {
118 Self {
119 ty: Self::test_entity_type(),
120 eid: Eid(eid.into()),
121 loc: None,
122 }
123 }
124 #[cfg(test)]
132 pub(crate) fn test_entity_type() -> EntityType {
133 let name = Name::parse_unqualified_name("test_entity_type")
134 .expect("test_entity_type should be a valid identifier");
135 EntityType::Specified(name)
136 }
137 pub fn with_eid_and_type(typename: &str, eid: &str) -> Result<Self, ParseErrors> {
145 Ok(Self {
146 ty: EntityType::Specified(Name::parse_unqualified_name(typename)?),
147 eid: Eid(eid.into()),
148 loc: None,
149 })
150 }
151
152 pub fn components(self) -> (EntityType, Eid) {
155 (self.ty, self.eid)
156 }
157
158 pub fn loc(&self) -> Option<&Loc> {
160 self.loc.as_ref()
161 }
162
163 pub fn from_components(name: Name, eid: Eid, loc: Option<Loc>) -> Self {
165 Self {
166 ty: EntityType::Specified(name),
167 eid,
168 loc,
169 }
170 }
171
172 pub fn unspecified_from_eid(eid: Eid) -> Self {
174 Self {
175 ty: EntityType::Unspecified,
176 eid,
177 loc: None,
178 }
179 }
180
181 pub fn entity_type(&self) -> &EntityType {
183 &self.ty
184 }
185
186 pub fn eid(&self) -> &Eid {
188 &self.eid
189 }
190
191 pub fn is_action(&self) -> bool {
193 self.entity_type().is_action()
194 }
195}
196
197impl std::fmt::Display for EntityUID {
198 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199 write!(f, "{}::\"{}\"", self.entity_type(), self.eid)
200 }
201}
202
203impl std::str::FromStr for EntityUID {
205 type Err = ParseErrors;
206
207 fn from_str(s: &str) -> Result<Self, Self::Err> {
208 crate::parser::parse_euid(s)
209 }
210}
211
212impl FromNormalizedStr for EntityUID {
213 fn describe_self() -> &'static str {
214 "Entity UID"
215 }
216}
217
218#[cfg(feature = "arbitrary")]
219impl<'a> arbitrary::Arbitrary<'a> for EntityUID {
220 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
221 Ok(Self {
222 ty: u.arbitrary()?,
223 eid: u.arbitrary()?,
224 loc: None,
225 })
226 }
227}
228
229#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
231pub struct Eid(SmolStr);
232
233impl Eid {
234 pub fn new(eid: impl Into<SmolStr>) -> Self {
236 Eid(eid.into())
237 }
238}
239
240impl AsRef<SmolStr> for Eid {
241 fn as_ref(&self) -> &SmolStr {
242 &self.0
243 }
244}
245
246impl AsRef<str> for Eid {
247 fn as_ref(&self) -> &str {
248 &self.0
249 }
250}
251
252#[cfg(feature = "arbitrary")]
253impl<'a> arbitrary::Arbitrary<'a> for Eid {
254 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
255 let x: String = u.arbitrary()?;
256 Ok(Self(x.into()))
257 }
258}
259
260impl std::fmt::Display for Eid {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 write!(f, "{}", self.0.escape_debug())
263 }
264}
265
266#[derive(Debug, Clone, Serialize)]
268pub struct Entity {
269 uid: EntityUID,
271
272 attrs: BTreeMap<SmolStr, PartialValueSerializedAsExpr>,
278
279 ancestors: HashSet<EntityUID>,
282}
283
284impl Entity {
285 pub fn new(
287 uid: EntityUID,
288 attrs: HashMap<SmolStr, RestrictedExpr>,
289 ancestors: HashSet<EntityUID>,
290 extensions: &Extensions<'_>,
291 ) -> Result<Self, EntityAttrEvaluationError> {
292 let evaluator = RestrictedEvaluator::new(extensions);
293 let evaluated_attrs = attrs
294 .into_iter()
295 .map(|(k, v)| {
296 let attr_val = evaluator
297 .partial_interpret(v.as_borrowed())
298 .map_err(|err| EntityAttrEvaluationError {
299 uid: uid.clone(),
300 attr: k.clone(),
301 err,
302 })?;
303 Ok((k, attr_val.into()))
304 })
305 .collect::<Result<_, EntityAttrEvaluationError>>()?;
306 Ok(Entity {
307 uid,
308 attrs: evaluated_attrs,
309 ancestors,
310 })
311 }
312
313 pub fn new_with_attr_partial_value(
318 uid: EntityUID,
319 attrs: HashMap<SmolStr, PartialValue>,
320 ancestors: HashSet<EntityUID>,
321 ) -> Self {
322 Entity {
323 uid,
324 attrs: attrs.into_iter().map(|(k, v)| (k, v.into())).collect(), ancestors,
326 }
327 }
328
329 pub fn new_with_attr_partial_value_serialized_as_expr(
334 uid: EntityUID,
335 attrs: BTreeMap<SmolStr, PartialValueSerializedAsExpr>,
336 ancestors: HashSet<EntityUID>,
337 ) -> Self {
338 Entity {
339 uid,
340 attrs,
341 ancestors,
342 }
343 }
344
345 pub fn uid(&self) -> &EntityUID {
347 &self.uid
348 }
349
350 pub fn get(&self, attr: &str) -> Option<&PartialValue> {
352 self.attrs.get(attr).map(|v| v.as_ref())
353 }
354
355 pub fn is_descendant_of(&self, e: &EntityUID) -> bool {
357 self.ancestors.contains(e)
358 }
359
360 pub fn ancestors(&self) -> impl Iterator<Item = &EntityUID> {
362 self.ancestors.iter()
363 }
364
365 pub fn attrs_len(&self) -> usize {
367 self.attrs.len()
368 }
369
370 pub fn keys(&self) -> impl Iterator<Item = &SmolStr> {
372 self.attrs.keys()
373 }
374
375 pub fn attrs(&self) -> impl Iterator<Item = (&SmolStr, &PartialValue)> {
377 self.attrs.iter().map(|(k, v)| (k, v.as_ref()))
378 }
379
380 pub fn with_uid(uid: EntityUID) -> Self {
382 Self {
383 uid,
384 attrs: BTreeMap::new(),
385 ancestors: HashSet::new(),
386 }
387 }
388
389 pub(crate) fn deep_eq(&self, other: &Self) -> bool {
393 self.uid == other.uid && self.attrs == other.attrs && self.ancestors == other.ancestors
394 }
395
396 #[cfg(any(test, fuzzing))]
399 pub fn set_attr(
400 &mut self,
401 attr: SmolStr,
402 val: RestrictedExpr,
403 extensions: &Extensions<'_>,
404 ) -> Result<(), EvaluationError> {
405 let val = RestrictedEvaluator::new(extensions).partial_interpret(val.as_borrowed())?;
406 self.attrs.insert(attr, val.into());
407 Ok(())
408 }
409
410 #[cfg(not(fuzzing))]
413 pub(crate) fn add_ancestor(&mut self, uid: EntityUID) {
414 self.ancestors.insert(uid);
415 }
416 #[cfg(fuzzing)]
418 pub fn add_ancestor(&mut self, uid: EntityUID) {
419 self.ancestors.insert(uid);
420 }
421
422 pub fn into_inner(
424 self,
425 ) -> (
426 EntityUID,
427 HashMap<SmolStr, PartialValue>,
428 HashSet<EntityUID>,
429 ) {
430 let Self {
431 uid,
432 attrs,
433 ancestors,
434 } = self;
435 (
436 uid,
437 attrs.into_iter().map(|(k, v)| (k, v.0)).collect(),
438 ancestors,
439 )
440 }
441}
442
443impl PartialEq for Entity {
444 fn eq(&self, other: &Self) -> bool {
445 self.uid() == other.uid()
446 }
447}
448
449impl Eq for Entity {}
450
451impl StaticallyTyped for Entity {
452 fn type_of(&self) -> Type {
453 self.uid.type_of()
454 }
455}
456
457impl TCNode<EntityUID> for Entity {
458 fn get_key(&self) -> EntityUID {
459 self.uid().clone()
460 }
461
462 fn add_edge_to(&mut self, k: EntityUID) {
463 self.add_ancestor(k)
464 }
465
466 fn out_edges(&self) -> Box<dyn Iterator<Item = &EntityUID> + '_> {
467 Box::new(self.ancestors())
468 }
469
470 fn has_edge_to(&self, e: &EntityUID) -> bool {
471 self.is_descendant_of(e)
472 }
473}
474
475impl std::fmt::Display for Entity {
476 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
477 write!(
478 f,
479 "{}:\n attrs:{}\n ancestors:{}",
480 self.uid,
481 self.attrs
482 .iter()
483 .map(|(k, v)| format!("{}: {}", k, v))
484 .join("; "),
485 self.ancestors.iter().join(", ")
486 )
487 }
488}
489
490#[serde_as]
495#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
496pub struct PartialValueSerializedAsExpr(
497 #[serde_as(as = "TryFromInto<RestrictedExpr>")] PartialValue,
498);
499
500impl AsRef<PartialValue> for PartialValueSerializedAsExpr {
501 fn as_ref(&self) -> &PartialValue {
502 &self.0
503 }
504}
505
506impl std::ops::Deref for PartialValueSerializedAsExpr {
507 type Target = PartialValue;
508 fn deref(&self) -> &Self::Target {
509 &self.0
510 }
511}
512
513impl From<PartialValue> for PartialValueSerializedAsExpr {
514 fn from(value: PartialValue) -> PartialValueSerializedAsExpr {
515 PartialValueSerializedAsExpr(value)
516 }
517}
518
519impl From<PartialValueSerializedAsExpr> for PartialValue {
520 fn from(value: PartialValueSerializedAsExpr) -> PartialValue {
521 value.0
522 }
523}
524
525impl std::fmt::Display for PartialValueSerializedAsExpr {
526 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
527 write!(f, "{}", self.0)
528 }
529}
530
531#[derive(Debug, Diagnostic, Error)]
535#[error("failed to evaluate attribute `{attr}` of `{uid}`: {err}")]
536pub struct EntityAttrEvaluationError {
537 pub uid: EntityUID,
539 pub attr: SmolStr,
541 #[diagnostic(transparent)]
543 pub err: EvaluationError,
544}
545
546#[cfg(test)]
547mod test {
548 use super::*;
549
550 #[test]
551 fn display() {
552 let e = EntityUID::with_eid("eid");
553 assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
554 }
555
556 #[test]
557 fn test_euid_equality() {
558 let e1 = EntityUID::with_eid("foo");
559 let e2 = EntityUID::from_components(
560 Name::parse_unqualified_name("test_entity_type").expect("should be a valid identifier"),
561 Eid("foo".into()),
562 None,
563 );
564 let e3 = EntityUID::unspecified_from_eid(Eid("foo".into()));
565 let e4 = EntityUID::unspecified_from_eid(Eid("bar".into()));
566 let e5 = EntityUID::from_components(
567 Name::parse_unqualified_name("Unspecified").expect("should be a valid identifier"),
568 Eid("foo".into()),
569 None,
570 );
571
572 assert_eq!(e1, e1);
574 assert_eq!(e2, e2);
575 assert_eq!(e3, e3);
576
577 assert_eq!(e1, e2);
579
580 assert!(e1 != e3);
582 assert!(e1 != e4);
583 assert!(e1 != e5);
584 assert!(e3 != e4);
585 assert!(e3 != e5);
586 assert!(e4 != e5);
587
588 assert!(format!("{e3}") != format!("{e5}"));
590 }
591}