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 itertools::Itertools;
26use miette::Diagnostic;
27use ref_cast::RefCast;
28use serde::{Deserialize, Serialize};
29use serde_with::{serde_as, TryFromInto};
30use smol_str::SmolStr;
31use std::collections::{BTreeMap, HashMap, HashSet};
32use std::str::FromStr;
33use std::sync::Arc;
34use thiserror::Error;
35
36pub static ACTION_ENTITY_TYPE: &str = "Action";
38
39#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord, RefCast)]
41#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
42#[serde(transparent)]
43#[repr(transparent)]
44pub struct EntityType(Name);
45
46impl EntityType {
47 pub fn is_action(&self) -> bool {
52 self.0.as_ref().basename() == &Id::new_unchecked(ACTION_ENTITY_TYPE)
53 }
54
55 pub fn name(&self) -> &Name {
57 &self.0
58 }
59
60 pub fn loc(&self) -> Option<&Loc> {
62 self.0.as_ref().loc()
63 }
64
65 pub fn qualify_with(&self, namespace: Option<&Name>) -> Self {
67 Self(self.0.qualify_with_name(namespace))
68 }
69
70 pub fn from_normalized_str(src: &str) -> Result<Self, ParseErrors> {
72 Name::from_normalized_str(src).map(Into::into)
73 }
74}
75
76impl From<Name> for EntityType {
77 fn from(n: Name) -> Self {
78 Self(n)
79 }
80}
81
82impl From<EntityType> for Name {
83 fn from(ty: EntityType) -> Name {
84 ty.0
85 }
86}
87
88impl AsRef<Name> for EntityType {
89 fn as_ref(&self) -> &Name {
90 &self.0
91 }
92}
93
94impl FromStr for EntityType {
95 type Err = ParseErrors;
96
97 fn from_str(s: &str) -> Result<Self, Self::Err> {
98 s.parse().map(Self)
99 }
100}
101
102impl std::fmt::Display for EntityType {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 write!(f, "{}", self.0)
105 }
106}
107
108#[derive(Serialize, Deserialize, Debug, Clone)]
110pub struct EntityUID {
111 ty: EntityType,
113 eid: Eid,
115 #[serde(skip)]
117 loc: Option<Loc>,
118}
119
120impl PartialEq for EntityUID {
122 fn eq(&self, other: &Self) -> bool {
123 self.ty == other.ty && self.eid == other.eid
124 }
125}
126impl Eq for EntityUID {}
127
128impl std::hash::Hash for EntityUID {
129 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
130 self.ty.hash(state);
133 self.eid.hash(state);
134 }
135}
136
137impl PartialOrd for EntityUID {
138 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
139 Some(self.cmp(other))
140 }
141}
142impl Ord for EntityUID {
143 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
144 self.ty.cmp(&other.ty).then(self.eid.cmp(&other.eid))
145 }
146}
147
148impl StaticallyTyped for EntityUID {
149 fn type_of(&self) -> Type {
150 Type::Entity {
151 ty: self.ty.clone(),
152 }
153 }
154}
155
156impl EntityUID {
157 #[cfg(test)]
160 pub(crate) fn with_eid(eid: &str) -> Self {
161 Self {
162 ty: Self::test_entity_type(),
163 eid: Eid(eid.into()),
164 loc: None,
165 }
166 }
167 #[cfg(test)]
175 pub(crate) fn test_entity_type() -> EntityType {
176 let name = Name::parse_unqualified_name("test_entity_type")
177 .expect("test_entity_type should be a valid identifier");
178 EntityType(name)
179 }
180 pub fn with_eid_and_type(typename: &str, eid: &str) -> Result<Self, ParseErrors> {
188 Ok(Self {
189 ty: EntityType(Name::parse_unqualified_name(typename)?),
190 eid: Eid(eid.into()),
191 loc: None,
192 })
193 }
194
195 pub fn components(self) -> (EntityType, Eid) {
198 (self.ty, self.eid)
199 }
200
201 pub fn loc(&self) -> Option<&Loc> {
203 self.loc.as_ref()
204 }
205
206 pub fn from_components(ty: EntityType, eid: Eid, loc: Option<Loc>) -> Self {
208 Self { ty, eid, loc }
209 }
210
211 pub fn entity_type(&self) -> &EntityType {
213 &self.ty
214 }
215
216 pub fn eid(&self) -> &Eid {
218 &self.eid
219 }
220
221 pub fn is_action(&self) -> bool {
223 self.entity_type().is_action()
224 }
225}
226
227impl std::fmt::Display for EntityUID {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 write!(f, "{}::\"{}\"", self.entity_type(), self.eid.escaped())
230 }
231}
232
233impl std::str::FromStr for EntityUID {
235 type Err = ParseErrors;
236
237 fn from_str(s: &str) -> Result<Self, Self::Err> {
238 crate::parser::parse_euid(s)
239 }
240}
241
242impl FromNormalizedStr for EntityUID {
243 fn describe_self() -> &'static str {
244 "Entity UID"
245 }
246}
247
248#[cfg(feature = "arbitrary")]
249impl<'a> arbitrary::Arbitrary<'a> for EntityUID {
250 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
251 Ok(Self {
252 ty: u.arbitrary()?,
253 eid: u.arbitrary()?,
254 loc: None,
255 })
256 }
257}
258
259#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
269pub struct Eid(SmolStr);
270
271impl Eid {
272 pub fn new(eid: impl Into<SmolStr>) -> Self {
274 Eid(eid.into())
275 }
276
277 pub fn escaped(&self) -> SmolStr {
279 self.0.escape_debug().collect()
280 }
281}
282
283impl AsRef<SmolStr> for Eid {
284 fn as_ref(&self) -> &SmolStr {
285 &self.0
286 }
287}
288
289impl AsRef<str> for Eid {
290 fn as_ref(&self) -> &str {
291 &self.0
292 }
293}
294
295#[cfg(feature = "arbitrary")]
296impl<'a> arbitrary::Arbitrary<'a> for Eid {
297 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
298 let x: String = u.arbitrary()?;
299 Ok(Self(x.into()))
300 }
301}
302
303#[derive(Debug, Clone, Serialize)]
305pub struct Entity {
306 uid: EntityUID,
308
309 attrs: BTreeMap<SmolStr, PartialValueSerializedAsExpr>,
315
316 ancestors: HashSet<EntityUID>,
319
320 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
327 tags: BTreeMap<SmolStr, PartialValueSerializedAsExpr>,
328}
329
330impl std::hash::Hash for Entity {
331 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
332 self.uid.hash(state);
333 }
334}
335
336impl Entity {
337 pub fn new(
342 uid: EntityUID,
343 attrs: impl IntoIterator<Item = (SmolStr, RestrictedExpr)>,
344 ancestors: HashSet<EntityUID>,
345 tags: impl IntoIterator<Item = (SmolStr, RestrictedExpr)>,
346 extensions: &Extensions<'_>,
347 ) -> Result<Self, EntityAttrEvaluationError> {
348 let evaluator = RestrictedEvaluator::new(extensions);
349 let evaluate_kvs = |(k, v): (SmolStr, RestrictedExpr), was_attr: bool| {
350 let attr_val = evaluator
351 .partial_interpret(v.as_borrowed())
352 .map_err(|err| EntityAttrEvaluationError {
353 uid: uid.clone(),
354 attr_or_tag: k.clone(),
355 was_attr,
356 err,
357 })?;
358 Ok((k, attr_val.into()))
359 };
360 let evaluated_attrs = attrs
361 .into_iter()
362 .map(|kv| evaluate_kvs(kv, true))
363 .collect::<Result<_, EntityAttrEvaluationError>>()?;
364 let evaluated_tags = tags
365 .into_iter()
366 .map(|kv| evaluate_kvs(kv, false))
367 .collect::<Result<_, EntityAttrEvaluationError>>()?;
368 Ok(Entity {
369 uid,
370 attrs: evaluated_attrs,
371 ancestors,
372 tags: evaluated_tags,
373 })
374 }
375
376 pub fn new_with_attr_partial_value(
385 uid: EntityUID,
386 attrs: impl IntoIterator<Item = (SmolStr, PartialValue)>,
387 ancestors: HashSet<EntityUID>,
388 ) -> Self {
389 Self::new_with_attr_partial_value_serialized_as_expr(
390 uid,
391 attrs.into_iter().map(|(k, v)| (k, v.into())).collect(),
392 ancestors,
393 )
394 }
395
396 pub fn new_with_attr_partial_value_serialized_as_expr(
401 uid: EntityUID,
402 attrs: BTreeMap<SmolStr, PartialValueSerializedAsExpr>,
403 ancestors: HashSet<EntityUID>,
404 ) -> Self {
405 Entity {
406 uid,
407 attrs,
408 ancestors,
409 tags: BTreeMap::new(),
410 }
411 }
412
413 pub fn uid(&self) -> &EntityUID {
415 &self.uid
416 }
417
418 pub fn get(&self, attr: &str) -> Option<&PartialValue> {
420 self.attrs.get(attr).map(|v| v.as_ref())
421 }
422
423 pub fn get_tag(&self, tag: &str) -> Option<&PartialValue> {
425 self.tags.get(tag).map(|v| v.as_ref())
426 }
427
428 pub fn is_descendant_of(&self, e: &EntityUID) -> bool {
430 self.ancestors.contains(e)
431 }
432
433 pub fn ancestors(&self) -> impl Iterator<Item = &EntityUID> {
435 self.ancestors.iter()
436 }
437
438 pub fn attrs_len(&self) -> usize {
440 self.attrs.len()
441 }
442
443 pub fn tags_len(&self) -> usize {
445 self.tags.len()
446 }
447
448 pub fn keys(&self) -> impl Iterator<Item = &SmolStr> {
450 self.attrs.keys()
451 }
452
453 pub fn tag_keys(&self) -> impl Iterator<Item = &SmolStr> {
455 self.tags.keys()
456 }
457
458 pub fn attrs(&self) -> impl Iterator<Item = (&SmolStr, &PartialValue)> {
460 self.attrs.iter().map(|(k, v)| (k, v.as_ref()))
461 }
462
463 pub fn tags(&self) -> impl Iterator<Item = (&SmolStr, &PartialValue)> {
465 self.tags.iter().map(|(k, v)| (k, v.as_ref()))
466 }
467
468 pub fn with_uid(uid: EntityUID) -> Self {
470 Self {
471 uid,
472 attrs: BTreeMap::new(),
473 ancestors: HashSet::new(),
474 tags: BTreeMap::new(),
475 }
476 }
477
478 pub(crate) fn deep_eq(&self, other: &Self) -> bool {
482 self.uid == other.uid && self.attrs == other.attrs && self.ancestors == other.ancestors
483 }
484
485 #[cfg(test)]
488 pub fn set_uid(&mut self, uid: EntityUID) {
489 self.uid = uid;
490 }
491
492 #[cfg(any(test, fuzzing))]
495 pub fn set_attr(
496 &mut self,
497 attr: SmolStr,
498 val: RestrictedExpr,
499 extensions: &Extensions<'_>,
500 ) -> Result<(), EvaluationError> {
501 let val = RestrictedEvaluator::new(extensions).partial_interpret(val.as_borrowed())?;
502 self.attrs.insert(attr, val.into());
503 Ok(())
504 }
505
506 #[cfg(any(test, fuzzing))]
509 pub fn set_tag(
510 &mut self,
511 tag: SmolStr,
512 val: RestrictedExpr,
513 extensions: &Extensions<'_>,
514 ) -> Result<(), EvaluationError> {
515 let val = RestrictedEvaluator::new(extensions).partial_interpret(val.as_borrowed())?;
516 self.tags.insert(tag, val.into());
517 Ok(())
518 }
519
520 #[cfg(not(fuzzing))]
523 pub(crate) fn add_ancestor(&mut self, uid: EntityUID) {
524 self.ancestors.insert(uid);
525 }
526 #[cfg(fuzzing)]
528 pub fn add_ancestor(&mut self, uid: EntityUID) {
529 self.ancestors.insert(uid);
530 }
531
532 pub fn into_inner(
534 self,
535 ) -> (
536 EntityUID,
537 HashMap<SmolStr, PartialValue>,
538 HashSet<EntityUID>,
539 HashMap<SmolStr, PartialValue>,
540 ) {
541 let Self {
542 uid,
543 attrs,
544 ancestors,
545 tags,
546 } = self;
547 (
548 uid,
549 attrs.into_iter().map(|(k, v)| (k, v.0)).collect(),
550 ancestors,
551 tags.into_iter().map(|(k, v)| (k, v.0)).collect(),
552 )
553 }
554
555 pub fn write_to_json(&self, f: impl std::io::Write) -> Result<(), EntitiesError> {
557 let ejson = EntityJson::from_entity(self)?;
558 serde_json::to_writer_pretty(f, &ejson).map_err(JsonSerializationError::from)?;
559 Ok(())
560 }
561
562 pub fn to_json_value(&self) -> Result<serde_json::Value, EntitiesError> {
564 let ejson = EntityJson::from_entity(self)?;
565 let v = serde_json::to_value(ejson).map_err(JsonSerializationError::from)?;
566 Ok(v)
567 }
568
569 pub fn to_json_string(&self) -> Result<String, EntitiesError> {
571 let ejson = EntityJson::from_entity(self)?;
572 let string = serde_json::to_string(&ejson).map_err(JsonSerializationError::from)?;
573 Ok(string)
574 }
575}
576
577impl PartialEq for Entity {
578 fn eq(&self, other: &Self) -> bool {
579 self.uid() == other.uid()
580 }
581}
582
583impl Eq for Entity {}
584
585impl StaticallyTyped for Entity {
586 fn type_of(&self) -> Type {
587 self.uid.type_of()
588 }
589}
590
591impl TCNode<EntityUID> for Entity {
592 fn get_key(&self) -> EntityUID {
593 self.uid().clone()
594 }
595
596 fn add_edge_to(&mut self, k: EntityUID) {
597 self.add_ancestor(k)
598 }
599
600 fn out_edges(&self) -> Box<dyn Iterator<Item = &EntityUID> + '_> {
601 Box::new(self.ancestors())
602 }
603
604 fn has_edge_to(&self, e: &EntityUID) -> bool {
605 self.is_descendant_of(e)
606 }
607}
608
609impl TCNode<EntityUID> for Arc<Entity> {
610 fn get_key(&self) -> EntityUID {
611 self.uid().clone()
612 }
613
614 fn add_edge_to(&mut self, k: EntityUID) {
615 Arc::make_mut(self).add_ancestor(k)
617 }
618
619 fn out_edges(&self) -> Box<dyn Iterator<Item = &EntityUID> + '_> {
620 Box::new(self.ancestors())
621 }
622
623 fn has_edge_to(&self, e: &EntityUID) -> bool {
624 self.is_descendant_of(e)
625 }
626}
627
628impl std::fmt::Display for Entity {
629 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
630 write!(
631 f,
632 "{}:\n attrs:{}\n ancestors:{}",
633 self.uid,
634 self.attrs
635 .iter()
636 .map(|(k, v)| format!("{}: {}", k, v))
637 .join("; "),
638 self.ancestors.iter().join(", ")
639 )
640 }
641}
642
643#[serde_as]
648#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
649pub struct PartialValueSerializedAsExpr(
650 #[serde_as(as = "TryFromInto<RestrictedExpr>")] PartialValue,
651);
652
653impl AsRef<PartialValue> for PartialValueSerializedAsExpr {
654 fn as_ref(&self) -> &PartialValue {
655 &self.0
656 }
657}
658
659impl std::ops::Deref for PartialValueSerializedAsExpr {
660 type Target = PartialValue;
661 fn deref(&self) -> &Self::Target {
662 &self.0
663 }
664}
665
666impl From<PartialValue> for PartialValueSerializedAsExpr {
667 fn from(value: PartialValue) -> PartialValueSerializedAsExpr {
668 PartialValueSerializedAsExpr(value)
669 }
670}
671
672impl From<PartialValueSerializedAsExpr> for PartialValue {
673 fn from(value: PartialValueSerializedAsExpr) -> PartialValue {
674 value.0
675 }
676}
677
678impl std::fmt::Display for PartialValueSerializedAsExpr {
679 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
680 write!(f, "{}", self.0)
681 }
682}
683
684#[derive(Debug, Diagnostic, Error)]
690#[error("failed to evaluate {} `{attr_or_tag}` of `{uid}`: {err}", if *.was_attr { "attribute" } else { "tag" })]
691pub struct EntityAttrEvaluationError {
692 pub uid: EntityUID,
694 pub attr_or_tag: SmolStr,
696 pub was_attr: bool,
698 #[diagnostic(transparent)]
700 pub err: EvaluationError,
701}
702
703#[cfg(test)]
704mod test {
705 use std::str::FromStr;
706
707 use super::*;
708
709 #[test]
710 fn display() {
711 let e = EntityUID::with_eid("eid");
712 assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
713 }
714
715 #[test]
716 fn test_euid_equality() {
717 let e1 = EntityUID::with_eid("foo");
718 let e2 = EntityUID::from_components(
719 Name::parse_unqualified_name("test_entity_type")
720 .expect("should be a valid identifier")
721 .into(),
722 Eid("foo".into()),
723 None,
724 );
725 let e3 = EntityUID::from_components(
726 Name::parse_unqualified_name("Unspecified")
727 .expect("should be a valid identifier")
728 .into(),
729 Eid("foo".into()),
730 None,
731 );
732
733 assert_eq!(e1, e1);
735 assert_eq!(e2, e2);
736
737 assert_eq!(e1, e2);
739
740 assert!(e1 != e3);
742 }
743
744 #[test]
745 fn action_checker() {
746 let euid = EntityUID::from_str("Action::\"view\"").unwrap();
747 assert!(euid.is_action());
748 let euid = EntityUID::from_str("Foo::Action::\"view\"").unwrap();
749 assert!(euid.is_action());
750 let euid = EntityUID::from_str("Foo::\"view\"").unwrap();
751 assert!(!euid.is_action());
752 let euid = EntityUID::from_str("Action::Foo::\"view\"").unwrap();
753 assert!(!euid.is_action());
754 }
755
756 #[test]
757 fn action_type_is_valid_id() {
758 assert!(Id::from_normalized_str(ACTION_ENTITY_TYPE).is_ok());
759 }
760}