1use std::collections::HashMap;
2use std::hash::Hash;
3use std::str::FromStr;
4
5mod esp;
7mod parser;
8
9#[cfg(feature = "yaml")]
10pub use esp::formats;
11
12#[cfg_attr(feature = "yaml", derive(serde::Deserialize))]
13#[cfg_attr(feature = "yaml", serde(try_from = "Option<String>"))]
14#[derive(Debug, Clone)]
15pub struct PossibleFormat<P: Placeholder> {
16 pub format: Option<Format<P>>,
17}
18
19impl<P: Placeholder> std::convert::TryFrom<Option<String>> for PossibleFormat<P> {
20 type Error = nom::Err<(String, nom::error::ErrorKind)>;
21
22 fn try_from(value: Option<String>) -> Result<Self, Self::Error> {
23 match value {
24 Some(x) => std::convert::TryFrom::try_from(x).map(|format| Self {
25 format: Some(format),
26 }),
27 None => Ok(Self { format: None }),
28 }
29 }
30}
31
32impl<P: Placeholder + Hash + Eq + Clone> PossibleFormat<P> {
33 pub fn filled_in_string(
34 &self,
35 feature_name: &str,
36 placeholders: &HashMap<P, Vec<String>>,
37 ) -> Result<String, FillInError<P>> {
38 if let Some(format) = self.format.as_ref() {
39 format.filled_in_string(placeholders)
40 } else {
41 Err(FillInError::FeatureNotSetUp(feature_name.to_string()))
42 }
43 }
44}
45
46impl<P: Placeholder + Hash + Eq + Clone> PossibleFormat<P> {
47 pub fn fill_in<W: std::fmt::Write>(
48 &self,
49 f: &mut W,
50 feature_name: &str,
51 placeholders: &HashMap<P, Vec<String>>,
52 ) -> Result<(), FillInError<P>> {
53 if let Some(format) = self.format.as_ref() {
54 format.fill_in(f, placeholders)
55 } else {
56 Err(FillInError::FeatureNotSetUp(feature_name.to_string()))
57 }
58 }
59}
60
61impl<P: Placeholder + Clone> PossibleFormat<P> {
62 pub fn combine(self, other: &Self) -> Self {
63 if let Some(format) = self.format {
64 Self {
65 format: Some(format),
66 }
67 } else {
68 other.clone()
69 }
70 }
71}
72
73impl<P: Placeholder> PossibleFormat<P> {
74 pub fn empty() -> Self {
75 Self { format: None }
76 }
77}
78
79#[derive(PartialEq, Debug, Clone)]
80#[cfg_attr(feature = "yaml", derive(serde::Deserialize))]
81#[cfg_attr(feature = "yaml", serde(try_from = "String"))]
82pub struct Format<P: Placeholder> {
83 pub items: Vec<FormatItem<P>>,
84}
85
86impl<P: Placeholder> FromStr for Format<P> {
87 type Err = nom::Err<(String, nom::error::ErrorKind)>;
88
89 fn from_str(s: &str) -> Result<Self, Self::Err> {
90 parser::format::<P>(s)
91 .map(|x| x.1)
92 .map_err(|x| x.map(|y| (y.input.to_string(), y.code)))
93 }
94}
95
96impl<P: Placeholder> std::convert::TryFrom<String> for Format<P> {
97 type Error = nom::Err<(String, nom::error::ErrorKind)>;
98 fn try_from(value: String) -> Result<Self, Self::Error> {
99 Self::from_str(&value)
100 }
101}
102
103impl<P: Placeholder> std::convert::From<Format<P>> for PossibleFormat<P> {
104 fn from(value: Format<P>) -> Self {
105 Self {
106 format: Some(value),
107 }
108 }
109}
110
111impl<P: Placeholder + Hash + Eq + Clone> Format<P> {
112 pub fn filled_in_string(
113 &self,
114 placeholders: &HashMap<P, Vec<String>>,
115 ) -> Result<String, FillInError<P>> {
116 let mut s = String::new();
117 self.fill_in(&mut s, placeholders)?;
118 Ok(s)
119 }
120}
121
122impl<P: Placeholder + Hash + Eq + Clone> Format<P> {
123 pub fn fill_in<W: std::fmt::Write>(
124 &self,
125 f: &mut W,
126 placeholders: &HashMap<P, Vec<String>>,
127 ) -> Result<(), FillInError<P>> {
128 for item in self.items.iter() {
129 item.fill_in(f, placeholders)?;
130 }
131 Ok(())
132 }
133}
134
135#[derive(PartialEq, Debug, Clone)]
136pub enum FormatItem<P: Placeholder> {
137 Word(String),
139 Placeholder(P),
140 PlaceholderList {
141 placeholder: P,
142 item_format: Format<P>,
143 join_format: Format<P>,
144 },
145 EscapedChar(FormatEscapeChar),
146 Indented(Format<P>),
147}
148
149#[cfg(test)]
150impl<P: Placeholder> FormatItem<P> {
151 pub fn from_sentence(s: &str) -> Vec<Self> {
152 let items = s.split(" ");
153 let mut result = Vec::new();
154 for (idx, item) in items.enumerate() {
155 if idx > 0 {
156 result.push(Self::EscapedChar(FormatEscapeChar::Space));
157 }
158 result.push(Self::Word(item.to_string()))
159 }
160 result
161 }
162}
163
164fn indent(s: String, nb_spaces: usize) -> String {
165 s.split("\n")
166 .map(|line| {
167 let spaces = " ".repeat(nb_spaces);
168 format!("{}{}", spaces, line)
169 })
170 .collect::<Vec<_>>()
171 .join("\n")
172}
173
174#[derive(Debug)]
175pub enum FillInError<Placeholder> {
176 Fmt(std::fmt::Error),
177 MissingPlaceholder(Placeholder),
178 PlaceholderCouldNotBeSubstituted(Placeholder),
179 PlaceholderGotMultipleItems(Placeholder),
180 FeatureNotSetUp(String),
181}
182
183impl<P: Placeholder + Hash + Eq + Clone> FormatItem<P> {
184 pub fn fill_in<W: std::fmt::Write>(
185 &self,
186 f: &mut W,
187 placeholders: &HashMap<P, Vec<String>>,
188 ) -> Result<(), FillInError<P>> {
189 match self {
190 FormatItem::Word(w) => write!(f, "{}", w).map_err(FillInError::Fmt),
191 FormatItem::Placeholder(p) => {
192 if let Some(v) = placeholders.get(p) {
193 if let Some(item) = v.get(0) {
194 write!(f, "{}", item).map_err(FillInError::Fmt)
195 } else if v.is_empty() {
196 Err(FillInError::PlaceholderCouldNotBeSubstituted(p.clone()))
197 } else {
198 Err(FillInError::PlaceholderGotMultipleItems(p.clone()))
199 }
200 } else {
201 Err(FillInError::MissingPlaceholder(p.clone()))
202 }
203 }
204 FormatItem::PlaceholderList {
205 placeholder: p,
206 item_format,
207 join_format,
208 } => {
209 if let Some(v) = placeholders.get(p) {
210 if v.is_empty() {
211 Err(FillInError::PlaceholderCouldNotBeSubstituted(p.clone()))
212 } else {
213 for (idx, item) in v.iter().enumerate() {
214 if idx > 0 {
216 join_format.fill_in(f, &Default::default())?;
217 }
218 item_format.fill_in(
219 f,
220 &vec![(p.clone(), vec![item.clone()])].into_iter().collect(),
221 )?;
222 }
223 Ok(())
224 }
225 } else {
226 Err(FillInError::MissingPlaceholder(p.clone()))
227 }
228 }
229 FormatItem::EscapedChar(format_escape_char) => {
230 format_escape_char.fill_in(f).map_err(FillInError::Fmt)
231 }
232 FormatItem::Indented(format) => {
233 let mut s = String::new();
234 format.fill_in(&mut s, placeholders)?;
235 write!(f, "{}", indent(s, 4)).map_err(FillInError::Fmt)
236 }
237 }
238 .map(|_| ())
239 }
240}
241
242#[derive(PartialEq, Debug, Clone, Copy)]
243pub enum FormatEscapeChar {
244 Newline,
246 Space,
248 Slash,
250 Ampersand,
252 Dollar,
254 Indent,
256 Dedent,
258}
259
260impl FormatEscapeChar {
261 pub fn fill_in<W: std::fmt::Write>(&self, f: &mut W) -> Result<(), std::fmt::Error> {
262 match self {
263 FormatEscapeChar::Newline => f.write_char('\n'),
264 FormatEscapeChar::Space => f.write_char(' '),
265 FormatEscapeChar::Slash => f.write_char('\\'),
266 FormatEscapeChar::Ampersand => f.write_char('&'),
267 FormatEscapeChar::Dollar => f.write_char('$'),
268 FormatEscapeChar::Indent => f.write_str(">>>>"),
269 FormatEscapeChar::Dedent => f.write_str("<<<<"),
270 }
271 .map(|_| ())
272 }
273}
274
275#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
276#[cfg_attr(feature = "yaml", derive(serde::Deserialize))]
277pub struct EmptyPlaceholder;
278
279impl std::fmt::Display for EmptyPlaceholder {
280 fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
281 Ok(())
282 }
283}
284
285impl<K> crate::config::Formattable<K> for EmptyPlaceholder {
286 fn format(&self, _formatting: &K) -> Result<String, crate::config::PrettyPrintError> {
287 Ok(String::new())
288 }
289}
290
291impl Placeholder for EmptyPlaceholder {
292 fn parser<'a>(input: &'a str) -> nom::IResult<&'a str, Self> {
293 nom::Parser::parse(
294 &mut nom::combinator::fail::<_, EmptyPlaceholder, _>(),
295 input,
296 )
297 }
298
299 fn name(&self) -> &'static str {
300 "no placeholders allowed here"
302 }
303}
304
305#[cfg(test)]
306mod tests {
307 use super::*;
308
309 #[test]
310 fn test_escape_chars() {
311 let mut s = String::new();
312 FormatEscapeChar::Ampersand.fill_in(&mut s).unwrap();
313 assert_eq!(s, "&");
314 FormatEscapeChar::Dollar.fill_in(&mut s).unwrap();
315 assert_eq!(s, "&$");
316 FormatEscapeChar::Newline.fill_in(&mut s).unwrap();
317 assert_eq!(s, "&$\n");
318 FormatEscapeChar::Slash.fill_in(&mut s).unwrap();
319 assert_eq!(s, "&$\n\\");
320 FormatEscapeChar::Space.fill_in(&mut s).unwrap();
321 assert_eq!(s, "&$\n\\ ");
322 FormatEscapeChar::Indent.fill_in(&mut s).unwrap();
323 assert_eq!(s, "&$\n\\ >>>>");
324 FormatEscapeChar::Dedent.fill_in(&mut s).unwrap();
325 assert_eq!(s, "&$\n\\ >>>><<<<");
326 }
327}
328
329pub trait Placeholder: Sized {
330 fn parser<'a>(input: &'a str) -> nom::IResult<&'a str, Self>;
331
332 fn name(&self) -> &'static str;
333}