std_mel/text/compose.rs
1use crate::data::string_map::*;
2use melodium_core::*;
3use melodium_macro::{check, mel_function, mel_treatment};
4
5/// Rescale stream of strings.
6///
7/// _Rescaling_ means that strings sent throught stream are rearranged according to the `delimiter`.
8///
9/// Unscaled stream can basically be cut at any position:
10/// ```
11/// "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean qua"
12/// "m velit, tristique et arcu in, viverra pulvinar ante. Interdum et m"
13/// "alesuada fames ac ante ipsum primis in faucibus. Cras varius, augue"
14/// " ac fringilla placerat, nibh lorem laoreet enim, sed fermentum libe"
15/// " ro justo ut sapien."
16/// ```
17///
18/// While treatments may expect well-defined strings:
19/// ```
20/// "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
21/// "Aenean quam velit, tristique et arcu in, viverra pulvinar ante."
22/// "Interdum et malesuada fames ac ante ipsum primis in faucibus."
23/// "Cras varius, augue ac fringilla placerat, nibh lorem laoreet enim, sed fermentum libero justo ut sapien."
24/// ```
25#[mel_treatment(
26 default delimiter "\n"
27 input unscaled Stream<string>
28 output scaled Stream<string>
29)]
30pub async fn rescale(delimiter: string) {
31 let mut previous = String::new();
32 'main: while let Ok(input) = unscaled
33 .recv_one()
34 .await
35 .map(|val| GetData::<string>::try_data(val).unwrap())
36 {
37 let splits: Vec<&str> = input.split_inclusive(&delimiter).collect();
38 for split in splits {
39 previous.push_str(split);
40 if previous.ends_with(&delimiter) {
41 let sendable = previous;
42 previous = String::new();
43 check!('main, scaled.send_one(sendable.into()).await);
44 }
45 }
46 }
47 if !previous.is_empty() {
48 let _ = scaled.send_one(previous.into()).await;
49 }
50}
51
52/// Split strings with delimiter.
53///
54/// `text` is splitted according to `delimiter`, and streamed as `splitted` vector.
55/// - `inclusive`: set if the delimiter must be kept at the end of splitted strings (if present).
56///
57/// ```mermaid
58/// graph LR
59/// T("split()")
60/// B["🟦"] -->|vector| T
61///
62/// T -->|value| O["[🟦 🟦 🟦]"]
63///
64/// style B fill:#ffff,stroke:#ffff
65/// style O fill:#ffff,stroke:#ffff
66/// ```
67#[mel_treatment(
68 default inclusive true
69 input text Stream<string>
70 output splitted Stream<Vec<string>>
71)]
72pub async fn split(delimiter: string, inclusive: bool) {
73 while let Ok(input) = text
74 .recv_many()
75 .await
76 .map(|values| TryInto::<Vec<string>>::try_into(values).unwrap())
77 {
78 let mut output = VecDeque::with_capacity(input.len());
79
80 if inclusive {
81 input.into_iter().for_each(|text| {
82 output.push_back(Value::Vec(
83 text.split_inclusive(&delimiter)
84 .map(|s| s.to_string().into())
85 .collect(),
86 ))
87 });
88 } else {
89 input.into_iter().for_each(|text| {
90 output.push_back(Value::Vec(
91 text.split(&delimiter)
92 .map(|s| s.to_string().into())
93 .collect(),
94 ))
95 });
96 }
97
98 check!(splitted.send_many(TransmissionValue::Other(output)).await);
99 }
100}
101
102/// Split strings with delimiter.
103///
104/// `text` is splitted as `Vec<string>` according to `delimiter`.
105/// - `inclusive`: set if the delimiter must be kept at the end of splitted strings (if present).
106#[mel_function]
107pub fn split(text: string, delimiter: string, inclusive: bool) -> Vec<string> {
108 if inclusive {
109 text.split_inclusive(&delimiter)
110 .map(|s| s.to_string())
111 .collect()
112 } else {
113 text.split(&delimiter).map(|s| s.to_string()).collect()
114 }
115}
116
117/// Trim stream of strings.
118///
119/// Stream strings with leading and trailing whitespace removed.
120/// _Whitespace_ is defined according to the terms of the Unicode Derived Core Property `White_Space`, which includes newlines.
121#[mel_treatment(
122 input text Stream<string>
123 output trimmed Stream<string>
124)]
125pub async fn trim() {
126 while let Ok(mut text) = text
127 .recv_many()
128 .await
129 .map(|values| TryInto::<Vec<string>>::try_into(values).unwrap())
130 {
131 text.iter_mut().for_each(|t| *t = t.trim().to_string());
132
133 check!(trimmed.send_many(text.into()).await);
134 }
135}
136
137/// Trim string.
138///
139/// Return string with leading and trailing whitespace removed.
140/// _Whitespace_ is defined according to the terms of the Unicode Derived Core Property `White_Space`, which includes newlines.
141#[mel_function]
142pub fn trim(text: string) -> string {
143 text.trim().to_string()
144}
145
146/// Trim end of streamed strings.
147///
148/// Stream strings with trailing whitespace removed.
149/// _Whitespace_ is defined according to the terms of the Unicode Derived Core Property `White_Space`, which includes newlines.
150#[mel_treatment(
151 input text Stream<string>
152 output trimmed Stream<string>
153)]
154pub async fn trim_end() {
155 while let Ok(mut text) = text
156 .recv_many()
157 .await
158 .map(|values| TryInto::<Vec<string>>::try_into(values).unwrap())
159 {
160 text.iter_mut().for_each(|t| *t = t.trim_end().to_string());
161
162 check!(trimmed.send_many(text.into()).await);
163 }
164}
165
166/// Trim end of string.
167///
168/// Return string with trailing whitespace removed.
169/// _Whitespace_ is defined according to the terms of the Unicode Derived Core Property `White_Space`, which includes newlines.
170#[mel_function]
171pub fn trim_end(text: string) -> string {
172 text.trim_end().to_string()
173}
174
175/// Trim start of streamed strings.
176///
177/// Stream strings with leading whitespace removed.
178/// _Whitespace_ is defined according to the terms of the Unicode Derived Core Property `White_Space`, which includes newlines.
179#[mel_treatment(
180 input text Stream<string>
181 output trimmed Stream<string>
182)]
183pub async fn trim_start() {
184 while let Ok(mut text) = text
185 .recv_many()
186 .await
187 .map(|values| TryInto::<Vec<string>>::try_into(values).unwrap())
188 {
189 text.iter_mut()
190 .for_each(|t| *t = t.trim_start().to_string());
191
192 check!(trimmed.send_many(text.into()).await);
193 }
194}
195
196/// Trim start of string.
197///
198/// Return string with trailing whitespace removed.
199/// _Whitespace_ is defined according to the terms of the Unicode Derived Core Property `White_Space`, which includes newlines.
200#[mel_function]
201pub fn trim_start(text: string) -> string {
202 text.trim_start().to_string()
203}
204
205/// Format string.
206///
207/// Return string formatted with given entries.
208/// Format string is expected to contains braced placeholders, like: `"Hello {name}!"`.
209///
210/// If a formatting error happens, like missing key of incorrect format string, an empty string is returned.
211#[mel_function]
212pub fn format(format: string, entries: StringMap) -> string {
213 strfmt::strfmt(&format, &entries.map).unwrap_or_default()
214}
215
216/// Checked format string.
217///
218/// Return string formatted with given entries.
219/// Format string is expected to contains braced placeholders, like: `"Hello {name}!"`.
220///
221/// If a formatting error happens, like missing key of incorrect format string, _none_ is returned.
222#[mel_function]
223pub fn checked_format(format: string, entries: StringMap) -> Option<string> {
224 strfmt::strfmt(&format, &entries.map).ok()
225}
226
227/// Format stream.
228///
229/// Stream string formatted with given entries.
230/// Format string is expected to contains braced placeholders, like: `"Hello {name}!"`.
231///
232/// If a formatting error happens, like missing key of incorrect format string, an empty string is sent.
233#[mel_treatment(
234 input entries Stream<StringMap>
235 output formatted Stream<string>
236)]
237pub async fn format(format: string) {
238 while let Ok(maps) = entries
239 .recv_many()
240 .await
241 .map(|values| TryInto::<Vec<Value>>::try_into(values).unwrap())
242 {
243 let formatted_str = maps
244 .into_iter()
245 .map(|map| {
246 GetData::<std::sync::Arc<dyn Data>>::try_data(map)
247 .unwrap()
248 .downcast_arc::<StringMap>()
249 .unwrap()
250 })
251 .map(|map| strfmt::strfmt(&format, &map.map).unwrap_or_default())
252 .collect::<VecDeque<_>>();
253
254 check!(
255 formatted
256 .send_many(TransmissionValue::String(formatted_str))
257 .await
258 );
259 }
260}
261
262/// Format stream.
263///
264/// Stream string formatted with given entries.
265/// Format string is expected to contains braced placeholders, like: `"Hello {name}!"`.
266///
267/// If a formatting error happens, like missing key of incorrect format string, _none_ is sent.
268#[mel_treatment(
269 input entries Stream<StringMap>
270 output formatted Stream<Option<string>>
271)]
272pub async fn checked_format(format: string) {
273 while let Ok(maps) = entries
274 .recv_many()
275 .await
276 .map(|values| TryInto::<Vec<Value>>::try_into(values).unwrap())
277 {
278 let formatted_str = maps
279 .into_iter()
280 .map(|map| {
281 GetData::<std::sync::Arc<dyn Data>>::try_data(map)
282 .unwrap()
283 .downcast_arc::<StringMap>()
284 .unwrap()
285 })
286 .map(|map| {
287 Value::Option(
288 strfmt::strfmt(&format, &map.map)
289 .map(|formatted| Box::new(Value::String(formatted)))
290 .ok(),
291 )
292 })
293 .collect::<VecDeque<_>>();
294
295 check!(
296 formatted
297 .send_many(TransmissionValue::Other(formatted_str))
298 .await
299 );
300 }
301}