Skip to main content

std_mel/engine/
log.rs

1use crate::engine::*;
2use melodium_core::common::executive::Level as LogLevel;
3use melodium_macro::{mel_data, mel_function, mel_treatment};
4
5/// Forward a stream of strings to the engine log at the given `level` under `label`.
6///
7/// Each received string is logged as a separate entry. The treatment continues until the stream closes.
8#[mel_treatment(
9    model engine Engine
10    input messages Stream<string>
11)]
12pub async fn log_stream(level: Level, label: string) {
13    let engine = EngineModel::into(engine);
14
15    while let Ok(msgs) = messages
16        .recv_many()
17        .await
18        .map(|values| TryInto::<Vec<string>>::try_into(values).unwrap())
19    {
20        for msg in msgs {
21            engine
22                .world()
23                .log(level.level, label.clone(), msg, Some(track_id))
24                .await;
25        }
26    }
27}
28
29/// Like `log_stream` but reads `label` as a block input rather than a constant parameter.
30///
31/// Waits for `label` to arrive first, then logs each string in `messages` at `level` under that label.
32#[mel_treatment(
33    model engine Engine
34    input label Block<string>
35    input messages Stream<string>
36)]
37pub async fn log_stream_label(level: Level) {
38    let engine = EngineModel::into(engine);
39
40    if let Ok(label) = label
41        .recv_one()
42        .await
43        .map(|val| GetData::<String>::try_data(val).unwrap())
44    {
45        while let Ok(msgs) = messages
46            .recv_many()
47            .await
48            .map(|values| TryInto::<Vec<string>>::try_into(values).unwrap())
49        {
50            for msg in msgs {
51                engine
52                    .world()
53                    .log(level.level, label.clone(), msg, Some(track_id))
54                    .await;
55            }
56        }
57    }
58}
59
60/// Forward a single string block to the engine log at the given `level` under `label`.
61#[mel_treatment(
62    model engine Engine
63    input message Block<string>
64)]
65pub async fn log_block(level: Level, label: string) {
66    let engine = EngineModel::into(engine);
67
68    if let Ok(msg) = message
69        .recv_one()
70        .await
71        .map(|val| GetData::<string>::try_data(val).unwrap())
72    {
73        engine
74            .world()
75            .log(level.level, label, msg, Some(track_id))
76            .await;
77    }
78}
79
80/// Like `log_block` but reads both `label` and `message` as block inputs.
81///
82/// Waits for `label` first, then logs `message` at `level` under that label.
83#[mel_treatment(
84    model engine Engine
85    input label Block<string>
86    input message Block<string>
87)]
88pub async fn log_block_label(level: Level) {
89    let engine = EngineModel::into(engine);
90
91    if let Ok(label) = label
92        .recv_one()
93        .await
94        .map(|val| GetData::<String>::try_data(val).unwrap())
95    {
96        if let Ok(msg) = message
97            .recv_one()
98            .await
99            .map(|val| GetData::<string>::try_data(val).unwrap())
100        {
101            engine
102                .world()
103                .log(level.level, label, msg, Some(track_id))
104                .await;
105        }
106    }
107}
108
109/// Convert each item in a `Display` stream to its string representation and log it at `level` under `label`.
110#[mel_treatment(
111    model engine Engine
112    input display Stream<D>
113    generic D (Display)
114)]
115pub async fn log_data_stream(level: Level, label: string) {
116    let engine = EngineModel::into(engine);
117
118    while let Ok(values) = display
119        .recv_many()
120        .await
121        .map(|values| Into::<VecDeque<Value>>::into(values))
122    {
123        for val in values {
124            engine
125                .world()
126                .log(level.level, label.clone(), format!("{val}"), Some(track_id))
127                .await;
128        }
129    }
130}
131
132/// Like `log_data_stream` but reads `label` as a block input.
133///
134/// Waits for `label` first, then converts and logs each item in `display` at `level`.
135#[mel_treatment(
136    model engine Engine
137    input label Block<string>
138    input display Stream<D>
139    generic D (Display)
140)]
141pub async fn log_data_stream_label(level: Level) {
142    let engine = EngineModel::into(engine);
143
144    if let Ok(label) = label
145        .recv_one()
146        .await
147        .map(|val| GetData::<String>::try_data(val).unwrap())
148    {
149        while let Ok(values) = display
150            .recv_many()
151            .await
152            .map(|values| Into::<VecDeque<Value>>::into(values))
153        {
154            for val in values {
155                engine
156                    .world()
157                    .log(level.level, label.clone(), format!("{val}"), Some(track_id))
158                    .await;
159            }
160        }
161    }
162}
163
164/// Convert a single `Display` block to its string representation and log it at `level` under `label`.
165#[mel_treatment(
166    model engine Engine
167    input display Block<D>
168    generic D (Display)
169)]
170pub async fn log_data_block(level: Level, label: string) {
171    let engine = EngineModel::into(engine);
172
173    if let Ok(val) = display.recv_one().await {
174        engine
175            .world()
176            .log(level.level, label, format!("{val}"), Some(track_id))
177            .await;
178    }
179}
180
181/// Like `log_data_block` but reads both `label` and `display` as block inputs.
182///
183/// Waits for `label` first, then converts and logs `display` at `level`.
184#[mel_treatment(
185    model engine Engine
186    input label Block<string>
187    input display Block<D>
188    generic D (Display)
189)]
190pub async fn log_data_block_label(level: Level) {
191    let engine = EngineModel::into(engine);
192
193    if let Ok(label) = label
194        .recv_one()
195        .await
196        .map(|val| GetData::<String>::try_data(val).unwrap())
197    {
198        if let Ok(val) = display.recv_one().await {
199            engine
200                .world()
201                .log(level.level, label, format!("{val}"), Some(track_id))
202                .await;
203        }
204    }
205}
206
207#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
208/// Log severity level.
209///
210/// Ordered from lowest to highest verbosity: `trace` < `debug` < `info` < `warning` < `error`.
211/// Use the constructor functions `|trace()`, `|debug()`, `|info()`, `|warning()`, `|error()` to obtain a value.
212#[mel_data(traits(Serialize Deserialize Bounded PartialEquality Equality PartialOrder Order))]
213pub struct Level {
214    pub level: LogLevel,
215}
216
217fn level_bounded_min() -> Level {
218    Level {
219        level: LogLevel::Trace,
220    }
221}
222
223fn level_bounded_max() -> Level {
224    Level {
225        level: LogLevel::Error,
226    }
227}
228
229/// Return the error log level.
230#[mel_function]
231pub fn error() -> Level {
232    Level {
233        level: LogLevel::Error,
234    }
235}
236
237/// Return the warning log level.
238#[mel_function]
239pub fn warning() -> Level {
240    Level {
241        level: LogLevel::Warning,
242    }
243}
244
245/// Return the info log level.
246#[mel_function]
247pub fn info() -> Level {
248    Level {
249        level: LogLevel::Info,
250    }
251}
252
253/// Return the debug log level.
254#[mel_function]
255pub fn debug() -> Level {
256    Level {
257        level: LogLevel::Debug,
258    }
259}
260
261/// Return the trace log level (most verbose).
262#[mel_function]
263pub fn trace() -> Level {
264    Level {
265        level: LogLevel::Trace,
266    }
267}