windowed_stats/experimental/event/
builder.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::marker::PhantomData;
6
7use crate::experimental::clock::Timed;
8use crate::experimental::event::Event;
9use crate::experimental::event::reactor::{Context, Reactor};
10use crate::experimental::inspect::{InspectSender, InspectedTimeMatrix, TimeMatrixClient};
11use crate::experimental::series::interpolation::InterpolationKind;
12use crate::experimental::series::statistic::{FoldError, Metadata, SerialStatistic, Statistic};
13use crate::experimental::series::{SamplingProfile, TimeMatrix, TimeMatrixFold};
14
15/// A type that maps the presence of an optional builder field to another type.
16pub trait Optional {
17    type Field;
18}
19
20/// An optional builder field that has been set to a value of type `T`.
21#[derive(Clone, Copy, Debug, Default)]
22pub struct Set<T>(PhantomData<fn() -> T>);
23
24impl<T> Optional for Set<T> {
25    type Field = T;
26}
27
28/// An optional builder field that has **not** been set.
29#[derive(Clone, Copy, Debug, Default)]
30pub struct Unset;
31
32impl Optional for Unset {
33    type Field = ();
34}
35
36/// Builds a [`Reactor`] that samples a [data record][`DataEvent::record`] with a [`TimeMatrix`].
37///
38/// See the [`event::sample_data_record`] function.
39///
40/// [`DataEvent::record`]: crate::experimental::event::DataEvent::record
41/// [`event::sample_data_record`]: crate::experimental::event::sample_data_record
42/// [`Reactor`]: crate::experimental::event::Reactor
43/// [`TimeMatrix`]: crate::experimental::series::TimeMatrix
44#[derive(Clone, Copy, Debug)]
45pub struct SampleDataRecord<F, S = (), M = Unset>
46where
47    M: Optional,
48{
49    statistic: F,
50    metadata: M::Field,
51    phantom: PhantomData<fn() -> S>,
52}
53
54impl<F, S, M> SampleDataRecord<F, S, M>
55where
56    M: Optional,
57{
58    fn reactor<T>(
59        matrix: InspectedTimeMatrix<T>,
60    ) -> impl Reactor<T, S, Response = (), Error = FoldError>
61    where
62        T: Clone,
63    {
64        move |event: Timed<Event<T>>, _: Context<'_, S>| {
65            if let Some(sample) = event.to_timed_sample() { matrix.fold_at(sample) } else { Ok(()) }
66        }
67    }
68}
69
70impl<F, S> SampleDataRecord<F, S, Set<Metadata<F>>>
71where
72    F: Statistic,
73{
74    pub fn in_time_matrix<P>(
75        self,
76        client: &TimeMatrixClient,
77        name: impl AsRef<str>,
78        profile: SamplingProfile,
79        interpolation: P::Output<F::Sample>,
80    ) -> impl Reactor<F::Sample, S, Response = (), Error = FoldError>
81    where
82        TimeMatrix<F, P>: 'static + TimeMatrixFold<F::Sample> + Send,
83        Metadata<F>: 'static + Send + Sync,
84        F: SerialStatistic<P>,
85        F::Sample: Send,
86        P: InterpolationKind,
87    {
88        let SampleDataRecord { statistic, metadata, .. } = self;
89        let matrix = client.inspect_time_matrix_with_metadata(
90            name.as_ref(),
91            TimeMatrix::with_statistic(profile, interpolation, statistic),
92            metadata,
93        );
94        Self::reactor(matrix)
95    }
96}
97
98impl<F, S> SampleDataRecord<F, S, Unset>
99where
100    F: Statistic,
101{
102    /// Builds the [`Reactor`] with the given metadata for the [`TimeMatrix`].
103    ///
104    /// The type of `metadata` is determined by the [`DataSemantic`] of the [`Statistic`]. For
105    /// example, the [`Union`] statistic has [`BitSet`] semantics and so requires types convertible
106    /// into the [`BitSetIndex`] metadata type.
107    ///
108    /// [`BitSet`]: crate::experimental::series::BitSet
109    /// [`BitSetIndex`]: crate::experimental::series::metadata::BitSetIndex
110    /// [`DataSemantic`]: crate::experimental::series::DataSemantic
111    /// [`Reactor`]: crate::experimental::event::Reactor
112    /// [`Statistic`]: crate::experimental::series::statistic::Statistic
113    /// [`TimeMatrix`]: crate::experimental::series::TimeMatrix
114    /// [`Union`]: crate::experimental::series::statistic::Union
115    pub fn with_metadata(
116        self,
117        metadata: impl Into<Metadata<F>>,
118    ) -> SampleDataRecord<F, S, Set<Metadata<F>>> {
119        let SampleDataRecord { statistic, .. } = self;
120        SampleDataRecord { statistic, metadata: metadata.into(), phantom: PhantomData }
121    }
122
123    pub fn in_time_matrix<P>(
124        self,
125        client: &TimeMatrixClient,
126        name: impl AsRef<str>,
127        profile: SamplingProfile,
128        interpolation: P::Output<F::Sample>,
129    ) -> impl Reactor<F::Sample, S, Response = (), Error = FoldError>
130    where
131        TimeMatrix<F, P>: 'static + TimeMatrixFold<F::Sample> + Send,
132        Metadata<F>: 'static + Send + Sync,
133        F: SerialStatistic<P>,
134        F::Sample: Send,
135        P: InterpolationKind,
136    {
137        let SampleDataRecord { statistic, .. } = self;
138        let matrix = client.inspect_time_matrix(
139            name.as_ref(),
140            TimeMatrix::with_statistic(profile, interpolation, statistic),
141        );
142        Self::reactor(matrix)
143    }
144}
145
146/// Constructs a builder for a [`Reactor`] that samples a [data record][`DataEvent::record`] with a
147/// [`TimeMatrix`] using the given [`Statistic`].
148///
149/// [`DataEvent::record`]: crate::experimental::event::DataEvent::record
150/// [`Reactor`]: crate::experimental::event::Reactor
151/// [`Statistic`]: crate::experimental::series::statistic::Statistic
152/// [`TimeMatrix`]: crate::experimental::series::TimeMatrix
153pub fn sample_data_record<S, F>(statistic: F) -> SampleDataRecord<F, S, Unset>
154where
155    F: Statistic,
156{
157    SampleDataRecord { statistic, metadata: (), phantom: PhantomData }
158}