use crate::ast::*;
use crate::parser::err::ParseErrors;
use crate::transitive_closure::TCNode;
use crate::FromNormalizedStr;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use std::collections::{HashMap, HashSet};
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum EntityType {
Concrete(Name),
Unspecified,
}
impl EntityType {
pub fn is_action(&self) -> bool {
match self {
Self::Concrete(name) => name.basename() == &Id::new_unchecked("Action"),
Self::Unspecified => false,
}
}
}
impl std::fmt::Display for EntityType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unspecified => write!(f, "<Unspecified>"),
Self::Concrete(name) => write!(f, "{}", name),
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct EntityUID {
ty: EntityType,
eid: Eid,
}
impl StaticallyTyped for EntityUID {
fn type_of(&self) -> Type {
Type::Entity {
ty: self.ty.clone(),
}
}
}
impl EntityUID {
#[cfg(test)]
pub(crate) fn with_eid(eid: &str) -> Self {
Self {
ty: Self::test_entity_type(),
eid: Eid(eid.into()),
}
}
#[cfg(test)]
pub(crate) fn test_entity_type() -> EntityType {
let name = Name::parse_unqualified_name("test_entity_type")
.expect("test_entity_type should be a valid identifier");
EntityType::Concrete(name)
}
pub fn with_eid_and_type(typename: &str, eid: &str) -> Result<Self, ParseErrors> {
Ok(Self {
ty: EntityType::Concrete(Name::parse_unqualified_name(typename)?),
eid: Eid(eid.into()),
})
}
pub fn components(self) -> (EntityType, Eid) {
(self.ty, self.eid)
}
pub fn from_components(name: Name, eid: Eid) -> Self {
Self {
ty: EntityType::Concrete(name),
eid,
}
}
pub fn unspecified_from_eid(eid: Eid) -> Self {
Self {
ty: EntityType::Unspecified,
eid,
}
}
pub fn entity_type(&self) -> &EntityType {
&self.ty
}
pub fn eid(&self) -> &Eid {
&self.eid
}
pub fn is_action(&self) -> bool {
self.entity_type().is_action()
}
}
impl std::fmt::Display for EntityUID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}::\"{}\"", self.entity_type(), self.eid)
}
}
impl std::str::FromStr for EntityUID {
type Err = ParseErrors;
fn from_str(s: &str) -> Result<Self, Self::Err> {
crate::parser::parse_euid(s)
}
}
impl FromNormalizedStr for EntityUID {
fn describe_self() -> &'static str {
"Entity UID"
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Hash, PartialOrd, Ord)]
pub struct Eid(SmolStr);
impl Eid {
pub fn new(eid: impl Into<SmolStr>) -> Self {
Eid(eid.into())
}
}
impl AsRef<SmolStr> for Eid {
fn as_ref(&self) -> &SmolStr {
&self.0
}
}
impl AsRef<str> for Eid {
fn as_ref(&self) -> &str {
&self.0
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for Eid {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let x: String = u.arbitrary()?;
Ok(Self(x.into()))
}
}
impl std::fmt::Display for Eid {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.escape_debug())
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Entity {
uid: EntityUID,
attrs: HashMap<SmolStr, RestrictedExpr>,
ancestors: HashSet<EntityUID>,
}
impl Entity {
pub fn new(
uid: EntityUID,
attrs: HashMap<SmolStr, RestrictedExpr>,
ancestors: HashSet<EntityUID>,
) -> Self {
Entity {
uid,
attrs,
ancestors,
}
}
pub fn uid(&self) -> EntityUID {
self.uid.clone()
}
pub fn get(&self, attr: &str) -> Option<&RestrictedExpr> {
self.attrs.get(attr)
}
pub fn is_descendant_of(&self, e: &EntityUID) -> bool {
self.ancestors.contains(e)
}
pub fn ancestors(&self) -> impl Iterator<Item = &EntityUID> {
self.ancestors.iter()
}
pub fn with_uid(uid: EntityUID) -> Self {
Self {
uid,
attrs: HashMap::new(),
ancestors: HashSet::new(),
}
}
pub(crate) fn attrs(&self) -> &HashMap<SmolStr, RestrictedExpr> {
&self.attrs
}
pub(crate) fn ancestors_set(&self) -> &HashSet<EntityUID> {
&self.ancestors
}
#[cfg(any(test, fuzzing))]
pub fn set_attr(&mut self, attr: SmolStr, val: RestrictedExpr) {
self.attrs.insert(attr, val);
}
#[cfg(not(fuzzing))]
pub(crate) fn add_ancestor(&mut self, uid: EntityUID) {
self.ancestors.insert(uid);
}
#[cfg(fuzzing)]
pub fn add_ancestor(&mut self, uid: EntityUID) {
self.ancestors.insert(uid);
}
}
impl PartialEq for Entity {
fn eq(&self, other: &Self) -> bool {
self.uid() == other.uid()
}
}
impl Eq for Entity {}
impl StaticallyTyped for Entity {
fn type_of(&self) -> Type {
self.uid.type_of()
}
}
impl TCNode<EntityUID> for Entity {
fn get_key(&self) -> EntityUID {
self.uid()
}
fn add_edge_to(&mut self, k: EntityUID) {
self.add_ancestor(k)
}
fn out_edges(&self) -> Box<dyn Iterator<Item = &EntityUID> + '_> {
Box::new(self.ancestors())
}
fn has_edge_to(&self, e: &EntityUID) -> bool {
self.is_descendant_of(e)
}
}
impl std::fmt::Display for Entity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}:\n attrs:{}\n ancestors:{}",
self.uid,
self.attrs
.iter()
.map(|(k, v)| format!("{}: {}", k, v))
.join("; "),
self.ancestors.iter().join(", ")
)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn display() {
let e = EntityUID::with_eid("eid");
assert_eq!(format!("{e}"), "test_entity_type::\"eid\"");
}
#[test]
fn test_euid_equality() {
let e1 = EntityUID::with_eid("foo");
let e2 = EntityUID::from_components(
Name::parse_unqualified_name("test_entity_type").expect("should be a valid identifier"),
Eid("foo".into()),
);
let e3 = EntityUID::unspecified_from_eid(Eid("foo".into()));
let e4 = EntityUID::unspecified_from_eid(Eid("bar".into()));
let e5 = EntityUID::from_components(
Name::parse_unqualified_name("Unspecified").expect("should be a valid identifier"),
Eid("foo".into()),
);
assert_eq!(e1, e1);
assert_eq!(e2, e2);
assert_eq!(e3, e3);
assert_eq!(e1, e2);
assert!(e1 != e3);
assert!(e1 != e4);
assert!(e1 != e5);
assert!(e3 != e4);
assert!(e3 != e5);
assert!(e4 != e5);
assert!(format!("{e3}") != format!("{e5}"));
}
}