1use crate::ast::*;
18use crate::entities::{err::EntitiesError, json::err::JsonSerializationError, EntityJson};
19use crate::evaluator::{EvaluationError, RestrictedEvaluator};
20use crate::extensions::Extensions;
21use crate::parser::err::ParseErrors;
22use crate::parser::Loc;
23use crate::transitive_closure::TCNode;
24use crate::FromNormalizedStr;
25use educe::Educe;
26use itertools::Itertools;
27use miette::Diagnostic;
28use ref_cast::RefCast;
29use serde::{Deserialize, Serialize};
30use serde_with::{serde_as, TryFromInto};
31use smol_str::SmolStr;
32use std::collections::{BTreeMap, HashMap, HashSet};
33use std::str::FromStr;
34use std::sync::Arc;
35use thiserror::Error;
36
37pub static ACTION_ENTITY_TYPE: &str = "Action";
39
40#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord, RefCast)]
42#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
43#[serde(transparent)]
44#[repr(transparent)]
45pub struct EntityType(Name);
46
47impl EntityType {
48 pub fn is_action(&self) -> bool {
53 self.0.as_ref().basename() == &Id::new_unchecked(ACTION_ENTITY_TYPE)
54 }
55
56 pub fn name(&self) -> &Name {
58 &self.0
59 }
60
61 pub fn loc(&self) -> Option<&Loc> {
63 self.0.as_ref().loc()
64 }
65
66 pub fn qualify_with(&self, namespace: Option<&Name>) -> Self {
68 Self(self.0.qualify_with_name(namespace))
69 }
70
71 pub fn from_normalized_str(src: &str) -> Result<Self, ParseErrors> {
73 Name::from_normalized_str(src).map(Into::into)
74 }
75}
76
77impl From<Name> for EntityType {
78 fn from(n: Name) -> Self {
79 Self(n)
80 }
81}
82
83impl From<EntityType> for Name {
84 fn from(ty: EntityType) -> Name {
85 ty.0
86 }
87}
88
89impl AsRef<Name> for EntityType {
90 fn as_ref(&self) -> &Name {
91 &self.0
92 }
93}
94
95impl FromStr for EntityType {
96 type Err = ParseErrors;
97
98 fn from_str(s: &str) -> Result<Self, Self::Err> {
99 s.parse().map(Self)
100 }
101}
102
103impl std::fmt::Display for EntityType {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 write!(f, "{}", self.0)
106 }
107}
108
109#[derive(Educe, Serialize, Deserialize, Debug, Clone)]
111#[educe(PartialEq, Eq, Hash, PartialOrd, Ord)]
112pub struct EntityUID {
113 ty: EntityType,
115 eid: Eid,
117 #[serde(skip)]
119 #[educe(PartialEq(ignore))]
120 #[educe(Hash(ignore))]
121 #[educe(PartialOrd(ignore))]
122 loc: Option<Loc>,
123}
124
125impl StaticallyTyped for EntityUID {
126 fn type_of(&self) -> Type {
127 Type::Entity {
128 ty: self.ty.clone(),
129 }
130 }
131}
132
133impl EntityUID {
134 #[cfg(test)]
137 pub(crate) fn with_eid(eid: &str) -> Self {
138 Self {
139 ty: Self::test_entity_type(),
140 eid: Eid(eid.into()),
141 loc: None,
142 }
143 }
144 #[cfg(test)]
152 pub(crate) fn test_entity_type() -> EntityType {
153 let name = Name::parse_unqualified_name("test_entity_type")
154 .expect("test_entity_type should be a valid identifier");
155 EntityType(name)
156 }
157 pub fn with_eid_and_type(typename: &str, eid: &str) -> Result<Self, ParseErrors> {
165 Ok(Self {
166 ty: EntityType(Name::parse_unqualified_name(typename)?),
167 eid: Eid(eid.into()),
168 loc: None,
169 })
170 }
171
172 pub fn components(self) -> (EntityType, Eid) {
175 (self.ty, self.eid)
176 }
177
178 pub fn loc(&self) -> Option<&Loc> {
180 self.loc.as_ref()
181 }
182
183 pub fn from_components(ty: EntityType, eid: Eid, loc: Option<Loc>) -> Self {
185 Self { ty, eid, loc }
186 }
187
188 pub fn entity_type(&self) -> &EntityType {
190 &self.ty
191 }
192
193 pub fn eid(&self) -> &Eid {
195 &self.eid
196 }
197
198 pub fn is_action(&self) -> bool {
200 self.entity_type().is_action()
201 }
202}
203
204impl std::fmt::Display for EntityUID {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 write!(f, "{}::\"{}\"", self.entity_type(), self.eid.escaped())
207 }
208}
209
210impl std::str::FromStr for EntityUID {
212 type Err = ParseErrors;
213
214 fn from_str(s: &str) -> Result<Self, Self::Err> {
215 crate::parser::parse_euid(s)
216 }
217}
218
219impl FromNormalizedStr for EntityUID {
220 fn describe_self() -> &'static str {
221 "Entity UID"
222 }
223}
224
225#[cfg(feature = "arbitrary")]
226impl<'a> arbitrary::Arbitrary<'a> for EntityUID {
227 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
228 Ok(Self {
229 ty: u.arbitrary()?,
230 eid: u.arbitrary()?,
231 loc: None,
232 })
233 }
234}
235
236#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
246pub struct Eid(SmolStr);
247
248impl Eid {
249 pub fn new(eid: impl Into<SmolStr>) -> Self {
251 Eid(eid.into())
252 }
253
254 pub fn escaped(&self) -> SmolStr {
256 self.0.escape_debug().collect()
257 }
258}
259
260impl AsRef<SmolStr> for Eid {
261 fn as_ref(&self) -> &SmolStr {
262 &self.0
263 }
264}
265
266impl AsRef<str> for Eid {
267 fn as_ref(&self) -> &str {
268 &self.0
269 }
270}
271
272#[cfg(feature = "arbitrary")]
273impl<'a> arbitrary::Arbitrary<'a> for Eid {
274 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
275 let x: String = u.arbitrary()?;
276 Ok(Self(x.into()))
277 }
278}
279
280#[derive(Debug, Clone, Serialize)]
282pub struct Entity {
283 uid: EntityUID,
285
286 attrs: BTreeMap<SmolStr, PartialValueSerializedAsExpr>,
292
293 ancestors: HashSet<EntityUID>,
296
297 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
304 tags: BTreeMap<SmolStr, PartialValueSerializedAsExpr>,
305}
306
307impl std::hash::Hash for Entity {
308 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
309 self.uid.hash(state);
310 }
311}
312
313impl Entity {
314 pub fn new(
319 uid: EntityUID,
320 attrs: impl IntoIterator<Item = (SmolStr, RestrictedExpr)>,
321 ancestors: HashSet<EntityUID>,
322 tags: impl IntoIterator<Item = (SmolStr, RestrictedExpr)>,
323 extensions: &Extensions<'_>,
324 ) -> Result<Self, EntityAttrEvaluationError> {
325 let evaluator = RestrictedEvaluator::new(extensions);
326 let evaluate_kvs = |(k, v): (SmolStr, RestrictedExpr), was_attr: bool| {
327 let attr_val = evaluator
328 .partial_interpret(v.as_borrowed())
329 .map_err(|err| EntityAttrEvaluationError {
330 uid: uid.clone(),
331 attr_or_tag: k.clone(),
332 was_attr,
333 err,
334 })?;
335 Ok((k, attr_val.into()))
336 };
337 let evaluated_attrs = attrs
338 .into_iter()
339 .map(|kv| evaluate_kvs(kv, true))
340 .collect::<Result<_, EntityAttrEvaluationError>>()?;
341 let evaluated_tags = tags
342 .into_iter()
343 .map(|kv| evaluate_kvs(kv, false))
344 .collect::<Result<_, EntityAttrEvaluationError>>()?;
345 Ok(Entity {
346 uid,
347 attrs: evaluated_attrs,
348 ancestors,
349 tags: evaluated_tags,
350 })
351 }
352
353 pub fn new_with_attr_partial_value(
362 uid: EntityUID,
363 attrs: impl IntoIterator<Item = (SmolStr, PartialValue)>,
364 ancestors: HashSet<EntityUID>,
365 tags: impl IntoIterator<Item = (SmolStr, PartialValue)>,
366 ) -> Self {
367 Self::new_with_attr_partial_value_serialized_as_expr(
368 uid,
369 attrs.into_iter().map(|(k, v)| (k, v.into())).collect(),
370 ancestors,
371 tags.into_iter().map(|(k, v)| (k, v.into())).collect(),
372 )
373 }
374
375 pub fn new_with_attr_partial_value_serialized_as_expr(
380 uid: EntityUID,
381 attrs: BTreeMap<SmolStr, PartialValueSerializedAsExpr>,
382 ancestors: HashSet<EntityUID>,
383 tags: BTreeMap<SmolStr, PartialValueSerializedAsExpr>,
384 ) -> Self {
385 Entity {
386 uid,
387 attrs,
388 ancestors,
389 tags,
390 }
391 }
392
393 pub fn uid(&self) -> &EntityUID {
395 &self.uid
396 }
397
398 pub fn get(&self, attr: &str) -> Option<&PartialValue> {
400 self.attrs.get(attr).map(|v| v.as_ref())
401 }
402
403 pub fn get_tag(&self, tag: &str) -> Option<&PartialValue> {
405 self.tags.get(tag).map(|v| v.as_ref())
406 }
407
408 pub fn is_descendant_of(&self, e: &EntityUID) -> bool {
410 self.ancestors.contains(e)
411 }
412
413 pub fn ancestors(&self) -> impl Iterator<Item = &EntityUID> {
415 self.ancestors.iter()
416 }
417
418 pub fn attrs_len(&self) -> usize {
420 self.attrs.len()
421 }
422
423 pub fn tags_len(&self) -> usize {
425 self.tags.len()
426 }
427
428 pub fn keys(&self) -> impl Iterator<Item = &SmolStr> {
430 self.attrs.keys()
431 }
432
433 pub fn tag_keys(&self) -> impl Iterator<Item = &SmolStr> {
435 self.tags.keys()
436 }
437
438 pub fn attrs(&self) -> impl Iterator<Item = (&SmolStr, &PartialValue)> {
440 self.attrs.iter().map(|(k, v)| (k, v.as_ref()))
441 }
442
443 pub fn tags(&self) -> impl Iterator<Item = (&SmolStr, &PartialValue)> {
445 self.tags.iter().map(|(k, v)| (k, v.as_ref()))
446 }
447
448 pub fn with_uid(uid: EntityUID) -> Self {
450 Self {
451 uid,
452 attrs: BTreeMap::new(),
453 ancestors: HashSet::new(),
454 tags: BTreeMap::new(),
455 }
456 }
457
458 pub(crate) fn deep_eq(&self, other: &Self) -> bool {
462 self.uid == other.uid && self.attrs == other.attrs && self.ancestors == other.ancestors
463 }
464
465 #[cfg(test)]
468 pub fn set_uid(&mut self, uid: EntityUID) {
469 self.uid = uid;
470 }
471
472 #[cfg(any(test, fuzzing))]
475 pub fn set_attr(
476 &mut self,
477 attr: SmolStr,
478 val: BorrowedRestrictedExpr<'_>,
479 extensions: &Extensions<'_>,
480 ) -> Result<(), EvaluationError> {
481 let val = RestrictedEvaluator::new(extensions).partial_interpret(val)?;
482 self.attrs.insert(attr, val.into());
483 Ok(())
484 }
485
486 #[cfg(any(test, fuzzing))]
489 pub fn set_tag(
490 &mut self,
491 tag: SmolStr,
492 val: BorrowedRestrictedExpr<'_>,
493 extensions: &Extensions<'_>,
494 ) -> Result<(), EvaluationError> {
495 let val = RestrictedEvaluator::new(extensions).partial_interpret(val)?;
496 self.tags.insert(tag, val.into());
497 Ok(())
498 }
499
500 pub fn add_ancestor(&mut self, uid: EntityUID) {
502 self.ancestors.insert(uid);
503 }
504
505 pub fn into_inner(
507 self,
508 ) -> (
509 EntityUID,
510 HashMap<SmolStr, PartialValue>,
511 HashSet<EntityUID>,
512 HashMap<SmolStr, PartialValue>,
513 ) {
514 let Self {
515 uid,
516 attrs,
517 ancestors,
518 tags,
519 } = self;
520 (
521 uid,
522 attrs.into_iter().map(|(k, v)| (k, v.0)).collect(),
523 ancestors,
524 tags.into_iter().map(|(k, v)| (k, v.0)).collect(),
525 )
526 }
527
528 pub fn write_to_json(&self, f: impl std::io::Write) -> Result<(), EntitiesError> {
530 let ejson = EntityJson::from_entity(self)?;
531 serde_json::to_writer_pretty(f, &ejson).map_err(JsonSerializationError::from)?;
532 Ok(())
533 }
534
535 pub fn to_json_value(&self) -> Result<serde_json::Value, EntitiesError> {
537 let ejson = EntityJson::from_entity(self)?;
538 let v = serde_json::to_value(ejson).map_err(JsonSerializationError::from)?;
539 Ok(v)
540 }
541
542 pub fn to_json_string(&self) -> Result<String, EntitiesError> {
544 let ejson = EntityJson::from_entity(self)?;
545 let string = serde_json::to_string(&ejson).map_err(JsonSerializationError::from)?;
546 Ok(string)
547 }
548}
549
550impl PartialEq for Entity {
552 fn eq(&self, other: &Self) -> bool {
553 self.uid() == other.uid()
554 }
555}
556
557impl Eq for Entity {}
558
559impl StaticallyTyped for Entity {
560 fn type_of(&self) -> Type {
561 self.uid.type_of()
562 }
563}
564
565impl TCNode<EntityUID> for Entity {
566 fn get_key(&self) -> EntityUID {
567 self.uid().clone()
568 }
569
570 fn add_edge_to(&mut self, k: EntityUID) {
571 self.add_ancestor(k)
572 }
573
574 fn out_edges(&self) -> Box<dyn Iterator<Item = &EntityUID> + '_> {
575 Box::new(self.ancestors())
576 }
577
578 fn has_edge_to(&self, e: &EntityUID) -> bool {
579 self.is_descendant_of(e)
580 }
581}
582
583impl TCNode<EntityUID> for Arc<Entity> {
584 fn get_key(&self) -> EntityUID {
585 self.uid().clone()
586 }
587
588 fn add_edge_to(&mut self, k: EntityUID) {
589 Arc::make_mut(self).add_ancestor(k)
591 }
592
593 fn out_edges(&self) -> Box<dyn Iterator<Item = &EntityUID> + '_> {
594 Box::new(self.ancestors())
595 }
596
597 fn has_edge_to(&self, e: &EntityUID) -> bool {
598 self.is_descendant_of(e)
599 }
600}
601
602impl std::fmt::Display for Entity {
603 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
604 write!(
605 f,
606 "{}:\n attrs:{}\n ancestors:{}",
607 self.uid,
608 self.attrs
609 .iter()
610 .map(|(k, v)| format!("{}: {}", k, v))
611 .join("; "),
612 self.ancestors.iter().join(", ")
613 )
614 }
615}
616
617#[serde_as]
622#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
623pub struct PartialValueSerializedAsExpr(
624 #[serde_as(as = "TryFromInto<RestrictedExpr>")] PartialValue,
625);
626
627impl AsRef<PartialValue> for PartialValueSerializedAsExpr {
628 fn as_ref(&self) -> &PartialValue {
629 &self.0
630 }
631}
632
633impl std::ops::Deref for PartialValueSerializedAsExpr {
634 type Target = PartialValue;
635 fn deref(&self) -> &Self::Target {
636 &self.0
637 }
638}
639
640impl From<PartialValue> for PartialValueSerializedAsExpr {
641 fn from(value: PartialValue) -> PartialValueSerializedAsExpr {
642 PartialValueSerializedAsExpr(value)
643 }
644}
645
646impl From<PartialValueSerializedAsExpr> for PartialValue {
647 fn from(value: PartialValueSerializedAsExpr) -> PartialValue {
648 value.0
649 }
650}
651
652impl std::fmt::Display for PartialValueSerializedAsExpr {
653 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
654 write!(f, "{}", self.0)
655 }
656}
657
658#[derive(Debug, Diagnostic, Error)]
664#[error("failed to evaluate {} `{attr_or_tag}` of `{uid}`: {err}", if *.was_attr { "attribute" } else { "tag" })]
665pub struct EntityAttrEvaluationError {
666 pub uid: EntityUID,
668 pub attr_or_tag: SmolStr,
670 pub was_attr: bool,
672 #[diagnostic(transparent)]
674 pub err: EvaluationError,
675}
676
677#[cfg(test)]
678mod test {
679 use std::str::FromStr;
680
681 use super::*;
682
683 #[test]
684 fn display() {
685 let e = EntityUID::with_eid("eid");
686 assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
687 }
688
689 #[test]
690 fn test_euid_equality() {
691 let e1 = EntityUID::with_eid("foo");
692 let e2 = EntityUID::from_components(
693 Name::parse_unqualified_name("test_entity_type")
694 .expect("should be a valid identifier")
695 .into(),
696 Eid("foo".into()),
697 None,
698 );
699 let e3 = EntityUID::from_components(
700 Name::parse_unqualified_name("Unspecified")
701 .expect("should be a valid identifier")
702 .into(),
703 Eid("foo".into()),
704 None,
705 );
706
707 assert_eq!(e1, e1);
709 assert_eq!(e2, e2);
710
711 assert_eq!(e1, e2);
713
714 assert!(e1 != e3);
716 }
717
718 #[test]
719 fn action_checker() {
720 let euid = EntityUID::from_str("Action::\"view\"").unwrap();
721 assert!(euid.is_action());
722 let euid = EntityUID::from_str("Foo::Action::\"view\"").unwrap();
723 assert!(euid.is_action());
724 let euid = EntityUID::from_str("Foo::\"view\"").unwrap();
725 assert!(!euid.is_action());
726 let euid = EntityUID::from_str("Action::Foo::\"view\"").unwrap();
727 assert!(!euid.is_action());
728 }
729
730 #[test]
731 fn action_type_is_valid_id() {
732 assert!(Id::from_normalized_str(ACTION_ENTITY_TYPE).is_ok());
733 }
734}