cl_format/
control_str.rs

1use crate::tildes::*;
2use std::fmt::Debug;
3use std::io::{BufRead, Cursor, Seek, SeekFrom};
4
5#[doc = r"The control string is the type contains control string for format.
6
7It includes:
8
91. the whole string
102. the parsed tree
11
12Use `new(&str)` to create a ControlStr and reuse this control string instance to `reveal(Args)` to get the result string"]
13#[derive(Debug, Clone, PartialEq)]
14pub struct ControlStr<'a> {
15    inner: &'a str,
16    tildes: Vec<((usize, usize), Tilde)>,
17}
18
19impl<'a> ControlStr<'a> {
20    #[doc = r"Make a new ContrilStr from &str"]
21    pub fn new(s: &'a str) -> Result<Self, Box<dyn std::error::Error + 'a>> {
22        let cc = Cursor::new(s);
23        let tildes = Self::scan(cc)?;
24
25        Ok(Self { inner: s, tildes })
26    }
27
28    #[allow(dead_code)]
29    #[doc = "Reveal arguments to string"]
30    pub fn reveal<'s, 'arg>(&self, args: Args<'s, 'arg>) -> Result<String, TildeError> {
31        //dbg!(self);
32        let mut start = 0;
33        let end = self.inner.len();
34
35        let mut result = String::with_capacity(args.left_count());
36
37        for (r, t) in &self.tildes {
38            result.push_str(&self.inner[start..r.0]);
39            t.reveal(&args, &mut result)?;
40            start = r.1;
41        }
42
43        result += &self.inner[start..end];
44
45        Ok(result)
46    }
47
48    fn scan(
49        mut s: Cursor<&'_ str>,
50    ) -> Result<Vec<((usize, usize), Tilde)>, Box<dyn std::error::Error + 'a>> {
51        let mut buf = vec![];
52        let mut has_read_len = 0;
53        let mut result = vec![];
54
55        loop {
56            //dbg!(s.position());
57            s.read_until(b'~', &mut buf)?;
58            match buf.last() {
59                // find the next '~'
60                Some(b'~') => {
61                    has_read_len += buf.len() - 1;
62                    s.seek(SeekFrom::Current(-1))?;
63                }
64                _ => return Ok(result),
65            }
66
67            let t = Tilde::parse(&mut s)?;
68            let end_index = has_read_len + t.len();
69
70            result.push(((has_read_len, end_index), t));
71            has_read_len = end_index;
72            buf.clear();
73        }
74    }
75}
76
77impl<'a> TryFrom<&'a str> for ControlStr<'a> {
78    type Error = Box<dyn std::error::Error + 'a>;
79
80    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
81        Self::new(value)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    //use crate::tildes::*;
88
89    use super::*;
90
91    fn parse_test_result<'x, 's>(
92        a: impl Iterator<Item = (&'x (usize, usize), Result<String, TildeError>)>,
93    ) -> Result<Vec<Option<String>>, String> {
94        let mut x = vec![];
95        for (_, aa) in a {
96            x.push(Some(aa.map_err(|e| e.to_string())?))
97        }
98        Ok(x)
99    }
100
101    fn reveal_tildes<'a, 'x: 'a>(
102        cs: &'x ControlStr,
103        args: &'x Args<'a, '_>,
104    ) -> impl Iterator<Item = (&'x (usize, usize), Result<String, TildeError>)> + 'x {
105        cs.tildes.iter().map(|(ind, tilde)| {
106            let mut b = String::new();
107            tilde.reveal(args, &mut b).unwrap();
108            (ind, Ok(b))
109        })
110    }
111
112    #[test]
113    fn test_try_from_self() -> Result<(), Box<dyn std::error::Error>> {
114        let case = "hello wor~{~a~}";
115        let x = ControlStr::new(case)?;
116        let y = x.clone();
117        //dbg!(&y);
118
119        assert_eq!(ControlStr::try_from(x)?, y);
120
121        Ok(())
122    }
123
124    #[test]
125    fn test_control_str_scan() -> Result<(), Box<dyn std::error::Error>> {
126        let case = "hello wor~{~a~}";
127        let c = Cursor::new(case);
128
129        assert_eq!(
130            ControlStr::scan(c)?,
131            vec![(
132                (9, 15),
133                Tilde::new(
134                    6,
135                    TildeKind::Loop((vec![Tilde::new(2, TildeKind::Va)], TildeLoopKind::Nil))
136                )
137            )]
138        );
139
140        let case = "~{~5$~}";
141        let c = Cursor::new(case);
142
143        assert_eq!(
144            ControlStr::scan(c)?,
145            vec![(
146                (0, 7),
147                Tilde::new(
148                    7,
149                    TildeKind::Loop((
150                        vec![Tilde::new(3, TildeKind::Float(Some("5".to_string())))],
151                        TildeLoopKind::Nil
152                    ))
153                )
154            )]
155        );
156
157        Ok(())
158    }
159
160    #[test]
161    fn test_reveal_normal_tildes() -> Result<(), String> {
162        let case = "hello wor~a";
163        let cs = ControlStr::new(case).map_err(|e| e.to_string())?;
164        let arg: &dyn TildeAble = &13_f32;
165        dbg!(arg.into_tildekind_va());
166
167        let result: Vec<Option<String>> = vec!["13".to_string()]
168            .into_iter()
169            .map(|s| Some(s))
170            .collect();
171
172        assert_eq!(
173            result,
174            parse_test_result(reveal_tildes(&cs, &([arg].into()))).map_err(|e| e.to_string())?
175        );
176
177        Ok(())
178    }
179
180    #[test]
181    fn test_reveal_loop_tildes() -> Result<(), Box<dyn std::error::Error>> {
182        let case = "hello wor~{~a~}~a";
183        let cs = ControlStr::new(case)?;
184        //let arg0: &dyn TildeAble = &13_f32;
185        //let arg1: &dyn TildeAble = &14_f32;
186        //let arg2: &dyn TildeAble = &15_f32;
187
188        let arg0 = 13_f32;
189        let arg1 = 14_f32;
190        let arg2 = 15_f32;
191        let a: Vec<&dyn TildeAble> = vec![&arg0, &arg1];
192        //let arg00 = Args::from(vec![&arg0 as &dyn TildeAble, &arg1]);
193        //let arg00 = Args::new(a);
194        //let arg: Vec<&dyn TildeAble> = vec![&arg00, &arg2];
195        let arg: Vec<&dyn TildeAble> = vec![&a, &arg2];
196
197        let result: Vec<Option<String>> = vec!["1314".to_string(), "15".to_string()]
198            .into_iter()
199            .map(|s| Some(s))
200            .collect();
201
202        assert_eq!(
203            result,
204            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
205        );
206
207        let case = "hello, ~@{~a~^, ~}";
208        let cs = ControlStr::new(case)?;
209        let arg: Vec<&dyn TildeAble> = vec![&1_i64, &2_i64, &3_i64];
210        let result: Vec<Option<String>> = vec!["1, 2, 3".to_string()]
211            .into_iter()
212            .map(|s| Some(s))
213            .collect();
214        assert_eq!(
215            result,
216            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
217        );
218
219        let case = "hello, ~{~a~^, ~}";
220        let cs = ControlStr::new(case)?;
221        dbg!(&cs);
222        // let a0: Args = Args::new(vec![
223        //     &1_i64 as &dyn TildeAble,
224        //     &2_i64 as &dyn TildeAble,
225        //     &3_i64 as &dyn TildeAble,
226        // ]);
227        let a0: Vec<&dyn TildeAble> = vec![
228            &1_i64 as &dyn TildeAble,
229            &2_i64 as &dyn TildeAble,
230            &3_i64 as &dyn TildeAble,
231        ];
232        let arg: Vec<&dyn TildeAble> = vec![&a0];
233        let result: Vec<Option<String>> = vec!["1, 2, 3".to_string()]
234            .into_iter()
235            .map(|s| Some(s))
236            .collect();
237        assert_eq!(
238            result,
239            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
240        );
241        Ok(())
242    }
243
244    #[test]
245    fn test_reveal_normal_cond_tildes() -> Result<(), Box<dyn std::error::Error>> {
246        let case = "~[cero~;uno~;dos~]";
247        let cs = ControlStr::new(case)?;
248
249        //dbg!(&cs);
250
251        let arg: Vec<&dyn TildeAble> = vec![&0_usize];
252        assert_eq!(
253            vec!["cero".to_string()]
254                .into_iter()
255                .map(|s| Some(s))
256                .collect::<Vec<Option<_>>>(),
257            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
258        );
259
260        //
261        let arg: Vec<&dyn TildeAble> = vec![&1_usize];
262        assert_eq!(
263            vec!["uno".to_string()]
264                .into_iter()
265                .map(|s| Some(s))
266                .collect::<Vec<Option<_>>>(),
267            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
268        );
269
270        //
271        let case = "~[cero~;uno~:;dos~]";
272        let cs = ControlStr::new(case)?;
273        //dbg!(&cs);
274
275        let arg: Vec<&dyn TildeAble> = vec![&0_usize];
276        assert_eq!(
277            vec!["cero".to_string()]
278                .into_iter()
279                .map(|s| Some(s))
280                .collect::<Vec<Option<_>>>(),
281            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
282        );
283
284        let arg: Vec<&dyn TildeAble> = vec![&2_usize];
285        assert_eq!(
286            vec!["dos".to_string()]
287                .into_iter()
288                .map(|s| Some(s))
289                .collect::<Vec<Option<_>>>(),
290            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
291        );
292
293        //dbg!(&cs);
294        let arg: Vec<&dyn TildeAble> = vec![&3_usize];
295        assert_eq!(
296            vec!["dos".to_string()]
297                .into_iter()
298                .map(|s| Some(s))
299                .collect::<Vec<Option<_>>>(),
300            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
301        );
302
303        //dbg!(&cs);
304        let arg: Vec<&dyn TildeAble> = vec![&4_usize];
305        assert_eq!(
306            vec!["dos".to_string()]
307                .into_iter()
308                .map(|s| Some(s))
309                .collect::<Vec<Option<_>>>(),
310            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
311        );
312
313        let arg: Vec<&dyn TildeAble> = vec![&100_usize];
314        assert_eq!(
315            vec!["dos".to_string()]
316                .into_iter()
317                .map(|s| Some(s))
318                .collect::<Vec<Option<_>>>(),
319            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
320        );
321
322        let case = "~#[NONE~;first: ~a~;~a and ~a~:;~a, ~a~]";
323        let cs = ControlStr::new(case)?;
324        let args: Vec<&dyn TildeAble> = vec![&1_i64];
325        //dbg!(t.reveal_args(&mut args));
326        assert_eq!(
327            vec!["first: 1".to_string()]
328                .into_iter()
329                .map(|s| Some(s))
330                .collect::<Vec<Option<_>>>(),
331            parse_test_result(reveal_tildes(&cs, &(args.into())))?
332        );
333
334        let cs = ControlStr::new(case)?;
335        let args: Vec<&dyn TildeAble> = vec![&2_i64, &2_i64];
336        //dbg!(t.reveal_args(&mut args));
337        assert_eq!(
338            vec!["2 and 2".to_string()]
339                .into_iter()
340                .map(|s| Some(s))
341                .collect::<Vec<Option<_>>>(),
342            parse_test_result(reveal_tildes(&cs, &(args.into())))?
343        );
344
345        let cs = ControlStr::new(case)?;
346        let args: Vec<&dyn TildeAble> = vec![&3_i64, &3_i64, &3_i64];
347        //dbg!(t.reveal_args(&mut args));
348        assert_eq!(
349            vec!["3, 3".to_string()]
350                .into_iter()
351                .map(|s| Some(s))
352                .collect::<Vec<Option<_>>>(),
353            parse_test_result(reveal_tildes(&cs, &(args.into())))?
354        );
355
356        Ok(())
357    }
358
359    #[test]
360    fn test_reveal_sharp_cond_tildes() -> Result<(), Box<dyn std::error::Error>> {
361        let case = "~#[NONE~;~a~;~a and ~a~:;~a, ~a~]~#[~; and ~a~:;, ~a, etc~].";
362        let cs = ControlStr::new(case)?;
363        dbg!(&cs);
364
365        let arg: Vec<&dyn TildeAble> = vec![];
366        assert_eq!(
367            vec![Some("NONE".to_string()), Some("".to_string())],
368            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
369        );
370
371        Ok(())
372    }
373
374    #[test]
375    fn test_reveal_at_cond_tildes() -> Result<(), Box<dyn std::error::Error>> {
376        let case = "~@[x = ~a ~]~@[y = ~a~]";
377        let cs = ControlStr::new(case)?;
378        dbg!(&cs);
379
380        let arg: Vec<&dyn TildeAble> = vec![&Some(&1_i64 as &dyn TildeAble), &None];
381        assert_eq!(
382            vec![Some("x = 1 ".to_string()), Some("".to_string())],
383            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
384        );
385
386        let case = "~@[x = ~a ~]~@[y = ~a~]";
387        let cs = ControlStr::new(case)?;
388        let arg: Vec<&dyn TildeAble> = vec![
389            &Some(&1_i64 as &dyn TildeAble),
390            &Some(&2_usize as &dyn TildeAble),
391        ];
392        assert_eq!(
393            vec!["x = 1 ".to_string(), "y = 2".to_string()]
394                .into_iter()
395                .map(|s| Some(s))
396                .collect::<Vec<Option<_>>>(),
397            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
398        );
399
400        Ok(())
401    }
402
403    #[test]
404    fn test_reveal_loop_cond_combine() -> Result<(), Box<dyn std::error::Error>> {
405        let case = "~{~a~#[~;, and ~:;, ~]~}";
406        let cs = ControlStr::new(case)?;
407        //dbg!(&cs);
408
409        let a = vec![&1_i64 as &dyn TildeAble, &2_i64 as &dyn TildeAble];
410        //let a = Args::from([&1_i64 as &dyn TildeAble, &2_i64 as &dyn TildeAble]);
411        let arg: Vec<&dyn TildeAble> = vec![&a];
412
413        assert_eq!(
414            vec![Some("1, and 2".to_string())],
415            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
416        );
417
418        //
419
420        let case = "~{~#[~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~}";
421        let cs = ControlStr::new(case)?;
422        //dbg!(&cs);
423
424        //let a = Args::new(vec![]);
425        let a: Vec<&dyn TildeAble> = vec![];
426        //let aa = vec![&a as &dyn TildeAble];
427        let arg: Vec<&dyn TildeAble> = vec![&a];
428        assert_eq!(
429            vec![Some("".to_string())],
430            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
431        );
432
433        let cs = ControlStr::new(case)?;
434        let a = vec![&1_i64 as &dyn TildeAble];
435        //let a = Args::from([&1_i64 as &dyn TildeAble]);
436        let arg = vec![&a as &dyn TildeAble];
437        assert_eq!(
438            vec!["1".to_string()]
439                .into_iter()
440                .map(|s| Some(s))
441                .collect::<Vec<Option<_>>>(),
442            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
443        );
444
445        let cs = ControlStr::new(case)?;
446        let a = vec![&1_i64 as &dyn TildeAble, &2_i64];
447        //let a = Args::from([&1_i64 as &dyn TildeAble, &2_i64]);
448        let arg: Vec<&dyn TildeAble> = vec![&a as &dyn TildeAble];
449        assert_eq!(
450            vec!["1 and 2".to_string()]
451                .into_iter()
452                .map(|s| Some(s))
453                .collect::<Vec<Option<_>>>(),
454            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
455        );
456
457        let cs = ControlStr::new(case)?;
458        let a = vec![&1_i64 as &dyn TildeAble, &2_i64, &3_i64];
459        //let a = Args::from([&1_i64 as &dyn TildeAble, &2_i64, &3_i64]);
460        let arg: Vec<&dyn TildeAble> = vec![&a as &dyn TildeAble];
461        assert_eq!(
462            vec!["1, 2, and 3".to_string()]
463                .into_iter()
464                .map(|s| Some(s))
465                .collect::<Vec<Option<_>>>(),
466            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
467        );
468
469        let cs = ControlStr::new(case)?;
470        let a = vec![&1_i64 as &dyn TildeAble, &2_i64, &3_i64, &4_i64];
471        //let a = Args::from([&1_i64 as &dyn TildeAble, &2_i64, &3_i64, &4_i64]);
472        let arg: Vec<&dyn TildeAble> = vec![&a as &dyn TildeAble];
473        assert_eq!(
474            vec!["1, 2, 3, and 4".to_string()]
475                .into_iter()
476                .map(|s| Some(s))
477                .collect::<Vec<Option<_>>>(),
478            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
479        );
480
481        let cs = ControlStr::new(case)?;
482        let a = vec![&1_i64 as &dyn TildeAble, &2_i64, &3_i64, &4_i64, &5_i64];
483        //let a = Args::from([&1_i64 as &dyn TildeAble, &2_i64, &3_i64, &4_i64, &5_i64]);
484        let arg: Vec<&dyn TildeAble> = vec![&a as &dyn TildeAble];
485        assert_eq!(
486            vec!["1, 2, 3, 4, and 5".to_string()]
487                .into_iter()
488                .map(|s| Some(s))
489                .collect::<Vec<Option<_>>>(),
490            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
491        );
492
493        let case = "~{~#[empty~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~}";
494        let cs = ControlStr::new(case)?;
495        let a = vec![];
496        //let a = Args::new(vec![]);
497        let arg: Vec<&dyn TildeAble> = vec![&a];
498        assert_eq!(
499            vec![Some("".to_string())],
500            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
501        );
502
503        let case = "~{~#[empty~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~:}";
504        let cs = ControlStr::new(case)?;
505        let arg: Vec<&dyn TildeAble> = vec![&a];
506        assert_eq!(
507            vec!["empty".to_string()]
508                .into_iter()
509                .map(|s| Some(s))
510                .collect::<Vec<Option<_>>>(),
511            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
512        );
513
514        Ok(())
515    }
516
517    #[test]
518    fn test_reveal_star() -> Result<(), Box<dyn std::error::Error>> {
519        let case = "~d ~:*(~d)";
520        let cs = ControlStr::new(case)?;
521        dbg!(&cs);
522
523        let arg = Args::from([&1_i64 as &dyn TildeAble]);
524        assert_eq!(
525            vec![
526                Some("1".to_string()),
527                Some("".to_string()),
528                Some("1".to_string())
529            ],
530            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
531        );
532
533        //
534        let case = "~{~d~*~^ ~}";
535        let cs = ControlStr::new(case)?;
536        dbg!(&cs);
537
538        //let a = Args::from([&1_i64 as &dyn TildeAble, &2, &3, &4]);
539        let a = vec![&1_i64 as &dyn TildeAble, &2, &3, &4];
540        let arg: Vec<&dyn TildeAble> = vec![&a as &dyn TildeAble];
541        assert_eq!(
542            vec!["1 3".to_string()]
543                .into_iter()
544                .map(|s| Some(s))
545                .collect::<Vec<Option<_>>>(),
546            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
547        );
548
549        Ok(())
550    }
551
552    #[test]
553    fn test_reveal_char() -> Result<(), Box<dyn std::error::Error>> {
554        let case = "~c ~C ~@c";
555        let cs = ControlStr::new(case)?;
556        dbg!(&cs);
557
558        let arg = Args::from([
559            &'a' as &dyn TildeAble,
560            &'b' as &dyn TildeAble,
561            &'c' as &dyn TildeAble,
562        ]);
563        assert_eq!(
564            vec![
565                Some("a".to_string()),
566                Some("b".to_string()),
567                Some("'c'".to_string())
568            ],
569            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
570        );
571
572        Ok(())
573    }
574
575    #[test]
576    fn test_reveal_standard() -> Result<(), Box<dyn std::error::Error>> {
577        let case = "~d ~s";
578        let cs = ControlStr::new(case)?;
579
580        let s = String::from("hello");
581        let arg = Args::from([&1_i64 as &dyn TildeAble, &s]);
582
583        assert_eq!(
584            vec![Some("1".to_string()), Some("\"hello\"".to_string())],
585            parse_test_result(reveal_tildes(&cs, &(arg.into())))?
586        );
587
588        Ok(())
589    }
590
591    #[test]
592    fn test_reveal_radix() -> Result<(), Box<dyn std::error::Error>> {
593        let cs = ControlStr::new("~R")?;
594        assert_eq!(
595            "one".to_string(),
596            cs.reveal([&1_i32 as &dyn TildeAble].into())?
597        );
598
599        assert_eq!(
600            "sixty-four".to_string(),
601            cs.reveal([&64_i128 as &dyn TildeAble].into())?
602        );
603
604        let cs = ControlStr::new("~:R")?;
605        assert_eq!(
606            "first".to_string(),
607            cs.reveal([&1_i32 as &dyn TildeAble].into())?
608        );
609
610        assert_eq!(
611            "sixty-fourth".to_string(),
612            cs.reveal([&64_isize as &dyn TildeAble].into())?
613        );
614
615        assert_eq!(
616            "negative sixty-fourth".to_string(),
617            cs.reveal([&-64_isize as &dyn TildeAble].into())?
618        );
619
620        //
621        let cs = ControlStr::new("~3,,, ,2:R")?;
622        assert_eq!(
623            "1 22".to_string(),
624            cs.reveal([&17_usize as &dyn TildeAble].into())?
625        );
626
627        assert_eq!(
628            "000001101 0000 0101".to_string(),
629            ControlStr::new("~2,19,0, ,4:R")?.reveal([&3333_usize as &dyn TildeAble].into())?
630        );
631
632        assert_eq!(
633            "1101 0000 0101".to_string(),
634            ControlStr::new("~2,8,0, ,4:R")?.reveal([&3333_usize as &dyn TildeAble].into())?
635        );
636
637        assert_eq!(
638            "6|55|35".to_string(),
639            ControlStr::new("~10,,,|,2:R")?.reveal([&65535_usize as &dyn TildeAble].into())?
640        );
641
642        //:= Next: more tests here
643        Ok(())
644    }
645
646    #[test]
647    fn test_reveal() -> Result<(), Box<dyn std::error::Error>> {
648        let case = "~a, ~a, ~a";
649        let cs = ControlStr::new(case)?;
650        assert_eq!(
651            "1, 2, 3".to_string(),
652            cs.reveal([&1_i32 as &dyn TildeAble, &2, &3].into())?
653        );
654
655        assert_eq!(
656            "4, 5, 6".to_string(),
657            cs.reveal([&4_i32 as &dyn TildeAble, &5, &6].into())?
658        );
659        Ok(())
660    }
661}