1use crate::ast::*;
18use crate::parser::err::ParseError;
19use crate::transitive_closure::TCNode;
20use itertools::Itertools;
21use serde::{Deserialize, Serialize};
22use smol_str::SmolStr;
23use std::collections::{HashMap, HashSet};
24
25#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
29#[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))]
30pub enum EntityType {
31 Concrete(Name),
33 Unspecified,
35}
36
37impl std::fmt::Display for EntityType {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 Self::Unspecified => write!(f, "<Unspecified>"),
43 Self::Concrete(name) => write!(f, "{}", name),
44 }
45 }
46}
47
48#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
50#[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))]
51pub struct EntityUID {
52 ty: EntityType,
54 eid: Eid,
56}
57
58impl StaticallyTyped for EntityUID {
59 fn type_of(&self) -> Type {
60 Type::Entity {
61 ty: self.ty.clone(),
62 }
63 }
64}
65
66impl EntityUID {
67 #[cfg(test)]
70 pub(crate) fn with_eid(eid: &str) -> Self {
71 Self {
72 ty: Self::test_entity_type(),
73 eid: Eid(eid.into()),
74 }
75 }
76 #[cfg(test)]
84 pub(crate) fn test_entity_type() -> EntityType {
85 let name = Name::parse_unqualified_name("test_entity_type")
86 .expect("test_entity_type should be a valid identifier");
87 EntityType::Concrete(name)
88 }
89 pub fn with_eid_and_type(typename: &str, eid: &str) -> Result<Self, Vec<ParseError>> {
97 Ok(Self {
98 ty: EntityType::Concrete(Name::parse_unqualified_name(typename)?),
99 eid: Eid(eid.into()),
100 })
101 }
102
103 pub fn components(self) -> (EntityType, Eid) {
106 (self.ty, self.eid)
107 }
108
109 pub fn from_components(name: Name, eid: Eid) -> Self {
111 Self {
112 ty: EntityType::Concrete(name),
113 eid,
114 }
115 }
116
117 pub fn unspecified_from_eid(eid: Eid) -> Self {
119 Self {
120 ty: EntityType::Unspecified,
121 eid,
122 }
123 }
124
125 pub fn entity_type(&self) -> &EntityType {
127 &self.ty
128 }
129
130 pub fn eid(&self) -> &Eid {
132 &self.eid
133 }
134}
135
136impl std::fmt::Display for EntityUID {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 write!(f, "{}::\"{}\"", self.entity_type(), self.eid)
139 }
140}
141
142impl std::str::FromStr for EntityUID {
144 type Err = Vec<ParseError>;
145
146 fn from_str(s: &str) -> Result<Self, Vec<ParseError>> {
147 crate::parser::parse_euid(s)
148 }
149}
150
151#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
153pub struct Eid(SmolStr);
154
155impl Eid {
156 pub fn new(eid: impl Into<SmolStr>) -> Self {
158 Eid(eid.into())
159 }
160}
161
162impl AsRef<SmolStr> for Eid {
163 fn as_ref(&self) -> &SmolStr {
164 &self.0
165 }
166}
167
168impl AsRef<str> for Eid {
169 fn as_ref(&self) -> &str {
170 &self.0
171 }
172}
173
174#[cfg(fuzzing)]
175impl<'a> arbitrary::Arbitrary<'a> for Eid {
176 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
177 let x: String = u.arbitrary()?;
178 Ok(Self(x.into()))
179 }
180}
181
182impl std::fmt::Display for Eid {
183 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184 write!(f, "{}", self.0.escape_debug())
185 }
186}
187
188#[derive(Serialize, Deserialize, Debug, Clone)]
190pub struct Entity {
191 uid: EntityUID,
193
194 attrs: HashMap<SmolStr, RestrictedExpr>,
199
200 ancestors: HashSet<EntityUID>,
203}
204
205impl Entity {
206 pub fn new(
208 uid: EntityUID,
209 attrs: HashMap<SmolStr, RestrictedExpr>,
210 ancestors: HashSet<EntityUID>,
211 ) -> Self {
212 Entity {
213 uid,
214 attrs,
215 ancestors,
216 }
217 }
218
219 pub fn uid(&self) -> EntityUID {
221 self.uid.clone()
222 }
223
224 pub fn get(&self, attr: &str) -> Option<&RestrictedExpr> {
226 self.attrs.get(attr)
227 }
228
229 pub fn is_descendant_of(&self, e: &EntityUID) -> bool {
231 self.ancestors.contains(e)
232 }
233
234 pub fn ancestors(&self) -> impl Iterator<Item = &EntityUID> {
236 self.ancestors.iter()
237 }
238
239 pub fn with_uid(uid: EntityUID) -> Self {
241 Self {
242 uid,
243 attrs: HashMap::new(),
244 ancestors: HashSet::new(),
245 }
246 }
247
248 pub(crate) fn attrs(&self) -> &HashMap<SmolStr, RestrictedExpr> {
251 &self.attrs
252 }
253
254 #[cfg(any(test, fuzzing))]
257 pub fn set_attr(&mut self, attr: SmolStr, val: RestrictedExpr) {
258 self.attrs.insert(attr, val);
259 }
260
261 #[cfg(not(fuzzing))]
264 pub(crate) fn add_ancestor(&mut self, uid: EntityUID) {
265 self.ancestors.insert(uid);
266 }
267 #[cfg(fuzzing)]
269 pub fn add_ancestor(&mut self, uid: EntityUID) {
270 self.ancestors.insert(uid);
271 }
272}
273
274impl PartialEq for Entity {
275 fn eq(&self, other: &Self) -> bool {
276 self.uid() == other.uid()
277 }
278}
279
280impl Eq for Entity {}
281
282impl StaticallyTyped for Entity {
283 fn type_of(&self) -> Type {
284 self.uid.type_of()
285 }
286}
287
288impl TCNode<EntityUID> for Entity {
289 fn get_key(&self) -> EntityUID {
290 self.uid()
291 }
292
293 fn add_edge_to(&mut self, k: EntityUID) {
294 self.add_ancestor(k)
295 }
296
297 fn out_edges(&self) -> Box<dyn Iterator<Item = &EntityUID> + '_> {
298 Box::new(self.ancestors())
299 }
300
301 fn has_edge_to(&self, e: &EntityUID) -> bool {
302 self.is_descendant_of(e)
303 }
304}
305
306impl std::fmt::Display for Entity {
307 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
308 write!(
309 f,
310 "{}:\n attrs:{}\n ancestors:{}",
311 self.uid,
312 self.attrs
313 .iter()
314 .map(|(k, v)| format!("{}: {}", k, v))
315 .join("; "),
316 self.ancestors.iter().join(", ")
317 )
318 }
319}
320
321#[cfg(test)]
322mod test {
323 use super::*;
324
325 #[test]
326 fn display() {
327 let e = EntityUID::with_eid("eid");
328 assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
329 }
330
331 #[test]
332 fn test_euid_equality() {
333 let e1 = EntityUID::with_eid("foo");
334 let e2 = EntityUID::from_components(
335 Name::parse_unqualified_name("test_entity_type").expect("should be a valid identifier"),
336 Eid("foo".into()),
337 );
338 let e3 = EntityUID::unspecified_from_eid(Eid("foo".into()));
339 let e4 = EntityUID::unspecified_from_eid(Eid("bar".into()));
340 let e5 = EntityUID::from_components(
341 Name::parse_unqualified_name("Unspecified").expect("should be a valid identifier"),
342 Eid("foo".into()),
343 );
344
345 assert_eq!(e1, e1);
347 assert_eq!(e2, e2);
348 assert_eq!(e3, e3);
349
350 assert_eq!(e1, e2);
352
353 assert!(e1 != e3);
355 assert!(e1 != e4);
356 assert!(e1 != e5);
357 assert!(e3 != e4);
358 assert!(e3 != e5);
359 assert!(e4 != e5);
360
361 assert!(format!("{e3}") != format!("{e5}"));
363 }
364}