rtlola_interpreter/configuration/
config_builder.rs

1use std::error::Error;
2use std::marker::PhantomData;
3use std::path::PathBuf;
4use std::time::SystemTime;
5
6use rtlola_frontend::mir::RtLolaMir;
7
8use crate::config::{Config, ExecutionMode, MonitorConfig, OfflineMode, OnlineMode};
9use crate::configuration::time::{OutputTimeRepresentation, RelativeFloat, TimeRepresentation};
10use crate::input::{ArrayFactory, EventFactory, EventFactoryError, InputMap, MappedFactory};
11use crate::monitor::{NoTracer, Tracer, TracingVerdict, VerdictRepresentation};
12#[cfg(feature = "queued-api")]
13use crate::QueuedMonitor;
14use crate::{CondDeserialize, CondSerialize, Monitor, Value};
15
16/* Type state of shared config */
17/// Represents a state of the [ConfigBuilder]
18/// Used to ensure that only valid configurations can be created
19pub trait ConfigState {}
20
21/// The config state in which the specification has yet to be configured
22#[derive(Debug, Clone, Default, Copy)]
23pub struct ConfigureIR {}
24impl ConfigState for ConfigureIR {}
25
26/// The config state in which the specification is configured
27#[derive(Debug, Clone)]
28pub struct IrConfigured {
29    ir: RtLolaMir,
30}
31impl ConfigState for IrConfigured {}
32
33/// The config state in which the specification is configured
34#[derive(Debug, Clone)]
35pub struct ModeConfigured<Mode: ExecutionMode> {
36    ir: RtLolaMir,
37    mode: Mode,
38}
39impl<Mode: ExecutionMode> ConfigState for ModeConfigured<Mode> {}
40
41/// An API configuration state in which the input source is configured but the input time is not.
42#[derive(Debug, Clone)]
43pub struct InputConfigured<Mode: ExecutionMode, Source: EventFactory> {
44    ir: RtLolaMir,
45    mode: Mode,
46    source: PhantomData<Source>,
47}
48impl<Source: EventFactory, Mode: ExecutionMode> ConfigState for InputConfigured<Mode, Source> {}
49
50/// An API configuration state in which the input source is configured but the input time is not.
51#[derive(Debug, Clone)]
52pub struct VerdictConfigured<
53    Mode: ExecutionMode,
54    Source: EventFactory,
55    Verdict: VerdictRepresentation,
56> {
57    ir: RtLolaMir,
58    mode: Mode,
59    source: PhantomData<Source>,
60    verdict: PhantomData<Verdict>,
61}
62impl<Source: EventFactory, Verdict: VerdictRepresentation, Mode: ExecutionMode> ConfigState
63    for VerdictConfigured<Mode, Source, Verdict>
64{
65}
66
67/// The main entry point of the application.
68/// Use the various methods to construct a configuration either for running the interpreter directly or to use the [Monitor] API interface.
69///
70/// An example construction of the API:
71/// ````
72/// use std::convert::Infallible;
73/// use rtlola_interpreter::monitor::Incremental;
74/// use rtlola_interpreter::input::ArrayFactory;
75/// use rtlola_interpreter::time::RelativeFloat;
76/// use rtlola_interpreter::{ConfigBuilder, Monitor, Value};
77///
78/// let monitor: Monitor<_, _, Incremental, _> = ConfigBuilder::new()
79///     .spec_str("input i: Int64")
80///     .offline::<RelativeFloat>()
81///     .with_array_events::<1, Infallible, [Value; 1]>()
82///     .with_verdict::<Incremental>()
83///     .monitor().expect("Failed to create monitor.");
84/// ````
85#[derive(Debug, Clone)]
86pub struct ConfigBuilder<S: ConfigState, OutputTime: OutputTimeRepresentation> {
87    /// Which format to use to output time
88    output_time_representation: PhantomData<OutputTime>,
89    /// The start time to assume
90    start_time: Option<SystemTime>,
91    /// The current state of the config
92    state: S,
93}
94
95impl ConfigBuilder<ConfigureIR, RelativeFloat> {
96    /// Creates a new configuration to be used with the API.
97    pub fn new() -> Self {
98        ConfigBuilder {
99            output_time_representation: PhantomData,
100            start_time: None,
101            state: ConfigureIR {},
102        }
103    }
104}
105
106impl Default for ConfigBuilder<ConfigureIR, RelativeFloat> {
107    fn default() -> Self {
108        Self::new()
109    }
110}
111
112impl<S: ConfigState, OutputTime: OutputTimeRepresentation> ConfigBuilder<S, OutputTime> {
113    /// Sets the format in which time is returned.
114    /// See the README for more details on time formats.
115    /// For possible formats see the [OutputTimeRepresentation](crate::time::OutputTimeRepresentation) trait.
116    pub fn output_time<T: OutputTimeRepresentation>(self) -> ConfigBuilder<S, T> {
117        let ConfigBuilder {
118            output_time_representation: _,
119            start_time,
120            state,
121        } = self;
122        ConfigBuilder {
123            output_time_representation: PhantomData,
124            start_time,
125            state,
126        }
127    }
128
129    /// Sets the start time of the execution.
130    pub fn start_time(mut self, time: SystemTime) -> Self {
131        self.start_time = Some(time);
132        self
133    }
134}
135
136impl<OutputTime: OutputTimeRepresentation> ConfigBuilder<ConfigureIR, OutputTime> {
137    /// Use an existing ir with the configuration
138    pub fn with_ir(self, ir: RtLolaMir) -> ConfigBuilder<IrConfigured, OutputTime> {
139        let ConfigBuilder {
140            output_time_representation,
141            start_time,
142            state: _,
143        } = self;
144        ConfigBuilder {
145            output_time_representation,
146            start_time,
147            state: IrConfigured { ir },
148        }
149    }
150
151    /// Read the specification from a file at the given path.
152    pub fn spec_file(self, path: PathBuf) -> ConfigBuilder<IrConfigured, OutputTime> {
153        let ConfigBuilder {
154            output_time_representation,
155            start_time,
156            state: _,
157        } = self;
158        let config = rtlola_frontend::ParserConfig::from_path(path).unwrap_or_else(|e| {
159            eprintln!("{}", e);
160            std::process::exit(1)
161        });
162        let handler = rtlola_frontend::Handler::from(&config);
163        let ir = rtlola_frontend::parse(&config).unwrap_or_else(|e| {
164            handler.emit_error(&e);
165            std::process::exit(1);
166        });
167        ConfigBuilder {
168            output_time_representation,
169            start_time,
170            state: IrConfigured { ir },
171        }
172    }
173
174    /// Read the specification from the given string.
175    pub fn spec_str(self, spec: &str) -> ConfigBuilder<IrConfigured, OutputTime> {
176        let ConfigBuilder {
177            output_time_representation,
178            start_time,
179            state: _,
180        } = self;
181        let config = rtlola_frontend::ParserConfig::for_string(spec.to_string());
182        let handler = rtlola_frontend::Handler::from(&config);
183        let ir = rtlola_frontend::parse(&config).unwrap_or_else(|e| {
184            handler.emit_error(&e);
185            std::process::exit(1);
186        });
187        ConfigBuilder {
188            output_time_representation,
189            start_time,
190            state: IrConfigured { ir },
191        }
192    }
193}
194
195impl<OutputTime: OutputTimeRepresentation> ConfigBuilder<IrConfigured, OutputTime> {
196    /// Sets the execute mode to be online, i.e. the time of events is taken by the interpreter.
197    pub fn online(self) -> ConfigBuilder<ModeConfigured<OnlineMode>, OutputTime> {
198        let ConfigBuilder {
199            output_time_representation,
200            start_time,
201            state: IrConfigured { ir },
202        } = self;
203
204        ConfigBuilder {
205            output_time_representation,
206            start_time,
207            state: ModeConfigured {
208                ir,
209                mode: OnlineMode::default(),
210            },
211        }
212    }
213
214    /// Sets the execute mode to be offline, i.e. takes the time of events from the input source.
215    /// How the input timestamps are interpreted is defined by the type parameter.
216    /// See the README for further details on timestamp representations.
217    /// For possible [TimeRepresentation]s see the [Time](crate::time) Module.
218    pub fn offline<InputTime: TimeRepresentation>(
219        self,
220    ) -> ConfigBuilder<ModeConfigured<OfflineMode<InputTime>>, OutputTime> {
221        let ConfigBuilder {
222            output_time_representation,
223            start_time,
224            state: IrConfigured { ir },
225        } = self;
226
227        ConfigBuilder {
228            output_time_representation,
229            start_time,
230            state: ModeConfigured {
231                ir,
232                mode: OfflineMode::default(),
233            },
234        }
235    }
236}
237
238impl<Mode: ExecutionMode, OutputTime: OutputTimeRepresentation>
239    ConfigBuilder<ModeConfigured<Mode>, OutputTime>
240{
241    /// Use the predefined [ArrayFactory] method to provide inputs to the API.
242    pub fn with_array_events<
243        const N: usize,
244        I: Error + Send + 'static,
245        E: TryInto<[Value; N], Error = I> + CondSerialize + CondDeserialize + Send,
246    >(
247        self,
248    ) -> ConfigBuilder<InputConfigured<Mode, ArrayFactory<N, I, E>>, OutputTime> {
249        let ConfigBuilder {
250            output_time_representation,
251            start_time,
252            state: ModeConfigured { ir, mode },
253        } = self;
254
255        ConfigBuilder {
256            output_time_representation,
257            start_time,
258            state: InputConfigured {
259                ir,
260                mode,
261                source: PhantomData,
262            },
263        }
264    }
265
266    /// Use the predefined [MappedFactory] method to provide inputs to the API.
267    /// Requires implementing [InputMap] for your type defining how the values of input streams are extracted from it.
268    pub fn with_mapped_events<Inner: InputMap>(
269        self,
270    ) -> ConfigBuilder<InputConfigured<Mode, MappedFactory<Inner>>, OutputTime> {
271        let ConfigBuilder {
272            output_time_representation,
273            start_time,
274            state: ModeConfigured { ir, mode },
275        } = self;
276
277        ConfigBuilder {
278            output_time_representation,
279            start_time,
280            state: InputConfigured {
281                ir,
282                mode,
283                source: PhantomData,
284            },
285        }
286    }
287
288    /// Use a custom input method to provide inputs to the API.
289    pub fn with_event_factory<Source: EventFactory>(
290        self,
291    ) -> ConfigBuilder<InputConfigured<Mode, Source>, OutputTime> {
292        let ConfigBuilder {
293            output_time_representation,
294            start_time,
295            state: ModeConfigured { ir, mode },
296        } = self;
297
298        ConfigBuilder {
299            output_time_representation,
300            start_time,
301            state: InputConfigured {
302                ir,
303                mode,
304                source: PhantomData,
305            },
306        }
307    }
308}
309
310impl<Mode: ExecutionMode, OutputTime: OutputTimeRepresentation, Source: EventFactory>
311    ConfigBuilder<InputConfigured<Mode, Source>, OutputTime>
312{
313    /// Sets the [VerdictRepresentation] for the monitor
314    pub fn with_verdict<Verdict: VerdictRepresentation>(
315        self,
316    ) -> ConfigBuilder<VerdictConfigured<Mode, Source, Verdict>, OutputTime> {
317        let ConfigBuilder {
318            output_time_representation,
319            start_time,
320            state: InputConfigured { ir, mode, source },
321        } = self;
322        ConfigBuilder {
323            output_time_representation,
324            start_time,
325            state: VerdictConfigured {
326                ir,
327                mode,
328                source,
329                verdict: Default::default(),
330            },
331        }
332    }
333}
334
335impl<
336        Source: EventFactory + 'static,
337        Mode: ExecutionMode,
338        Verdict: VerdictRepresentation<Tracing = NoTracer>,
339        OutputTime: OutputTimeRepresentation,
340    > ConfigBuilder<VerdictConfigured<Mode, Source, Verdict>, OutputTime>
341{
342    /// Adds tracing functionality to the evaluator
343    pub fn with_tracer<T: Tracer>(
344        self,
345    ) -> ConfigBuilder<VerdictConfigured<Mode, Source, TracingVerdict<T, Verdict>>, OutputTime>
346    {
347        let ConfigBuilder {
348            output_time_representation,
349            start_time,
350            state:
351                VerdictConfigured {
352                    ir,
353                    mode,
354                    source,
355                    verdict: _,
356                },
357        } = self;
358        ConfigBuilder {
359            output_time_representation,
360            start_time,
361            state: VerdictConfigured {
362                ir,
363                mode,
364                source,
365                verdict: Default::default(),
366            },
367        }
368    }
369}
370
371impl<
372        Source: EventFactory + 'static,
373        Mode: ExecutionMode,
374        Verdict: VerdictRepresentation,
375        OutputTime: OutputTimeRepresentation,
376    > ConfigBuilder<VerdictConfigured<Mode, Source, Verdict>, OutputTime>
377{
378    /// Finalize the configuration and generate a configuration.
379    pub fn build(self) -> MonitorConfig<Source, Mode, Verdict, OutputTime> {
380        let ConfigBuilder {
381            output_time_representation,
382            start_time,
383            state: VerdictConfigured { ir, mode, .. },
384        } = self;
385        let config = Config {
386            ir,
387            mode,
388            output_time_representation,
389            start_time,
390        };
391        MonitorConfig::new(config)
392    }
393
394    /// Create a [Monitor] from the configuration. The entrypoint of the API. The data is provided to the [Input](crate::input::EventFactory) source at creation.
395    pub fn monitor_with_data(
396        self,
397        data: Source::CreationData,
398    ) -> Result<Monitor<Source, Mode, Verdict, OutputTime>, EventFactoryError> {
399        self.build().monitor_with_data(data)
400    }
401
402    /// Create a [Monitor] from the configuration. The entrypoint of the API.
403    pub fn monitor(self) -> Result<Monitor<Source, Mode, Verdict, OutputTime>, EventFactoryError>
404    where
405        Source: EventFactory<CreationData = ()> + 'static,
406    {
407        self.build().monitor()
408    }
409}
410
411impl<
412        Source: EventFactory + 'static,
413        SourceTime: TimeRepresentation,
414        Verdict: VerdictRepresentation,
415        OutputTime: OutputTimeRepresentation,
416    > ConfigBuilder<VerdictConfigured<OfflineMode<SourceTime>, Source, Verdict>, OutputTime>
417{
418    #[cfg(feature = "queued-api")]
419    /// Create a [QueuedMonitor] from the configuration. The entrypoint of the API. The data is provided to the [Input](crate::input::EventFactory) source at creation.
420    pub fn queued_monitor_with_data(
421        self,
422        data: Source::CreationData,
423    ) -> QueuedMonitor<Source, OfflineMode<SourceTime>, Verdict, OutputTime> {
424        self.build().queued_monitor_with_data(data)
425    }
426
427    #[cfg(feature = "queued-api")]
428    /// Create a [QueuedMonitor] from the configuration. The entrypoint of the API.
429    pub fn queued_monitor(
430        self,
431    ) -> QueuedMonitor<Source, OfflineMode<SourceTime>, Verdict, OutputTime>
432    where
433        Source: EventFactory<CreationData = ()> + 'static,
434    {
435        self.build().queued_monitor()
436    }
437}
438
439impl<
440        Source: EventFactory + 'static,
441        Verdict: VerdictRepresentation,
442        OutputTime: OutputTimeRepresentation,
443    > ConfigBuilder<VerdictConfigured<OnlineMode, Source, Verdict>, OutputTime>
444{
445    #[cfg(feature = "queued-api")]
446    /// Create a [QueuedMonitor] from the configuration. The entrypoint of the API. The data is provided to the [Input](crate::input::EventFactory) source at creation.
447    pub fn queued_monitor_with_data(
448        self,
449        data: Source::CreationData,
450    ) -> QueuedMonitor<Source, OnlineMode, Verdict, OutputTime> {
451        self.build().queued_monitor_with_data(data)
452    }
453
454    #[cfg(feature = "queued-api")]
455    /// Create a [QueuedMonitor] from the configuration. The entrypoint of the API.
456    pub fn queued_monitor(self) -> QueuedMonitor<Source, OnlineMode, Verdict, OutputTime>
457    where
458        Source: EventFactory<CreationData = ()> + 'static,
459    {
460        self.build().queued_monitor()
461    }
462}