Skip to main content

cognee_core/
sentinels.rs

1//! Control-flow sentinel values that pipeline tasks return to steer the
2//! executor. Sentinels are ordinary [`Value`]s (via the blanket
3//! `impl<T> Value for T` in `task.rs`), so no manual trait impl is needed.
4
5use crate::task::Value;
6
7/// Returned by a task to discard the current item: it is not forwarded to
8/// downstream tasks and does not appear in the pipeline output.
9///
10/// Mirrors Python's `_Drop` sentinel (`cognee/pipelines/types.py`).
11///
12/// # Usage
13///
14/// Return `Ok(Box::new(DroppedSentinel))` from any task to silently discard
15/// the current item without raising an error. The executor filters it out
16/// before forwarding to downstream tasks or including it in the final output.
17///
18/// # Batch tasks
19///
20/// A batch task that wants to drop *individual* items of its slice should
21/// emit an iterator or stream that omits them, or yield `DroppedSentinel`
22/// per item — the executor's iterator/stream path filters those out too.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct DroppedSentinel;
25
26/// Returns `true` if `value` is a [`DroppedSentinel`].
27///
28/// `value` must be the **dereferenced** trait object (`&dyn Value`), **not**
29/// an `Arc<dyn Value>` or `Box<dyn Value>` directly: the blanket
30/// `impl<T: Any + Send + Sync + 'static> Value for T` means `.as_any()` on a
31/// smart pointer downcasts to the pointer type, never the inner value. Pass
32/// `arc.as_ref()` / `boxed.as_ref()`.
33pub fn is_dropped(value: &dyn Value) -> bool {
34    value.as_any().downcast_ref::<DroppedSentinel>().is_some()
35}
36
37/// Returned by an *enriching* task to forward its input unchanged.
38///
39/// Honored only when the task's [`TaskInfo::enriches`](crate::task::TaskInfo)
40/// is `true`; on a non-enriching task it is an error. Mirrors Python's
41/// `enriches` behavior (`cognee/modules/pipelines/tasks/task.py`): an enriching
42/// task that returns `None` passes its input through untouched.
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub struct PassthroughSentinel;
45
46/// True if `value` is a [`PassthroughSentinel`]. See [`is_dropped`] for the
47/// `&dyn Value` (dereference-the-pointer) contract.
48pub fn is_passthrough(value: &dyn Value) -> bool {
49    value
50        .as_any()
51        .downcast_ref::<PassthroughSentinel>()
52        .is_some()
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58    use std::sync::Arc;
59
60    #[test]
61    fn detects_dropped_sentinel() {
62        let v: Arc<dyn Value> = Arc::new(DroppedSentinel);
63        assert!(is_dropped(v.as_ref()));
64    }
65
66    #[test]
67    fn ignores_regular_value() {
68        let v: Arc<dyn Value> = Arc::new(42_usize);
69        assert!(!is_dropped(v.as_ref()));
70    }
71
72    #[test]
73    fn ignores_boxed_regular_value() {
74        let v: Box<dyn Value> = Box::new(99_i32);
75        assert!(!is_dropped(v.as_ref()));
76    }
77
78    #[test]
79    fn detects_boxed_dropped_sentinel() {
80        let v: Box<dyn Value> = Box::new(DroppedSentinel);
81        assert!(is_dropped(v.as_ref()));
82    }
83
84    #[test]
85    fn detects_passthrough_sentinel() {
86        let v: Arc<dyn Value> = Arc::new(PassthroughSentinel);
87        assert!(is_passthrough(v.as_ref()));
88    }
89
90    #[test]
91    fn passthrough_ignores_regular_value() {
92        let v: Arc<dyn Value> = Arc::new(42_usize);
93        assert!(!is_passthrough(v.as_ref()));
94    }
95
96    #[test]
97    fn passthrough_ignores_dropped_sentinel() {
98        let v: Arc<dyn Value> = Arc::new(DroppedSentinel);
99        assert!(!is_passthrough(v.as_ref()));
100    }
101}