clip_core/describe/
formatter.rs1#[derive(Default)]
20pub struct Formatter<'a> {
21 pub very_start: Option<&'a str>,
22 pub very_end: Option<&'a str>,
23 pub start: Option<&'a str>,
24 pub end: Option<&'a str>,
25 pub middle: Option<&'a str>,
26 pub new_line_chars: Option<&'a str>,
27}
28
29pub fn start_with(string: String, chars: &str) -> String {
31 let mut result = String::new();
32 for line in string.lines() {
33 result.push_str(format!("{chars}{line}\n").as_str());
34 }
35 result
36}
37
38pub fn start_other_lines_with(string: String, chars: &str) -> String {
39 let mut iterator = string.lines();
40 let mut result = String::new();
41 if let Some(first_line) = iterator.next() {
42 result.push_str(format!("{first_line}\n").as_str());
43 }
44 for line in iterator {
45 result.push_str(format!("{chars}{line}\n").as_str());
46 }
47 result
48}
49
50impl<'a> Formatter<'a> {
51 pub fn fmt<'b, Item: 'b, I: Iterator<Item = &'b Item>, F: FnMut(I::Item) -> Option<String>>(
52 &self,
53 args: I,
54 format_function: F,
55 ) -> String {
56 format!(
57 "{very_start}{description}{very_end}",
58 very_start = self.very_start.unwrap_or(""),
59 very_end = self.very_end.unwrap_or(""),
60 description = args.filter_map(format_function).fold(
61 "".to_string(),
62 |string: String, item: String| {
63 format!(
64 "{string}{middle}{start}{content}{end}",
65 start = self.start.unwrap_or(""),
66 content = if let Some(chars) = self.new_line_chars {
67 start_other_lines_with(item, chars)
68 } else { item },
69 middle = if string.is_empty() {
70 ""
71 } else {
72 self.middle.unwrap_or("")
73 },
74 end = self.end.unwrap_or("")
75 )
76 }
77 )
78 )
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 #[test]
86 fn default_formatter() {
87 assert_eq!(
88 Formatter::default().fmt([1, 2, 3].iter(), |item| Some(item.to_string())),
89 String::from("123")
90 );
91 }
92
93 #[test]
94 fn it_should_fmt_with_start_and_end() {
95 assert_eq!(
96 Formatter {
97 start: Some("<"),
98 end: Some(">"),
99 ..Default::default()
100 }
101 .fmt([1, 2, 3].iter(), |item| Some(item.to_string())),
102 "<1><2><3>"
103 );
104 }
105
106 #[test]
107 fn it_should_fmt_with_middle_char() {
108 assert_eq!(
109 Formatter {
110 middle: Some(" "),
111 ..Default::default()
112 }
113 .fmt([1, 2, 3].iter(), |item| Some(item.to_string())),
114 "1 2 3"
115 );
116 }
117
118 #[test]
119 fn it_should_fmt_with_start_end_middle_char() {
120 assert_eq!(
121 Formatter {
122 start: Some("<"),
123 end: Some(">"),
124 middle: Some(" "),
125 very_start: Some("Result: "),
126 very_end: Some("."),
127 new_line_chars: None,
128 }
129 .fmt([1, 2, 3].iter(), |item| Some(item.to_string())),
130 "Result: <1> <2> <3>."
131 );
132 }
133
134 #[test]
135 fn it_should_fmt_and_filter_none_values() {
136 assert_eq!(
137 Formatter {
138 start: Some("<"),
139 end: Some(">"),
140 middle: Some(","),
141 ..Default::default()
142 }
143 .fmt([1, 2, 3, 4, 5, 4, 3, 5].iter(), |item| {
144 if item % 2 == 0 {
145 Some(item.to_string())
146 } else {
147 None
148 }
149 }),
150 "<2>,<4>,<4>"
151 );
152 assert_eq!(
153 Formatter {
154 start: Some("<"),
155 end: Some(">"),
156 middle: Some(","),
157 ..Default::default()
158 }
159 .fmt([1, 2, 3, 4, 5, 4, 3, 5].iter(), |item| {
160 if item % 2 != 0 {
161 Some(item.to_string())
162 } else {
163 None
164 }
165 }),
166 "<1>,<3>,<5>,<3>,<5>"
167 );
168 }
169}