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 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 s.read_until(b'~', &mut buf)?;
58 match buf.last() {
59 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 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 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 = 13_f32;
189 let arg1 = 14_f32;
190 let arg2 = 15_f32;
191 let a: Vec<&dyn TildeAble> = vec![&arg0, &arg1];
192 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: 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 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 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 let case = "~[cero~;uno~:;dos~]";
272 let cs = ControlStr::new(case)?;
273 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 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 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 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 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 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 let a = vec![&1_i64 as &dyn TildeAble, &2_i64 as &dyn TildeAble];
410 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 let case = "~{~#[~;~a~;~a and ~a~:;~@{~a~#[~;, and ~:;, ~]~}~]~}";
421 let cs = ControlStr::new(case)?;
422 let a: Vec<&dyn TildeAble> = vec![];
426 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 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 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 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 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 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 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 let case = "~{~d~*~^ ~}";
535 let cs = ControlStr::new(case)?;
536 dbg!(&cs);
537
538 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 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 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}