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}