Skip to main content

a3s_code_core/state_graph/
behavior.rs

1use super::{GraphEvent, GraphEventRecord, GraphPatch, StateGraph};
2use std::fmt;
3use std::sync::Arc;
4use thiserror::Error;
5
6type EventPredicate = dyn Fn(&GraphEventRecord, &StateGraph) -> bool + Send + Sync;
7
8#[derive(Clone, Default)]
9pub struct EventFilter {
10    event_types: Vec<String>,
11    object_types: Vec<String>,
12    relation_types: Vec<String>,
13    predicate: Option<Arc<EventPredicate>>,
14}
15
16impl EventFilter {
17    pub fn new(event_types: impl IntoIterator<Item = impl Into<String>>) -> Self {
18        Self {
19            event_types: event_types.into_iter().map(Into::into).collect(),
20            ..Self::default()
21        }
22    }
23
24    pub fn with_object_types(
25        mut self,
26        object_types: impl IntoIterator<Item = impl Into<String>>,
27    ) -> Self {
28        self.object_types = object_types.into_iter().map(Into::into).collect();
29        self
30    }
31
32    pub fn with_relation_types(
33        mut self,
34        relation_types: impl IntoIterator<Item = impl Into<String>>,
35    ) -> Self {
36        self.relation_types = relation_types.into_iter().map(Into::into).collect();
37        self
38    }
39
40    pub fn where_predicate(
41        mut self,
42        predicate: impl Fn(&GraphEventRecord, &StateGraph) -> bool + Send + Sync + 'static,
43    ) -> Self {
44        self.predicate = Some(Arc::new(predicate));
45        self
46    }
47
48    pub fn matches(&self, event: &GraphEventRecord, graph: &StateGraph) -> bool {
49        let event_type_matches = self.event_types.is_empty()
50            || self
51                .event_types
52                .iter()
53                .any(|candidate| candidate == event.event.event_type());
54        let object_type_matches = self.object_types.is_empty()
55            || event_object_type(event, graph).is_some_and(|object_type| {
56                self.object_types
57                    .iter()
58                    .any(|candidate| candidate == object_type)
59            });
60        let relation_type_matches = self.relation_types.is_empty()
61            || event_relation_type(event, graph).is_some_and(|relation_type| {
62                self.relation_types
63                    .iter()
64                    .any(|candidate| candidate == relation_type)
65            });
66        event_type_matches
67            && object_type_matches
68            && relation_type_matches
69            && self
70                .predicate
71                .as_ref()
72                .is_none_or(|predicate| predicate(event, graph))
73    }
74}
75
76impl fmt::Debug for EventFilter {
77    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78        formatter
79            .debug_struct("EventFilter")
80            .field("event_types", &self.event_types)
81            .field("object_types", &self.object_types)
82            .field("relation_types", &self.relation_types)
83            .field("has_predicate", &self.predicate.is_some())
84            .finish()
85    }
86}
87
88fn event_object_type<'a>(event: &'a GraphEventRecord, graph: &'a StateGraph) -> Option<&'a str> {
89    match &event.event {
90        GraphEvent::ObjectCreated { object_type, .. } => Some(object_type),
91        GraphEvent::ObjectUpdated { id, .. } => {
92            graph.object(id).map(|object| object.object_type.as_str())
93        }
94        GraphEvent::ObjectRemoved { .. } => None,
95        _ => None,
96    }
97}
98
99fn event_relation_type<'a>(event: &'a GraphEventRecord, graph: &'a StateGraph) -> Option<&'a str> {
100    match &event.event {
101        GraphEvent::RelationCreated { relation_type, .. } => Some(relation_type),
102        GraphEvent::RelationUpdated { id, .. } => graph
103            .relation(id)
104            .map(|relation| relation.relation_type.as_str()),
105        GraphEvent::RelationRemoved { .. } => None,
106        _ => None,
107    }
108}
109
110pub struct BehaviorContext<'a> {
111    pub graph: &'a StateGraph,
112    pub event: &'a GraphEventRecord,
113}
114
115#[derive(Debug, Clone, Error, PartialEq, Eq)]
116#[error("{message}")]
117pub struct BehaviorError {
118    pub message: String,
119}
120
121impl BehaviorError {
122    pub fn new(message: impl Into<String>) -> Self {
123        Self {
124            message: message.into(),
125        }
126    }
127}
128
129pub trait Behavior: Send + Sync {
130    fn name(&self) -> &str;
131    fn filter(&self) -> &EventFilter;
132    fn evaluate(&self, context: BehaviorContext<'_>) -> Result<Vec<GraphPatch>, BehaviorError>;
133}
134
135type BehaviorFn =
136    dyn for<'a> Fn(BehaviorContext<'a>) -> Result<Vec<GraphPatch>, BehaviorError> + Send + Sync;
137
138pub struct FnBehavior {
139    name: String,
140    filter: EventFilter,
141    evaluator: Arc<BehaviorFn>,
142}
143
144impl FnBehavior {
145    pub fn new(
146        name: impl Into<String>,
147        filter: EventFilter,
148        evaluator: impl for<'a> Fn(BehaviorContext<'a>) -> Result<Vec<GraphPatch>, BehaviorError>
149            + Send
150            + Sync
151            + 'static,
152    ) -> Self {
153        Self {
154            name: name.into(),
155            filter,
156            evaluator: Arc::new(evaluator),
157        }
158    }
159}
160
161impl Behavior for FnBehavior {
162    fn name(&self) -> &str {
163        &self.name
164    }
165
166    fn filter(&self) -> &EventFilter {
167        &self.filter
168    }
169
170    fn evaluate(&self, context: BehaviorContext<'_>) -> Result<Vec<GraphPatch>, BehaviorError> {
171        (self.evaluator)(context)
172    }
173}