rtile/
lib.rs

1//!
2//! rtile provides a way to work with rectangular areas of text as atomic units which can be used for code generation.
3//!
4//! ```
5//! use rtile::prelude::*;
6//! kp!(greet_one, "Welcome to rtile!     ");
7//! tp!(greet_two, "Have a great day!");
8//! assert_eq!(ts!("@{greet_one}@{greet_two}"), "Welcome to rtile!     Have a great day!");
9//! ```
10//!
11
12#![warn(missing_docs)]
13
14use std::any::type_name;
15use std::cell::RefCell;
16use std::collections::HashMap;
17use std::collections::HashSet;
18use std::fmt::Debug;
19use std::fmt::Display;
20use std::fmt::Formatter;
21use std::fmt::Result;
22use std::iter::Iterator;
23use std::ops::Add;
24use std::ops::AddAssign;
25use std::ops::BitOr;
26use std::ops::BitOrAssign;
27
28///
29/// Prelude for RTile
30///
31pub mod prelude {
32    pub use std::marker::PhantomData;
33    pub use std::rc::Rc;
34
35    pub use crate::*;
36}
37
38use prelude::*;
39
40///
41/// give a name to a tile using any string literal and persist it in tls (thread local storage)
42/// ```
43/// use rtile::prelude::*;
44/// let tile = t!("tile value");
45/// stp!(persisted_tile_name, tile);
46/// assert_eq!("tile value".to_string(), t!("@{persisted_tile_name}").to_string());
47/// ```
48///
49#[macro_export]
50macro_rules! stp {
51    ($i: ident, $t: expr) => {{
52        set_tiles(format!("{}", stringify!($i)), $t.to_string());
53        set_raw_tiles(format!("{}", stringify!($i)), $t.clone());
54    }};
55}
56
57///
58/// give a name to a tile using a variable containing a string value and persist it in tls (thread local storage)
59/// ```
60/// use rtile::prelude::*;
61/// let tile = t!("tile value");
62/// let name_of_the_persisted_tile = "persisted_tile_name";
63/// stq!(name_of_the_persisted_tile, tile);
64/// assert_eq!("tile value".to_string(), t!("@{persisted_tile_name}").to_string());
65/// ```
66///
67#[macro_export]
68macro_rules! stq {
69    ($e: expr, $t: expr) => {{
70        set_tiles(format!("{}", $e), $t.to_string());
71        set_raw_tiles(format!("{}", $e), $t.clone());
72    }};
73}
74
75///
76/// get the tile which is persisted in the tls (thread local storage)
77/// ```
78/// use rtile::prelude::*;
79/// let tile = t!("tile value");
80/// stp!(persisted_tile_name, tile);
81/// let result = gtp!(persisted_tile_name).unwrap();
82/// assert_eq!(tile, result);
83/// ```
84///
85#[macro_export]
86macro_rules! gtp {
87    ($i: ident) => {{
88        get_raw_tile(&stringify!($i).to_string())
89    }};
90}
91
92///
93/// get the tile which is persisted in the tls (thread local storage) using a variable containing a string value
94/// ```
95/// use rtile::prelude::*;
96/// let tile = t!("tile value");
97/// stp!(persisted_tile_name, tile);
98/// let name_of_the_persisted_tile = "persisted_tile_name";
99/// let result = gtq!(name_of_the_persisted_tile).unwrap();
100/// assert_eq!(tile, result);
101/// ```
102///
103#[macro_export]
104macro_rules! gtq {
105    ($e: expr) => {{
106        let target_tile_name = format!("{}", $e);
107        get_raw_tile(&target_tile_name)
108    }};
109}
110
111#[doc(hidden)]
112///
113/// Tiles with trimming
114///
115/// t - trim white spaces - do_trimming: true
116///
117pub trait MacroAttributeForT {
118    #[doc(hidden)]
119    fn process(&self) -> RTile;
120}
121
122impl MacroAttributeForT for &str {
123    fn process(&self) -> RTile {
124        RTile::construct_from_str(self)
125    }
126}
127
128impl MacroAttributeForT for String {
129    fn process(&self) -> RTile {
130        RTile::construct_from_str(self.as_str())
131    }
132}
133
134impl MacroAttributeForT for &String {
135    fn process(&self) -> RTile {
136        RTile::construct_from_str(self.as_str())
137    }
138}
139
140impl MacroAttributeForT for RTile {
141    fn process(&self) -> RTile {
142        RTile::construct_from_str(self.to_string().as_str())
143    }
144}
145
146impl MacroAttributeForT for &RTile {
147    fn process(&self) -> RTile {
148        RTile::construct_from_str(self.to_string().as_str())
149    }
150}
151
152impl MacroAttributeForT for Vec<&str> {
153    fn process(&self) -> RTile {
154        RTile::new_str(self.clone())
155    }
156}
157
158impl MacroAttributeForT for Vec<String> {
159    fn process(&self) -> RTile {
160        RTile::new(self.clone())
161    }
162}
163
164/// tf! is used to flatten the multilines of the tile output into a single string
165///
166/// ```
167/// use rtile::prelude::*;
168/// tp!(
169///     tile_2,
170///     "
171///             seven,
172///     "
173/// );
174/// tp!(
175///     tile_1,
176///     "
177///             six,
178///             @{tile_2}
179///             eight,
180///     "
181/// );
182/// let input_tile = t!("
183///             one,
184///             two,
185///             three,
186///             four,
187///             five,
188///             @{tile_1}
189///             nine,
190///             ten
191/// ");
192/// let output = tf!(input_tile);
193/// let expected_output = "one,two,three,four,five,six,seven,eight,nine,ten".to_string();
194/// assert_eq!(output, expected_output);
195///
196/// let v1 = vec!["  one  ", "  two  ", "  three  "];
197/// let v2 = vec!["  1  ", "  2  ", "  3  "];
198/// let val = k!(v1) + k!(v2);
199/// assert_eq!(tf!(val), "one      1two      2three    3");
200/// ```
201///        
202#[macro_export]
203macro_rules! tf {
204    ($t: expr) => {{
205        $t.to_string()
206            .split('\n')
207            .collect::<Vec<&str>>()
208            .iter()
209            .map(|&item| item.trim())
210            .collect::<Vec<&str>>()
211            .join("")
212    }};
213}
214
215/// t! is to expand any inner tiles and to trim the white spaces around the block of text and return a tile
216///
217/// ```
218/// use rtile::prelude::*;
219/// tp!(
220///     tile_2,
221///     "
222///             seven,
223///     "
224/// );
225/// tp!(
226///     tile_1,
227///     "
228///             six,
229///             @{tile_2}
230///             eight,
231///     "
232/// );
233/// let input_tile = t!("
234///             one,
235///             two,
236///             three,
237///             four,
238///             five,
239///             @{tile_1}
240///             nine,
241///             ten
242/// ");
243/// let output = input_tile.to_string();
244/// let expected_output = "one,\ntwo,\nthree,\nfour,\nfive,\nsix,\nseven,\neight,\nnine,\nten".to_string();
245/// assert_eq!(output, expected_output);
246/// ```
247///   
248#[macro_export]
249macro_rules! t {
250    () => {{
251        RTile::new(vec![])
252    }};
253    ("") => {{
254        RTile {
255            name: None,
256            lns: vec!["".to_string()],
257            do_trimming: true,
258            marker: PhantomData::<Rc<()>>,
259        }
260    }};
261    ($e:expr) => {{
262        MacroAttributeForT::process(&$e)
263    }};
264    ($($arg:tt)*) => {{
265        let val = format!($($arg)*);
266        t!(val)
267    }};
268}
269
270/// tp! is to used to persist the tile into the tls (thread local storage), with a given name (string literal) and return a tile
271///
272/// ```
273/// use rtile::prelude::*;
274/// tp!(
275///     tile_one,
276///     "
277///             one
278///
279///             two
280///     "
281/// );
282/// tp!(
283///     tile_two,
284///     "
285///             three
286///             four
287///             five
288///     "
289/// );
290/// let input_tile = t!("
291///             @{tile_one} @{tile_two}
292/// ");
293/// let output = input_tile.to_string();
294/// let expected_output = ts!("
295///                          one three
296///                              four
297///                          two five
298///                          ");
299/// assert_eq!(output, expected_output);
300/// ```
301///   
302#[macro_export]
303macro_rules! tp {
304    ($i:ident) => {{
305        let mut $i = t!();
306        $i.name = Some(stringify!($i).to_string());
307        set_tiles(format!("{}", stringify!($i)), $i.to_string());
308        set_raw_tiles(format!("{}", stringify!($i)), $i.clone());
309        $i
310    }};
311    ($i:ident, $e:expr) => {{
312        let mut $i = t!($e);
313        $i.name = Some(stringify!($i).to_string());
314        set_tiles(format!("{}", stringify!($i)), $i.to_string());
315        set_raw_tiles(format!("{}", stringify!($i)), $i.clone());
316        $i
317    }};
318    ($i:ident, $($arg:tt)*) => {{
319        let val = format!($($arg)*);
320        let mut $i = t!(val);
321        $i.name = Some(stringify!($i).to_string());
322        set_tiles(format!("{}", stringify!($i)), $i.to_string());
323        set_raw_tiles(format!("{}", stringify!($i)), $i.clone());
324        $i
325    }};
326}
327
328/// tq! is to used to persist the tile into the tls (thread local storage), with a variable having a string value and return a tile
329///
330/// ```
331///         
332/// use rtile::prelude::*;
333/// let persisted_tile_one = "tile_one";
334/// let persisted_tile_two = "tile_two";
335/// tq!(
336///     persisted_tile_one,
337///     "
338///             one
339///
340///             two
341///     "
342/// );
343/// tq!(
344///     persisted_tile_two,
345///     "
346///             three
347///             four
348///             five
349///     "
350/// );
351/// let input_tile = t!("
352///             @{tile_one} @{tile_two}
353/// ");
354/// let output = input_tile.to_string();
355/// let expected_output = ts!("
356///                          one three
357///                              four
358///                          two five
359///                          ");
360/// assert_eq!(output, expected_output);
361/// ```
362///  
363#[macro_export]
364macro_rules! tq {
365    ($e:expr) => {{
366        let mut target_tile = t!();
367        let target_tile_name = format!("{}", $e);
368        target_tile.name = Some(target_tile_name.clone());
369        set_tiles(target_tile_name.clone(), target_tile.to_string());
370        set_raw_tiles(target_tile_name, target_tile.clone());
371        target_tile
372    }};
373    ($e:expr, $val:expr) => {{
374        let mut target_tile = t!($val);
375        let target_tile_name = format!("{}", $e);
376        target_tile.name = Some(target_tile_name.clone());
377        set_tiles(target_tile_name.clone(), target_tile.to_string());
378        set_raw_tiles(target_tile_name, target_tile.clone());
379        target_tile
380    }};
381    ($e:expr, $($arg:tt)*) => {{
382        let val = format!($($arg)*);
383        let mut target_tile = t!(val);
384        let target_tile_name = format!("{}", $e);
385        target_tile.name = Some(target_tile_name.clone());
386        set_tiles(target_tile_name.clone(), target_tile.to_string());
387        set_raw_tiles(target_tile_name, target_tile.clone());
388        target_tile
389    }};
390}
391
392/// tt! is to used to expand the inner tiles and return the expanded ouput as a trimmed tile
393///
394/// ```
395/// use rtile::prelude::*;
396///
397/// tp!(numbers, "1, 2, 3, 4, 5");
398/// let mut result = tt!("Numbers: @{numbers}");
399/// tp!(numbers, "one, two, three, four, five");
400/// result |= t!("In words: @{numbers}");
401/// assert_eq!(result.to_string(), ts!("
402///                                     Numbers: 1, 2, 3, 4, 5
403///                                     In words: one, two, three, four, five
404///                                     "));
405/// ```
406#[macro_export]
407macro_rules! tt {
408    ($e:expr) => {{
409        t!(t!($e))
410    }};
411    ($($arg:tt)*) => {{
412        let val = format!($($arg)*);
413        t!(t!(val))
414    }};
415}
416
417/// ttp! is to used to expand the inner tiles, persist the result to tls (thread local storage) using a string literal and return a trimmed tile
418///
419/// ```
420/// use rtile::prelude::*;
421///
422/// tp!(numbers, "1, 2, 3, 4, 5");
423/// ttp!(numbers, "Numbers: @{numbers}");
424/// assert_eq!(t!("@{numbers}").to_string(), "Numbers: 1, 2, 3, 4, 5".to_string());
425/// ```
426#[macro_export]
427macro_rules! ttp {
428    ($i:ident, $e:expr) => {{
429        let mut $i = t!(t!($e));
430        $i.name = Some(stringify!($i).to_string());
431        set_tiles(format!("{}", stringify!($i)), $i.to_string());
432        set_raw_tiles(format!("{}", stringify!($i)), $i.clone());
433        $i
434    }};
435    ($i:ident, $($arg:tt)*) => {{
436        let val = format!($($arg)*);
437        let mut $i = t!(t!(val));
438        $i.name = Some(stringify!($i).to_string());
439        set_tiles(format!("{}", stringify!($i)), $i.to_string());
440        set_raw_tiles(format!("{}", stringify!($i)), $i.clone());
441        $i
442    }};
443}
444
445/// ttq! is to used to expand the inner tiles, persist the result to tls (thread local storage) using a variable name and return a trimmed tile
446///
447/// ```
448/// use rtile::prelude::*;
449///
450/// tp!(numbers, "1, 2, 3, 4, 5");
451/// let persisted_tile_name = "numbers";
452/// ttq!(persisted_tile_name, "Numbers: @{numbers}");
453/// assert_eq!(t!("@{numbers}").to_string(), "Numbers: 1, 2, 3, 4, 5".to_string());
454/// ```
455#[macro_export]
456macro_rules! ttq {
457    ($e:expr, $val:expr) => {{
458        let mut target_tile = t!(t!($val));
459        let target_tile_name = format!("{}", $e);
460        target_tile.name = Some(target_tile_name.clone());
461        set_tiles(target_tile_name.clone(), target_tile.to_string());
462        set_raw_tiles(target_tile_name, target_tile.clone());
463        target_tile
464    }};
465    ($e:expr, $($arg:tt)*) => {{
466        let val = format!($($arg)*);
467        let mut target_tile = t!(t!(val));
468        let target_tile_name = format!("{}", $e);
469        target_tile.name = Some(target_tile_name.clone());
470        set_tiles(target_tile_name.clone(), target_tile.to_string());
471        set_raw_tiles(target_tile_name, target_tile.clone());
472        target_tile
473    }};
474}
475
476/// sr! returns the trimmed raw data of a tile
477///
478/// ```
479/// use rtile::prelude::*;
480/// let tile = t!("
481///                @{numbers}
482///                @{alphabets}
483///                ");
484/// let result = sr!(tile);
485/// assert_eq!(result, "@{numbers}\n@{alphabets}".to_string());
486/// ```
487#[macro_export]
488macro_rules! sr {
489    ($e:expr) => {{
490        $e.raw()
491    }};
492    ($($arg:tt)*) => {{
493        let val = format!($($arg)*);
494        t!(val).raw()
495    }};
496}
497
498/// ts! is to expand any inner tiles and to trim the white spaces around the block of text and return a String
499/// ```
500/// use rtile::prelude::*;
501/// tp!(tile_one, "   one hundred   ");
502/// tp!(tile_two, "   two   ");
503/// let result = ts!("
504///                 @{tile_one}
505///                 @{tile_two}
506///                 ");
507/// assert_eq!(result, "one hundred\ntwo");
508///
509/// let result = ts!("
510///                     @{tile_one}
511///                     @{tile_two}    ");
512/// assert_eq!(result, "one hundred\ntwo");
513/// ```
514#[macro_export]
515macro_rules! ts {
516    () => {{
517        "".to_string()
518    }};
519    ($e:expr) => {{
520        t!($e).to_string()
521    }};
522    ($($arg:tt)*) => {{
523        let val = format!($($arg)*);
524        t!(val).to_string()
525    }};
526}
527
528#[doc(hidden)]
529///
530/// Tiles without trimming
531///
532/// k - keep white spaces - do_trimming: false
533///
534pub trait MacroAttributeForK {
535    #[doc(hidden)]
536    fn process(&self) -> RTile;
537}
538
539impl MacroAttributeForK for &str {
540    fn process(&self) -> RTile {
541        RTile::from_str_without_trimming(self)
542    }
543}
544
545impl MacroAttributeForK for String {
546    fn process(&self) -> RTile {
547        RTile::from_str_without_trimming(self.as_str())
548    }
549}
550
551impl MacroAttributeForK for &String {
552    fn process(&self) -> RTile {
553        RTile::from_str_without_trimming(self.as_str())
554    }
555}
556
557impl MacroAttributeForK for RTile {
558    fn process(&self) -> RTile {
559        RTile::from_str_without_trimming(self.to_string().as_str())
560    }
561}
562
563impl MacroAttributeForK for &RTile {
564    fn process(&self) -> RTile {
565        RTile::from_str_without_trimming(self.to_string().as_str())
566    }
567}
568
569impl MacroAttributeForK for Vec<&str> {
570    fn process(&self) -> RTile {
571        RTile::new_without_trimming_str(self.clone())
572    }
573}
574
575impl MacroAttributeForK for Vec<String> {
576    fn process(&self) -> RTile {
577        RTile::new_without_trimming(self.clone())
578    }
579}
580
581/// kf! is used to flatten the multilines of the tile output into a single string, without trimming the white spaces. i.e. keep the white spaces
582///
583/// ```
584/// use rtile::prelude::*;
585/// let v1 = vec!["  one  ", "  two  ", "  three  "];
586/// let val = k!(v1);
587/// assert_eq!(kf!(val), "  one      two      three  ");
588///
589/// let v1 = vec!["  one  ", "  two  ", "  three  "];
590/// let v2 = vec!["  1  ", "  2  ", "  3  "];
591/// let val = k!(v1) + k!(v2);
592/// assert_eq!(kf!(val), "  one      1    two      2    three    3  ");
593/// ```
594///        
595#[macro_export]
596macro_rules! kf {
597    ($t: expr) => {{
598        $t.to_string().split('\n').collect::<Vec<&str>>().join("")
599    }};
600}
601
602/// k! is to expand any inner tiles, to keep the white spaces (i.e. do not trim any white spaces around the block) and return a tile
603///
604/// ```
605/// use rtile::prelude::*;
606///
607/// let v1 = vec!["  one  ", "  two  ", "  three  "];
608/// let val = k!(v1);
609/// assert_eq!(val.to_string(), "  one    \n  two    \n  three  ");
610/// ```
611#[macro_export]
612macro_rules! k {
613    () => {{
614        RTile::new_without_trimming(vec![])
615    }};
616    ("") => {{
617        RTile {
618            name: None,
619            lns: vec!["".to_string()],
620            do_trimming: false,
621            marker: PhantomData::<Rc<()>>,
622        }
623    }};
624    ($e:expr) => {{
625        MacroAttributeForK::process(&$e)
626    }};
627    ($($arg:tt)*) => {{
628        let val = format!($($arg)*);
629        k!(val)
630    }};
631}
632
633/// kp! is to expand any inner tiles, to keep the white spaces (i.e. do not trim any white spaces around the block), with a given name (string literal) and return a tile
634///
635/// ```
636/// use rtile::prelude::*;
637///
638/// kp!(tile_1, " abc ");
639/// kp!(tile_2, "@{tile_1}");
640///
641/// let result = k!("@{tile_2}");
642/// assert_eq!(ks!(result), " abc ");
643/// ```
644#[macro_export]
645macro_rules! kp {
646    ($i:ident) => {{
647        let mut $i = k!();
648        $i.name = Some(stringify!($i).to_string());
649        set_tiles(format!("{}", stringify!($i)), $i.to_string());
650        set_raw_tiles(format!("{}", stringify!($i)), $i.clone());
651        $i
652    }};
653    ($i:ident, $e:expr) => {{
654        let mut $i = k!($e);
655        $i.name = Some(stringify!($i).to_string());
656        set_tiles(format!("{}", stringify!($i)), $i.to_string());
657        set_raw_tiles(format!("{}", stringify!($i)), $i.clone());
658        $i
659    }};
660    ($i:ident, $($arg:tt)*) => {{
661        let val = format!($($arg)*);
662        let mut $i = k!(val);
663        $i.name = Some(stringify!($i).to_string());
664        set_tiles(format!("{}", stringify!($i)), $i.to_string());
665        set_raw_tiles(format!("{}", stringify!($i)), $i.clone());
666        $i
667    }};
668}
669
670/// kq! is to expand any inner tiles, to keep the white spaces (i.e. do not trim any white spaces around the block), with a variable having a string value and return a tile
671///
672/// ```
673/// use rtile::prelude::*;
674/// let t1 = "tile_1";
675/// let t2 = "tile_2";
676///
677/// kq!(t1, " abc ");
678/// kq!(t2, "@{tile_1}");
679///
680/// let result = k!("@{tile_2}");
681/// assert_eq!(ks!(result), " abc ");
682/// ```
683#[macro_export]
684macro_rules! kq {
685    ($e:expr) => {{
686        let mut target_tile = k!();
687        let target_tile_name = format!("{}", $e);
688        target_tile.name = Some(target_tile_name.clone());
689        set_tiles(target_tile_name.clone(), target_tile.to_string());
690        set_raw_tiles(target_tile_name, target_tile.clone());
691        target_tile
692    }};
693    ($e:expr, $val:expr) => {{
694        let mut target_tile = k!($val);
695        let target_tile_name = format!("{}", $e);
696        target_tile.name = Some(target_tile_name.clone());
697        set_tiles(target_tile_name.clone(), target_tile.to_string());
698        set_raw_tiles(target_tile_name, target_tile.clone());
699        target_tile
700    }};
701    ($e:expr, $($arg:tt)*) => {{
702        let val = format!($($arg)*);
703        let mut target_tile = k!(val);
704        let target_tile_name = format!("{}", $e);
705        target_tile.name = Some(target_tile_name.clone());
706        set_tiles(target_tile_name.clone(), target_tile.to_string());
707        set_raw_tiles(target_tile_name, target_tile.clone());
708        target_tile
709    }};
710}
711
712/// kk! is to used to expand the inner tiles, by keeping the white spaces (i.e. do not trim any white spaces around the block) and return a tile
713///
714/// ```
715/// use rtile::prelude::*;
716///
717/// kp!(numbers, "     1, 2, 3, 4, 5     ");
718/// let mut result = kk!("  Numbers: @{numbers}  ");
719/// kp!(numbers, "   one, two, three, four, five   ");
720/// result |= k!("  In words: @{numbers}  ");
721/// assert_eq!(result.to_string(), ks!("  Numbers:      1, 2, 3, 4, 5       \n  In words:    one, two, three, four, five     "));
722/// ```
723#[macro_export]
724macro_rules! kk {
725    ($e:expr) => {{
726        k!(k!($e))
727    }};
728    ($($arg:tt)*) => {{
729        let val = format!($($arg)*);
730        k!(k!(val))
731    }};
732}
733
734/// kkp! is to used to expand the inner tiles, by keeping the white spaces (i.e. do not trim any white spaces around the block), persist the result to tls (thread local storage) using a string literal and return a tile
735///
736/// ```
737/// use rtile::prelude::*;
738///
739/// kp!(numbers, "     1, 2, 3, 4, 5     ");
740/// kkp!(numbers, "  Numbers: @{numbers}  ");
741/// assert_eq!(ks!("@{numbers}"), "  Numbers:      1, 2, 3, 4, 5       ".to_string());
742/// ```
743#[macro_export]
744macro_rules! kkp {
745    ($i:ident, $e:expr) => {{
746        let mut $i = k!(k!($e));
747        $i.name = Some(stringify!($i).to_string());
748        set_tiles(format!("{}", stringify!($i)), $i.to_string());
749        set_raw_tiles(format!("{}", stringify!($i)), $i.clone());
750        $i
751    }};
752    ($i:ident, $($arg:tt)*) => {{
753        let val = format!($($arg)*);
754        let mut $i = k!(k!(val));
755        $i.name = Some(stringify!($i).to_string());
756        set_tiles(format!("{}", stringify!($i)), $i.to_string());
757        set_raw_tiles(format!("{}", stringify!($i)), $i.clone());
758        $i
759    }};
760}
761
762/// kkq! is to used to expand the inner tiles, by keeping the white spaces (i.e. do not trim any white spaces around the block), persist the result to tls (thread local storage) using a variable having a string value and return a tile
763///
764/// ```
765/// use rtile::prelude::*;
766/// let tile_name = "numbers";
767/// kq!(tile_name, "     1, 2, 3, 4, 5     ");
768/// kkq!(tile_name, "  Numbers: @{numbers}  ");
769/// assert_eq!(ks!("@{numbers}"), "  Numbers:      1, 2, 3, 4, 5       ".to_string());
770/// ```
771#[macro_export]
772macro_rules! kkq {
773    ($e:expr, $val:expr) => {{
774        let mut target_tile = k!(k!($val));
775        let target_tile_name = format!("{}", $e);
776        target_tile.name = Some(target_tile_name.clone());
777        set_tiles(target_tile_name.clone(), target_tile.to_string());
778        set_raw_tiles(target_tile_name, target_tile.clone());
779        target_tile
780    }};
781    ($e:expr, $($arg:tt)*) => {{
782        let val = format!($($arg)*);
783        let mut target_tile = k!(k!(val));
784        let target_tile_name = format!("{}", $e);
785        target_tile.name = Some(target_tile_name.clone());
786        set_tiles(target_tile_name.clone(), target_tile.to_string());
787        set_raw_tiles(target_tile_name, target_tile.clone());
788        target_tile
789    }};
790}
791
792/// ks! is to expand any inner tiles by keeping the white spaces (i.e. do not trim any white spaces around the block) and return a String
793/// ```
794/// use rtile::prelude::*;
795/// kp!(tile_one, "   one   ");
796/// kp!(tile_two, "   two   ");
797/// let result = ks!("@{tile_one}, @{tile_two}");
798/// assert_eq!(result, "   one   ,    two   ");
799/// ```
800#[macro_export]
801macro_rules! ks {
802    () => {{
803        "".to_string()
804    }};
805    ($e:expr) => {{
806        k!($e).to_string()
807    }};
808    ($($arg:tt)*) => {{
809        let val = format!($($arg)*);
810        k!(val).to_string()
811    }};
812}
813
814thread_local! {
815    static TL_PROCESSED_TILES: RefCell<HashMap<String, String>> = RefCell::new(HashMap::new());
816    static TL_RAW_TILES: RefCell<HashMap<String, RTile>> = RefCell::new(HashMap::new());
817}
818
819#[doc(hidden)]
820pub fn set_tiles(key: String, value: String) {
821    TL_PROCESSED_TILES.with_borrow_mut(|v| v.insert(key, value));
822}
823
824#[doc(hidden)]
825pub fn set_raw_tiles(key: String, value: RTile) {
826    TL_RAW_TILES.with_borrow_mut(|v| v.insert(key, value));
827}
828
829#[doc(hidden)]
830pub fn get_raw_tile(key: &str) -> Option<RTile> {
831    let key = &key.to_string();
832
833    TL_RAW_TILES.with_borrow(|v| {
834        if v.contains_key(key) {
835            Some(v.get(key).unwrap().clone())
836        } else {
837            None
838        }
839    })
840}
841
842/// remove_tile, used to remove a tile by name from the tls (thread local storage)
843/// ```
844/// use rtile::prelude::*;
845///
846/// tp!(tile1, "one");
847/// tp!(tile2, "two");
848/// assert_eq!(ts!("@{tile1}-@{tile2}"), "one-two".to_string());
849/// //remove the tile
850/// remove_tile("tile1");
851/// //If the tile is not present, a blank tile would be created using that name
852/// assert_eq!(ts!("@{tile1}-@{tile2}"), "-two".to_string());
853/// remove_tile("tile2");
854/// //If the tile is not present, a blank tile would be created using that name
855/// assert_eq!(ts!("@{tile1}-@{tile2}"), "-".to_string());
856/// ```
857pub fn remove_tile(key: &str) {
858    let key = &key.to_string();
859    TL_RAW_TILES.with_borrow_mut(|v| v.remove(key));
860    TL_PROCESSED_TILES.with_borrow_mut(|v| v.remove(key));
861}
862
863/// clear_tiles, used to remove all tiles from the tls (thread local storage)
864/// ```
865/// use rtile::prelude::*;
866///
867/// tp!(tile1, "one");
868/// tp!(tile2, "two");
869/// assert_eq!(ts!("@{tile1}-@{tile2}"), "one-two".to_string());
870/// //remove the tile
871/// clear_tiles();
872/// //If the tile is not present, a blank tile would be created using that name
873/// assert_eq!(ts!("@{tile1}-@{tile2}"), "-".to_string());
874/// ```
875pub fn clear_tiles() {
876    TL_RAW_TILES.with_borrow_mut(|v| v.clear());
877    TL_PROCESSED_TILES.with_borrow_mut(|v| v.clear());
878}
879
880/// get_blank_tiles, used to return blank tiles stored in the tls (thread local storage)
881/// ```
882/// use rtile::prelude::*;
883///
884/// t!("@{tile1}-@{tile2}");
885///
886/// let result = get_blank_tiles();
887/// assert_eq!(result.contains(&"tile1".to_string()), true);
888/// assert_eq!(result.contains(&"tile2".to_string()), true);
889/// ```
890pub fn get_blank_tiles() -> HashSet<String> {
891    let mut blank_tiles = HashSet::new();
892    TL_RAW_TILES.with_borrow(|v| {
893        for (tile_name, tile) in v.iter() {
894            if tile.lns == Vec::<String>::new() {
895                assert!(!tile_name.is_empty());
896                blank_tiles.insert(tile_name.clone());
897            }
898        }
899    });
900    blank_tiles
901}
902
903fn trim<I, T>(t1: I, do_trimming: bool) -> Vec<String>
904where
905    I: IntoIterator<Item = T> + Debug,
906    T: Into<String>,
907{
908    let t1: Vec<String> = t1.into_iter().map(Into::into).collect();
909    if !do_trimming {
910        return t1;
911    }
912
913    let mut lns: Vec<String> = t1.into_iter().map(|ln| ln.trim_end().to_string()).collect();
914    lns = lns.into_iter().skip_while(|ln| ln.is_empty()).collect();
915    lns.reverse();
916    lns = lns.into_iter().skip_while(|ln| ln.is_empty()).collect();
917    lns.reverse();
918    let lf: Vec<&str> = lns
919        .iter()
920        .filter(|ln| !ln.is_empty())
921        .map(|ln| ln.as_str())
922        .collect();
923    let left = if lf.is_empty() {
924        0
925    } else {
926        lf.iter()
927            .map(|ln| ln.len() - ln.trim_start().len())
928            .min()
929            .unwrap_or(0_usize)
930    };
931    let result = lns
932        .into_iter()
933        .map(|ln| ln.chars().skip(left).collect())
934        .collect();
935    result
936}
937
938fn append<I, T>(t1: &mut Vec<String>, t2: I)
939where
940    I: IntoIterator<Item = T> + Debug,
941    T: Into<String>,
942{
943    let t2: Vec<String> = t2.into_iter().map(Into::into).collect();
944
945    let diff: i32 = t2.len() as i32 - t1.len() as i32;
946    if diff > 0 {
947        t1.extend(vec!["".to_owned(); diff as usize]);
948    }
949    let w = t1.iter().map(|s| s.chars().count()).max().unwrap_or(0);
950    for (i, s) in t2.into_iter().enumerate() {
951        t1[i] = format!("{:<w$}{}", t1[i], s, w = w);
952    }
953}
954
955enum ExtraSteps {
956    DoNothing,
957    DoAppendTheTextFromCursorToInnerTileNameOrTheEndOfLine,
958}
959
960fn get_next_inner_tile_name(
961    ln: &str,
962    current_cursor: &mut usize,
963    end: &mut usize,
964) -> Option<String> {
965    find_next_inner_tile_name(
966        ln,
967        current_cursor,
968        end,
969        &mut Vec::<String>::new(),
970        ExtraSteps::DoNothing,
971    )
972}
973
974fn find_next_inner_tile_name(
975    ln: &str,
976    current_cursor: &mut usize,
977    end: &mut usize,
978    curr: &mut Vec<String>,
979    extra_steps: ExtraSteps,
980) -> Option<String> {
981    let mut start = ln[*current_cursor..].find("@{").unwrap_or(ln.len());
982    if *current_cursor == ln.len() && start == ln.len() && *end == ln.len() && !ln.is_empty() {
983        return None;
984    }
985    if start < ln.len() {
986        start += *current_cursor;
987    }
988
989    if let ExtraSteps::DoAppendTheTextFromCursorToInnerTileNameOrTheEndOfLine = extra_steps {
990        append(curr, vec![&ln[*end..start]]);
991    }
992
993    if start == ln.len() {
994        return None;
995    }
996    *end = ln[start..].find('}').map_or(0, |i| i + 1);
997    if *end == 0 {
998        panic!("unfinished @{{}} expression");
999    }
1000    *end += start;
1001    *current_cursor = *end;
1002    let tile_name = ln[start + 2..*end - 1].to_string();
1003    Some(tile_name)
1004}
1005
1006fn r_format_using_processed_tiles_data(s: &str) -> Vec<String> {
1007    let lns: Vec<&str> = s.split('\n').collect();
1008    let mut res = vec![];
1009    for ln in lns {
1010        let mut curr = vec![];
1011        let mut current_cursor = 0_usize;
1012        let mut end = 0;
1013
1014        while let Some(tile_name) = find_next_inner_tile_name(
1015            ln,
1016            &mut current_cursor,
1017            &mut end,
1018            &mut curr,
1019            ExtraSteps::DoAppendTheTextFromCursorToInnerTileNameOrTheEndOfLine,
1020        ) {
1021            TL_PROCESSED_TILES.with_borrow(|v| {
1022                if v.contains_key(&tile_name) {
1023                    let tile_value = v.get(&tile_name).unwrap();
1024                    let lns: Vec<&str> = tile_value.split('\n').collect();
1025                    append(&mut curr, lns);
1026                } else {
1027                    println!("{} tile is not found", tile_name);
1028                }
1029            });
1030        }
1031        res.append(&mut curr);
1032    }
1033    res
1034}
1035
1036fn r_format_using_raw_tiles_data(s: &str) -> Vec<String> {
1037    let lns: Vec<&str> = s.split('\n').collect();
1038    let mut res = vec![];
1039    for ln in lns {
1040        let mut curr = vec![];
1041        let mut current_cursor = 0_usize;
1042        let mut end = 0;
1043
1044        while let Some(tile_name) = find_next_inner_tile_name(
1045            ln,
1046            &mut current_cursor,
1047            &mut end,
1048            &mut curr,
1049            ExtraSteps::DoAppendTheTextFromCursorToInnerTileNameOrTheEndOfLine,
1050        ) {
1051            TL_RAW_TILES.with_borrow(|v_raw| {
1052                if v_raw.contains_key(&tile_name) {
1053                    let tile_value = v_raw.get(&tile_name).unwrap();
1054                    check_for_recursion_of_tiles(&tile_name, tile_value);
1055                    process_all_required_tiles_data(&tile_name, tile_value);
1056
1057                    TL_PROCESSED_TILES.with_borrow(|v| {
1058                        if v.contains_key(&tile_name) {
1059                            let tile_value = v.get(&tile_name).unwrap();
1060                            let lns: Vec<&str> = tile_value.split('\n').collect();
1061                            append(&mut curr, lns);
1062                        } else {
1063                            println!("{} tile is not found", tile_name);
1064                        }
1065                    });
1066                } else {
1067                    println!("{} tile is not found", tile_name);
1068                }
1069            });
1070        }
1071        res.append(&mut curr);
1072    }
1073    res
1074}
1075
1076fn check_for_recursion_of_tiles(tile_name: &String, tile_value: &RTile) {
1077    let mut inner_tiles: Vec<String> = vec![];
1078    let mut processed_tiles: HashSet<String> = HashSet::new();
1079    let mut direct_parents: HashSet<String> = HashSet::new();
1080    direct_parents.insert(tile_name.clone());
1081    check_for_recursion_in_inner_tiles(
1082        tile_name,
1083        tile_value,
1084        &mut processed_tiles,
1085        &mut inner_tiles,
1086        &direct_parents,
1087    );
1088}
1089
1090fn process_all_required_tiles_data(tile_name: &String, tile_value: &RTile) {
1091    let mut inner_tiles: Vec<String> = vec![tile_name.clone()];
1092    let mut processed_tiles: HashSet<String> = HashSet::new();
1093
1094    find_inner_tiles(
1095        tile_name,
1096        tile_value,
1097        &mut processed_tiles,
1098        &mut inner_tiles,
1099    );
1100
1101    if !inner_tiles.is_empty() {
1102        for inner_tile_index in (0..inner_tiles.len()).rev() {
1103            let inner_tile_name = inner_tiles.get(inner_tile_index).unwrap();
1104
1105            let result = TL_RAW_TILES.with_borrow(|v| {
1106                if v.contains_key(inner_tile_name) {
1107                    let inner_tile_value = v.get(inner_tile_name).unwrap();
1108                    inner_tile_value.reevaluate()
1109                } else {
1110                    //tile not found, so return emtpy string
1111                    String::new()
1112                }
1113            });
1114
1115            TL_PROCESSED_TILES.with_borrow_mut(|v| v.insert(inner_tile_name.clone(), result));
1116        }
1117    }
1118}
1119
1120fn check_for_recursion_in_inner_tiles(
1121    tile_name: &String,
1122    tile_value: &RTile,
1123    processed_tiles: &mut HashSet<String>,
1124    inner_tiles: &mut Vec<String>,
1125    direct_parents: &HashSet<String>,
1126) {
1127    for ln in &tile_value.lns {
1128        let mut curr = vec![];
1129        let mut current_cursor = 0_usize;
1130        let mut end = 0;
1131
1132        while let Some(inner_tile_name) = find_next_inner_tile_name(
1133            ln,
1134            &mut current_cursor,
1135            &mut end,
1136            &mut curr,
1137            ExtraSteps::DoAppendTheTextFromCursorToInnerTileNameOrTheEndOfLine,
1138        ) {
1139            if processed_tiles.contains(&inner_tile_name) {
1140                continue;
1141            } else {
1142                TL_RAW_TILES.with_borrow(|v| {
1143                    if v.contains_key(&inner_tile_name) {
1144                        if direct_parents.contains(&inner_tile_name) {
1145                            panic!("detected a recursion");
1146                        } else {
1147                            let inner_tile_value = v.get(&inner_tile_name).unwrap();
1148                            inner_tiles.push(inner_tile_name.clone());
1149
1150                            let mut all_direct_parents = direct_parents.clone();
1151                            all_direct_parents.insert(inner_tile_name.clone());
1152                            check_for_recursion_in_inner_tiles(
1153                                &inner_tile_name,
1154                                inner_tile_value,
1155                                processed_tiles,
1156                                inner_tiles,
1157                                &all_direct_parents,
1158                            );
1159                        }
1160                    } else {
1161                        println!("{} tile is not found", inner_tile_name);
1162                    }
1163                });
1164            }
1165        }
1166        processed_tiles.insert(tile_name.to_string());
1167    }
1168}
1169
1170fn find_inner_tiles(
1171    tile_name: &String,
1172    tile_value: &RTile,
1173    processed_tiles: &mut HashSet<String>,
1174    inner_tiles: &mut Vec<String>,
1175) {
1176    for ln in &tile_value.lns {
1177        let mut curr = vec![];
1178        let mut current_cursor = 0_usize;
1179        let mut end = 0;
1180        while let Some(inner_tile_name) = find_next_inner_tile_name(
1181            ln,
1182            &mut current_cursor,
1183            &mut end,
1184            &mut curr,
1185            ExtraSteps::DoAppendTheTextFromCursorToInnerTileNameOrTheEndOfLine,
1186        ) {
1187            if processed_tiles.contains(&inner_tile_name) {
1188                continue;
1189            } else {
1190                TL_RAW_TILES.with_borrow(|v| {
1191                    if v.contains_key(&inner_tile_name) {
1192                        let inner_tile_value = v.get(&inner_tile_name).unwrap();
1193                        inner_tiles.push(inner_tile_name.clone());
1194
1195                        find_inner_tiles(
1196                            &inner_tile_name,
1197                            inner_tile_value,
1198                            processed_tiles,
1199                            inner_tiles,
1200                        );
1201                    } else {
1202                        println!("{} tile is not found", inner_tile_name);
1203                    }
1204                });
1205            }
1206        }
1207        processed_tiles.insert(tile_name.to_string());
1208    }
1209}
1210
1211fn identify_any_missing_inner_tiles(
1212    tile_name: Option<String>,
1213    tile_lns: &[String],
1214    processed_tiles: &mut HashSet<String>,
1215    missing_inner_tiles: &mut HashSet<String>,
1216) {
1217    for ln in tile_lns {
1218        let mut curr = vec![];
1219        let mut current_cursor = 0_usize;
1220        let mut end = 0;
1221        while let Some(inner_tile_name) = find_next_inner_tile_name(
1222            ln,
1223            &mut current_cursor,
1224            &mut end,
1225            &mut curr,
1226            ExtraSteps::DoAppendTheTextFromCursorToInnerTileNameOrTheEndOfLine,
1227        ) {
1228            if processed_tiles.contains(&inner_tile_name) {
1229                continue;
1230            } else {
1231                TL_RAW_TILES.with_borrow(|v| {
1232                    if v.contains_key(&inner_tile_name) {
1233                        let inner_tile_value = v.get(&inner_tile_name).unwrap();
1234
1235                        identify_any_missing_inner_tiles(
1236                            Some(inner_tile_name.clone()),
1237                            &inner_tile_value.lns,
1238                            processed_tiles,
1239                            missing_inner_tiles,
1240                        );
1241                    } else if missing_inner_tiles.contains(&inner_tile_name) {
1242                    } else {
1243                        missing_inner_tiles.insert(inner_tile_name.clone());
1244                    }
1245                });
1246            }
1247        }
1248        if tile_name.is_some() {
1249            processed_tiles.insert(tile_name.clone().unwrap());
1250        }
1251    }
1252}
1253
1254fn get_blank_inner_tiles_names(
1255    tile_name: Option<String>,
1256    tile_lns: &[String],
1257    processed_tiles: &mut HashSet<String>,
1258    blank_inner_tiles: &mut Vec<String>,
1259) {
1260    for ln in tile_lns {
1261        let mut curr = vec![];
1262        let mut current_cursor = 0_usize;
1263        let mut end = 0;
1264        while let Some(inner_tile_name) = find_next_inner_tile_name(
1265            ln,
1266            &mut current_cursor,
1267            &mut end,
1268            &mut curr,
1269            ExtraSteps::DoAppendTheTextFromCursorToInnerTileNameOrTheEndOfLine,
1270        ) {
1271            if processed_tiles.contains(&inner_tile_name) {
1272                continue;
1273            } else {
1274                TL_RAW_TILES.with_borrow(|v| {
1275                    if v.contains_key(&inner_tile_name) {
1276                        let inner_tile_value = v.get(&inner_tile_name).unwrap();
1277                        if inner_tile_value.lns == Vec::<String>::new() {
1278                            blank_inner_tiles.push(inner_tile_name.clone());
1279                        }
1280
1281                        get_blank_inner_tiles_names(
1282                            Some(inner_tile_name),
1283                            &inner_tile_value.lns,
1284                            processed_tiles,
1285                            blank_inner_tiles,
1286                        );
1287                    } else {
1288                        println!("{} tile is not found", inner_tile_name);
1289                    }
1290                });
1291            }
1292        }
1293        if tile_name.is_some() {
1294            processed_tiles.insert(tile_name.clone().unwrap());
1295        }
1296    }
1297}
1298
1299#[doc(hidden)]
1300#[derive(Debug, Clone, PartialEq)]
1301pub struct RTile {
1302    pub name: Option<String>,
1303    pub lns: Vec<String>,
1304    pub do_trimming: bool,
1305    pub marker: PhantomData<Rc<()>>,
1306}
1307
1308impl RTile {
1309    pub fn new_str(lns: Vec<&str>) -> Self {
1310        let lns: Vec<String> = lns.iter().map(|&item| item.to_string()).collect();
1311        create_blank_tiles_of_any_missing_inner_tiles(None, &lns);
1312        Self {
1313            name: None,
1314            lns: trim(lns, true),
1315            do_trimming: true,
1316            marker: PhantomData::<Rc<()>>,
1317        }
1318    }
1319
1320    pub fn new(lns: Vec<String>) -> Self {
1321        create_blank_tiles_of_any_missing_inner_tiles(None, &lns);
1322        Self {
1323            name: None,
1324            lns: trim(lns, true),
1325            do_trimming: true,
1326            marker: PhantomData::<Rc<()>>,
1327        }
1328    }
1329
1330    pub fn construct_from_str(val: &str) -> Self {
1331        let lns: Vec<&str> = val.split('\n').collect();
1332        let lns: Vec<String> = lns.iter().map(|&item| item.to_string()).collect();
1333        create_blank_tiles_of_any_missing_inner_tiles(None, &lns);
1334        Self {
1335            name: None,
1336            lns: trim(lns, true),
1337            do_trimming: true,
1338            marker: PhantomData::<Rc<()>>,
1339        }
1340    }
1341
1342    pub fn new_without_trimming_str(lns: Vec<&str>) -> Self {
1343        let lns: Vec<String> = lns.iter().map(|&item| item.to_string()).collect();
1344        create_blank_tiles_of_any_missing_inner_tiles(None, &lns);
1345        Self {
1346            name: None,
1347            lns: trim(lns, false),
1348            do_trimming: false,
1349            marker: PhantomData::<Rc<()>>,
1350        }
1351    }
1352
1353    pub fn new_without_trimming(lns: Vec<String>) -> Self {
1354        create_blank_tiles_of_any_missing_inner_tiles(None, &lns);
1355        Self {
1356            name: None,
1357            lns: trim(lns, false),
1358            do_trimming: false,
1359            marker: PhantomData::<Rc<()>>,
1360        }
1361    }
1362
1363    pub fn from_str_without_trimming(val: &str) -> Self {
1364        let lns: Vec<&str> = val.split('\n').collect();
1365        let lns: Vec<String> = lns.iter().map(|&item| item.to_string()).collect();
1366        create_blank_tiles_of_any_missing_inner_tiles(None, &lns);
1367        Self {
1368            name: None,
1369            lns: trim(lns, false),
1370            do_trimming: false,
1371            marker: PhantomData::<Rc<()>>,
1372        }
1373    }
1374
1375    pub fn get_names_of_blank_inner_tiles(&self) -> Vec<String> {
1376        let mut processed_tiles: HashSet<String> = HashSet::new();
1377        let mut blank_inner_tiles = vec![];
1378        get_blank_inner_tiles_names(
1379            self.name.clone(),
1380            &self.lns,
1381            &mut processed_tiles,
1382            &mut blank_inner_tiles,
1383        );
1384        blank_inner_tiles
1385    }
1386
1387    pub fn reevaluate(&self) -> String {
1388        // calling r_format_using_processed_tiles_data, as all the inner tiles are supposed to be reevaluated / processed by now
1389        trim(
1390            r_format_using_processed_tiles_data(self.lns.join("\n").as_str()),
1391            self.do_trimming,
1392        )
1393        .join("\n")
1394    }
1395
1396    pub fn join<T: Display + Debug>(&self, x: &[T], last: Option<RTile>) -> Self {
1397        let mut res = RTile::new_without_trimming(vec![]);
1398        for (idx, item) in x.iter().enumerate() {
1399            match item {
1400                i if type_name::<T>() == "rtile::RTile" => {
1401                    let lns = i
1402                        .to_string()
1403                        .split('\n')
1404                        .collect::<Vec<&str>>()
1405                        .iter()
1406                        .map(|&item| item.to_string())
1407                        .collect();
1408                    res += RTile::new_without_trimming(lns);
1409                }
1410                i => {
1411                    res += RTile::construct_from_str(i.to_string().as_str());
1412                }
1413            };
1414            if idx < x.len() - 1 {
1415                res += self.clone();
1416            }
1417        }
1418        match last {
1419            Some(t) => res + t,
1420            None => res,
1421        }
1422    }
1423
1424    pub fn vjoin<T: Display + Debug>(&self, x: &[T], inline: bool, last: Option<RTile>) -> Self {
1425        let last = last.unwrap_or_else(|| RTile::new(vec![]));
1426        let mut res = RTile::new_without_trimming(vec![]);
1427        for (idx, item) in x.iter().enumerate() {
1428            match item {
1429                i if type_name::<T>() == "rtile::RTile" => {
1430                    let lns = i
1431                        .to_string()
1432                        .split('\n')
1433                        .collect::<Vec<&str>>()
1434                        .iter()
1435                        .map(|&item| item.to_string())
1436                        .collect();
1437
1438                    res |= RTile::new_without_trimming(lns)
1439                        + if inline {
1440                            if idx < x.len() - 1 {
1441                                self.clone()
1442                            } else {
1443                                last.clone()
1444                            }
1445                        } else {
1446                            RTile::construct_from_str("")
1447                        }
1448                }
1449                i => {
1450                    res |= RTile::construct_from_str(i.to_string().as_str())
1451                        + if inline {
1452                            if idx < x.len() - 1 {
1453                                self.clone()
1454                            } else {
1455                                last.clone()
1456                            }
1457                        } else {
1458                            RTile::construct_from_str("")
1459                        }
1460                }
1461            };
1462            if !inline {
1463                let tile = if idx < x.len() - 1 {
1464                    self.clone()
1465                } else {
1466                    last.clone()
1467                };
1468                res |= tile;
1469            }
1470        }
1471        res
1472    }
1473
1474    ///
1475    /// This function returns the trimmed raw data of the tile
1476    ///
1477    /// ex:
1478    /// let tile = t!("   @{another_tile_one}             @{another_tile_two}       ");
1479    /// calling tile.raw(); would return a trimmed, non-expanded raw data of the tile
1480    /// so tile.raw() == "@{another_tile_one}             @{another_tile_two}".to_string();
1481    pub fn raw(&self) -> String {
1482        trim(self.lns.clone(), self.do_trimming)
1483            .join("\n")
1484            .to_string()
1485    }
1486
1487    pub fn has_inner_tiles_in_raw_data(&self) -> bool {
1488        for ln in &self.lns {
1489            let start = ln[..].find("@{").unwrap_or(ln.len());
1490            if start == ln.len() {
1491                continue;
1492            }
1493            let end = ln[start..].find('}').map_or(0, |i| i + 1);
1494            if end == 0 {
1495                panic!("unfinished @{{}} expression");
1496            }
1497            return true;
1498        }
1499        false
1500    }
1501
1502    pub fn inner_tiles_in_raw_data(&self) -> Vec<Vec<String>> {
1503        let mut result = vec![];
1504        for ln in &self.lns {
1505            let mut tiles_on_line = vec![];
1506            let mut current_cursor = 0_usize;
1507            let mut end = 0;
1508            while let Some(tile_name) = get_next_inner_tile_name(ln, &mut current_cursor, &mut end)
1509            {
1510                tiles_on_line.push(tile_name.clone());
1511                current_cursor = end;
1512            }
1513            result.push(tiles_on_line);
1514        }
1515        result
1516    }
1517
1518    pub fn inner_tiles(&self) -> HashSet<String> {
1519        let mut inner_tiles: Vec<String> = vec![];
1520        let mut processed_tiles: HashSet<String> = HashSet::new();
1521
1522        find_inner_tiles(&String::new(), self, &mut processed_tiles, &mut inner_tiles);
1523
1524        inner_tiles.into_iter().collect()
1525    }
1526
1527    pub fn flatten(&self) -> String {
1528        self.lns
1529            .iter()
1530            .map(|item| item.trim())
1531            .collect::<Vec<&str>>()
1532            .join("")
1533    }
1534
1535    pub fn dimensions(&self) -> (usize, usize) {
1536        let width = self
1537            .lns
1538            .iter()
1539            .map(|s| s.chars().count())
1540            .max()
1541            .unwrap_or(0);
1542        let height = self.lns.len();
1543        (width, height)
1544    }
1545}
1546
1547fn create_blank_tiles_of_any_missing_inner_tiles(name: Option<String>, lns: &[String]) {
1548    let mut processed_tiles: HashSet<String> = HashSet::new();
1549    let mut missing_inner_tiles: HashSet<String> = HashSet::new();
1550    identify_any_missing_inner_tiles(name, lns, &mut processed_tiles, &mut missing_inner_tiles);
1551    if !missing_inner_tiles.is_empty() {
1552        for missing_inner_tile_name in missing_inner_tiles {
1553            TL_RAW_TILES.with_borrow_mut(|v| {
1554                v.insert(
1555                    missing_inner_tile_name.clone(),
1556                    RTile {
1557                        name: Some(missing_inner_tile_name.clone()),
1558                        lns: vec![],
1559                        do_trimming: true,
1560                        marker: PhantomData::<Rc<()>>,
1561                    },
1562                )
1563            });
1564            TL_PROCESSED_TILES
1565                .with_borrow_mut(|v| v.insert(missing_inner_tile_name.clone(), String::new()));
1566        }
1567    }
1568}
1569
1570impl Add for RTile {
1571    type Output = Self;
1572
1573    fn add(self, other: RTile) -> Self::Output {
1574        let mut lns = self.lns.clone();
1575        append(&mut lns, other.lns);
1576
1577        create_blank_tiles_of_any_missing_inner_tiles(None, &lns);
1578
1579        Self {
1580            name: None,
1581            lns,
1582            do_trimming: self.do_trimming,
1583            marker: PhantomData::<Rc<()>>,
1584        }
1585    }
1586}
1587
1588impl AddAssign for RTile {
1589    fn add_assign(&mut self, other: Self) {
1590        append(&mut self.lns, other.lns);
1591    }
1592}
1593
1594impl BitOr for RTile {
1595    type Output = Self;
1596
1597    fn bitor(self, other: RTile) -> Self::Output {
1598        let lns = [&self.lns[..], &other.lns[..]].concat();
1599
1600        create_blank_tiles_of_any_missing_inner_tiles(None, &lns);
1601
1602        Self {
1603            name: None,
1604            lns,
1605            do_trimming: self.do_trimming,
1606            marker: PhantomData::<Rc<()>>,
1607        }
1608    }
1609}
1610
1611impl BitOrAssign for RTile {
1612    fn bitor_assign(&mut self, other: Self) {
1613        self.lns = [&self.lns[..], &other.lns[..]].concat();
1614    }
1615}
1616
1617impl Display for RTile {
1618    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1619        if self.do_trimming {
1620            write!(
1621                f,
1622                "{}",
1623                trim(
1624                    r_format_using_raw_tiles_data(self.lns.join("\n").as_str()),
1625                    true,
1626                )
1627                .join("\n")
1628            )
1629        } else {
1630            let ktile_vec = r_format_using_raw_tiles_data(self.lns.join("\n").as_str());
1631            let blank_vec = vec![""; ktile_vec.len()];
1632            let output = k!(blank_vec) + k!(ktile_vec) + k!(blank_vec);
1633            write!(
1634                f,
1635                "{}",
1636                r_format_using_raw_tiles_data(output.lns.join("\n").as_str()).join("\n")
1637            )
1638        }
1639    }
1640}