Skip to main content

sc_observability_types/
projection.rs

1use std::sync::Arc;
2
3use crate::{
4    LogEvent, MetricRecord, Observable, Observation, ProjectionError, SpanSignal, SubscriberError,
5};
6
7type SubscriberRegistrationParts<T> = (
8    Arc<dyn ObservationSubscriber<T>>,
9    Option<Arc<dyn ObservationFilter<T>>>,
10);
11
12type ProjectionRegistrationParts<T> = (
13    Option<Arc<dyn LogProjector<T>>>,
14    Option<Arc<dyn SpanProjector<T>>>,
15    Option<Arc<dyn MetricProjector<T>>>,
16    Option<Arc<dyn ObservationFilter<T>>>,
17);
18
19/// Open subscriber contract for typed observations.
20pub trait ObservationSubscriber<T>: Send + Sync
21where
22    T: Observable,
23{
24    /// Consumes one routed observation.
25    ///
26    /// # Errors
27    ///
28    /// Returns [`SubscriberError`] when the subscriber rejects or cannot
29    /// process the observation.
30    fn observe(&self, observation: &Observation<T>) -> Result<(), SubscriberError>;
31}
32
33/// Open filter contract evaluated before subscriber or projector execution.
34pub trait ObservationFilter<T>: Send + Sync
35where
36    T: Observable,
37{
38    /// Returns whether the observation should proceed to the subscriber or projector.
39    fn accepts(&self, observation: &Observation<T>) -> bool;
40}
41
42/// Open projector contract from typed observations into log events.
43pub trait LogProjector<T>: Send + Sync
44where
45    T: Observable,
46{
47    /// Projects one observation into zero or more log events.
48    ///
49    /// # Errors
50    ///
51    /// Returns [`ProjectionError`] when the projector cannot derive log output
52    /// for the supplied observation.
53    fn project_logs(&self, observation: &Observation<T>) -> Result<Vec<LogEvent>, ProjectionError>;
54}
55
56/// Open projector contract from typed observations into span signals.
57pub trait SpanProjector<T>: Send + Sync
58where
59    T: Observable,
60{
61    /// Projects one observation into zero or more span lifecycle signals.
62    ///
63    /// # Errors
64    ///
65    /// Returns [`ProjectionError`] when the projector cannot derive span
66    /// signals for the supplied observation.
67    fn project_spans(
68        &self,
69        observation: &Observation<T>,
70    ) -> Result<Vec<SpanSignal>, ProjectionError>;
71}
72
73/// Open projector contract from typed observations into metric records.
74pub trait MetricProjector<T>: Send + Sync
75where
76    T: Observable,
77{
78    /// Projects one observation into zero or more metric records.
79    ///
80    /// # Errors
81    ///
82    /// Returns [`ProjectionError`] when the projector cannot derive metric
83    /// output for the supplied observation.
84    fn project_metrics(
85        &self,
86        observation: &Observation<T>,
87    ) -> Result<Vec<MetricRecord>, ProjectionError>;
88}
89
90/// Construction-time registration for one typed observation subscriber.
91#[derive(Clone)]
92#[expect(
93    missing_debug_implementations,
94    reason = "public registration wrappers intentionally store trait objects that do not have a stable or useful Debug surface"
95)]
96pub struct SubscriberRegistration<T>
97where
98    T: Observable,
99{
100    /// Registered subscriber implementation.
101    subscriber: Arc<dyn ObservationSubscriber<T>>,
102    /// Optional filter evaluated before subscriber execution.
103    filter: Option<Arc<dyn ObservationFilter<T>>>,
104}
105
106impl<T> SubscriberRegistration<T>
107where
108    T: Observable,
109{
110    /// Creates a subscriber registration with no filter.
111    #[must_use]
112    pub fn new(subscriber: Arc<dyn ObservationSubscriber<T>>) -> Self {
113        Self {
114            subscriber,
115            filter: None,
116        }
117    }
118
119    /// Attaches a filter evaluated before subscriber execution.
120    #[must_use]
121    pub fn with_filter(mut self, filter: Arc<dyn ObservationFilter<T>>) -> Self {
122        self.filter = Some(filter);
123        self
124    }
125
126    /// Splits the registration into its subscriber and optional filter.
127    #[must_use]
128    pub fn into_parts(self) -> SubscriberRegistrationParts<T> {
129        (self.subscriber, self.filter)
130    }
131}
132
133/// Construction-time registration for log/span/metric projection of a payload.
134#[derive(Clone)]
135#[expect(
136    missing_debug_implementations,
137    reason = "public registration wrappers intentionally store trait objects that do not have a stable or useful Debug surface"
138)]
139pub struct ProjectionRegistration<T>
140where
141    T: Observable,
142{
143    /// Optional log projector.
144    log_projector: Option<Arc<dyn LogProjector<T>>>,
145    /// Optional span projector.
146    span_projector: Option<Arc<dyn SpanProjector<T>>>,
147    /// Optional metric projector.
148    metric_projector: Option<Arc<dyn MetricProjector<T>>>,
149    /// Optional filter evaluated before projection.
150    filter: Option<Arc<dyn ObservationFilter<T>>>,
151}
152
153impl<T> ProjectionRegistration<T>
154where
155    T: Observable,
156{
157    /// Creates an empty projection registration ready for projector attachment.
158    #[must_use]
159    pub fn new() -> Self {
160        Self {
161            log_projector: None,
162            span_projector: None,
163            metric_projector: None,
164            filter: None,
165        }
166    }
167
168    /// Attaches a log projector.
169    #[must_use]
170    pub fn with_log_projector(mut self, projector: Arc<dyn LogProjector<T>>) -> Self {
171        self.log_projector = Some(projector);
172        self
173    }
174
175    /// Attaches a span projector.
176    #[must_use]
177    pub fn with_span_projector(mut self, projector: Arc<dyn SpanProjector<T>>) -> Self {
178        self.span_projector = Some(projector);
179        self
180    }
181
182    /// Attaches a metric projector.
183    #[must_use]
184    pub fn with_metric_projector(mut self, projector: Arc<dyn MetricProjector<T>>) -> Self {
185        self.metric_projector = Some(projector);
186        self
187    }
188
189    /// Attaches a filter evaluated before projection.
190    #[must_use]
191    pub fn with_filter(mut self, filter: Arc<dyn ObservationFilter<T>>) -> Self {
192        self.filter = Some(filter);
193        self
194    }
195
196    /// Splits the registration into its projector components and optional filter.
197    #[must_use]
198    pub fn into_parts(self) -> ProjectionRegistrationParts<T> {
199        (
200            self.log_projector,
201            self.span_projector,
202            self.metric_projector,
203            self.filter,
204        )
205    }
206}
207
208impl<T> Default for ProjectionRegistration<T>
209where
210    T: Observable,
211{
212    fn default() -> Self {
213        Self::new()
214    }
215}