1use crate::ast::*;
18use crate::parser::err::ParseError;
19use crate::transitive_closure::TCNode;
20use crate::FromNormalizedStr;
21use itertools::Itertools;
22use serde::{Deserialize, Serialize};
23use smol_str::SmolStr;
24use std::collections::{HashMap, HashSet};
25
26#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
30#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
31pub enum EntityType {
32 Concrete(Name),
34 Unspecified,
36}
37
38impl EntityType {
39 pub fn is_action(&self) -> bool {
41 match self {
42 Self::Concrete(name) => name.basename() == &Id::new_unchecked("Action"),
43 Self::Unspecified => false,
44 }
45 }
46}
47
48impl std::fmt::Display for EntityType {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 match self {
53 Self::Unspecified => write!(f, "<Unspecified>"),
54 Self::Concrete(name) => write!(f, "{}", name),
55 }
56 }
57}
58
59#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
61#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
62pub struct EntityUID {
63 ty: EntityType,
65 eid: Eid,
67}
68
69impl StaticallyTyped for EntityUID {
70 fn type_of(&self) -> Type {
71 Type::Entity {
72 ty: self.ty.clone(),
73 }
74 }
75}
76
77impl EntityUID {
78 #[cfg(test)]
81 pub(crate) fn with_eid(eid: &str) -> Self {
82 Self {
83 ty: Self::test_entity_type(),
84 eid: Eid(eid.into()),
85 }
86 }
87 #[cfg(test)]
95 pub(crate) fn test_entity_type() -> EntityType {
96 let name = Name::parse_unqualified_name("test_entity_type")
97 .expect("test_entity_type should be a valid identifier");
98 EntityType::Concrete(name)
99 }
100 pub fn with_eid_and_type(typename: &str, eid: &str) -> Result<Self, Vec<ParseError>> {
108 Ok(Self {
109 ty: EntityType::Concrete(Name::parse_unqualified_name(typename)?),
110 eid: Eid(eid.into()),
111 })
112 }
113
114 pub fn components(self) -> (EntityType, Eid) {
117 (self.ty, self.eid)
118 }
119
120 pub fn from_components(name: Name, eid: Eid) -> Self {
122 Self {
123 ty: EntityType::Concrete(name),
124 eid,
125 }
126 }
127
128 pub fn unspecified_from_eid(eid: Eid) -> Self {
130 Self {
131 ty: EntityType::Unspecified,
132 eid,
133 }
134 }
135
136 pub fn entity_type(&self) -> &EntityType {
138 &self.ty
139 }
140
141 pub fn eid(&self) -> &Eid {
143 &self.eid
144 }
145
146 pub fn is_action(&self) -> bool {
148 self.entity_type().is_action()
149 }
150}
151
152impl std::fmt::Display for EntityUID {
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 write!(f, "{}::\"{}\"", self.entity_type(), self.eid)
155 }
156}
157
158impl std::str::FromStr for EntityUID {
160 type Err = Vec<ParseError>;
161
162 fn from_str(s: &str) -> Result<Self, Vec<ParseError>> {
163 crate::parser::parse_euid(s)
164 }
165}
166
167impl FromNormalizedStr for EntityUID {
168 fn describe_self() -> &'static str {
169 "Entity UID"
170 }
171}
172
173#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
175pub struct Eid(SmolStr);
176
177impl Eid {
178 pub fn new(eid: impl Into<SmolStr>) -> Self {
180 Eid(eid.into())
181 }
182}
183
184impl AsRef<SmolStr> for Eid {
185 fn as_ref(&self) -> &SmolStr {
186 &self.0
187 }
188}
189
190impl AsRef<str> for Eid {
191 fn as_ref(&self) -> &str {
192 &self.0
193 }
194}
195
196#[cfg(feature = "arbitrary")]
197impl<'a> arbitrary::Arbitrary<'a> for Eid {
198 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
199 let x: String = u.arbitrary()?;
200 Ok(Self(x.into()))
201 }
202}
203
204impl std::fmt::Display for Eid {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 write!(f, "{}", self.0.escape_debug())
207 }
208}
209
210#[derive(Serialize, Deserialize, Debug, Clone)]
212pub struct Entity {
213 uid: EntityUID,
215
216 attrs: HashMap<SmolStr, RestrictedExpr>,
221
222 ancestors: HashSet<EntityUID>,
225}
226
227impl Entity {
228 pub fn new(
230 uid: EntityUID,
231 attrs: HashMap<SmolStr, RestrictedExpr>,
232 ancestors: HashSet<EntityUID>,
233 ) -> Self {
234 Entity {
235 uid,
236 attrs,
237 ancestors,
238 }
239 }
240
241 pub fn uid(&self) -> EntityUID {
243 self.uid.clone()
244 }
245
246 pub fn get(&self, attr: &str) -> Option<&RestrictedExpr> {
248 self.attrs.get(attr)
249 }
250
251 pub fn is_descendant_of(&self, e: &EntityUID) -> bool {
253 self.ancestors.contains(e)
254 }
255
256 pub fn ancestors(&self) -> impl Iterator<Item = &EntityUID> {
258 self.ancestors.iter()
259 }
260
261 pub fn with_uid(uid: EntityUID) -> Self {
263 Self {
264 uid,
265 attrs: HashMap::new(),
266 ancestors: HashSet::new(),
267 }
268 }
269
270 pub(crate) fn attrs(&self) -> &HashMap<SmolStr, RestrictedExpr> {
273 &self.attrs
274 }
275
276 pub(crate) fn ancestors_set(&self) -> &HashSet<EntityUID> {
279 &self.ancestors
280 }
281
282 #[cfg(any(test, fuzzing))]
285 pub fn set_attr(&mut self, attr: SmolStr, val: RestrictedExpr) {
286 self.attrs.insert(attr, val);
287 }
288
289 #[cfg(not(fuzzing))]
292 pub(crate) fn add_ancestor(&mut self, uid: EntityUID) {
293 self.ancestors.insert(uid);
294 }
295 #[cfg(fuzzing)]
297 pub fn add_ancestor(&mut self, uid: EntityUID) {
298 self.ancestors.insert(uid);
299 }
300}
301
302impl PartialEq for Entity {
303 fn eq(&self, other: &Self) -> bool {
304 self.uid() == other.uid()
305 }
306}
307
308impl Eq for Entity {}
309
310impl StaticallyTyped for Entity {
311 fn type_of(&self) -> Type {
312 self.uid.type_of()
313 }
314}
315
316impl TCNode<EntityUID> for Entity {
317 fn get_key(&self) -> EntityUID {
318 self.uid()
319 }
320
321 fn add_edge_to(&mut self, k: EntityUID) {
322 self.add_ancestor(k)
323 }
324
325 fn out_edges(&self) -> Box<dyn Iterator<Item = &EntityUID> + '_> {
326 Box::new(self.ancestors())
327 }
328
329 fn has_edge_to(&self, e: &EntityUID) -> bool {
330 self.is_descendant_of(e)
331 }
332}
333
334impl std::fmt::Display for Entity {
335 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
336 write!(
337 f,
338 "{}:\n attrs:{}\n ancestors:{}",
339 self.uid,
340 self.attrs
341 .iter()
342 .map(|(k, v)| format!("{}: {}", k, v))
343 .join("; "),
344 self.ancestors.iter().join(", ")
345 )
346 }
347}
348
349#[cfg(test)]
350mod test {
351 use super::*;
352
353 #[test]
354 fn display() {
355 let e = EntityUID::with_eid("eid");
356 assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
357 }
358
359 #[test]
360 fn test_euid_equality() {
361 let e1 = EntityUID::with_eid("foo");
362 let e2 = EntityUID::from_components(
363 Name::parse_unqualified_name("test_entity_type").expect("should be a valid identifier"),
364 Eid("foo".into()),
365 );
366 let e3 = EntityUID::unspecified_from_eid(Eid("foo".into()));
367 let e4 = EntityUID::unspecified_from_eid(Eid("bar".into()));
368 let e5 = EntityUID::from_components(
369 Name::parse_unqualified_name("Unspecified").expect("should be a valid identifier"),
370 Eid("foo".into()),
371 );
372
373 assert_eq!(e1, e1);
375 assert_eq!(e2, e2);
376 assert_eq!(e3, e3);
377
378 assert_eq!(e1, e2);
380
381 assert!(e1 != e3);
383 assert!(e1 != e4);
384 assert!(e1 != e5);
385 assert!(e3 != e4);
386 assert!(e3 != e5);
387 assert!(e4 != e5);
388
389 assert!(format!("{e3}") != format!("{e5}"));
391 }
392}