i3status_rs/formatting/
template.rs

1use super::formatter::{Formatter, new_formatter};
2use super::{FormatError, Fragment, Values, parse};
3use crate::config::SharedConfig;
4use crate::errors::*;
5
6use std::str::FromStr;
7use std::sync::Arc;
8
9#[derive(Debug, Clone)]
10pub struct FormatTemplate(Arc<[TokenList]>);
11
12impl Default for FormatTemplate {
13    fn default() -> Self {
14        Self(Arc::new([]))
15    }
16}
17
18#[derive(Debug)]
19pub struct TokenList(pub Vec<Token>);
20
21#[derive(Debug)]
22pub enum Token {
23    Text(String),
24    Recursive(FormatTemplate),
25    Placeholder {
26        name: String,
27        formatter: Option<Box<dyn Formatter>>,
28    },
29    Icon {
30        name: String,
31    },
32}
33
34impl FormatTemplate {
35    pub fn contains_key(&self, key: &str) -> bool {
36        self.0.iter().any(|token_list| {
37            token_list.0.iter().any(|token| match token {
38                Token::Placeholder { name, .. } => name == key,
39                Token::Recursive(rec) => rec.contains_key(key),
40                _ => false,
41            })
42        })
43    }
44
45    pub fn render(
46        &self,
47        values: &Values,
48        config: &SharedConfig,
49    ) -> Result<Vec<Fragment>, FormatError> {
50        for (i, token_list) in self.0.iter().enumerate() {
51            match token_list.render(values, config) {
52                Ok(res) => return Ok(res),
53                Err(
54                    FormatError::PlaceholderNotFound(_)
55                    | FormatError::IncompatibleFormatter { .. }
56                    | FormatError::NumberOutOfRange(_),
57                ) if i != self.0.len() - 1 => (),
58                Err(e) => return Err(e),
59            }
60        }
61        Ok(Vec::new())
62    }
63
64    pub fn init_intervals(&self, intervals: &mut Vec<u64>) {
65        for tl in self.0.iter() {
66            for t in &tl.0 {
67                match t {
68                    Token::Recursive(r) => r.init_intervals(intervals),
69                    Token::Placeholder {
70                        formatter: Some(f), ..
71                    } => {
72                        if let Some(i) = f.interval() {
73                            intervals.push(i.as_millis() as u64);
74                        }
75                    }
76                    _ => (),
77                }
78            }
79        }
80    }
81}
82
83impl TokenList {
84    pub fn render(
85        &self,
86        values: &Values,
87        config: &SharedConfig,
88    ) -> Result<Vec<Fragment>, FormatError> {
89        let mut retval = Vec::new();
90        let mut cur = Fragment::default();
91        for token in &self.0 {
92            match token {
93                Token::Text(text) => {
94                    if cur.metadata.is_default() {
95                        cur.text.push_str(text);
96                    } else {
97                        if !cur.text.is_empty() {
98                            retval.push(cur);
99                        }
100                        cur = text.clone().into();
101                    }
102                }
103                Token::Recursive(rec) => {
104                    if !cur.text.is_empty() {
105                        retval.push(cur);
106                    }
107                    retval.extend(rec.render(values, config)?);
108                    cur = retval.pop().unwrap_or_default();
109                }
110                Token::Placeholder { name, formatter } => {
111                    let value = values
112                        .get(name.as_str())
113                        .ok_or_else(|| FormatError::PlaceholderNotFound(name.into()))?;
114                    let formatter = formatter
115                        .as_ref()
116                        .map(Box::as_ref)
117                        .unwrap_or_else(|| value.default_formatter());
118                    let formatted = formatter.format(&value.inner, config)?;
119                    if value.metadata == cur.metadata {
120                        cur.text.push_str(&formatted);
121                    } else {
122                        if !cur.text.is_empty() {
123                            retval.push(cur);
124                        }
125                        cur = Fragment {
126                            text: formatted,
127                            metadata: value.metadata,
128                        };
129                    }
130                }
131                Token::Icon { name } => {
132                    let icon = config.get_icon(name, None)?;
133                    if cur.metadata.is_default() {
134                        cur.text.push_str(&icon);
135                    } else {
136                        if !cur.text.is_empty() {
137                            retval.push(cur);
138                        }
139                        cur = icon.into();
140                    }
141                }
142            }
143        }
144
145        if !cur.text.is_empty() {
146            retval.push(cur);
147        }
148
149        Ok(retval)
150    }
151}
152
153impl FromStr for FormatTemplate {
154    type Err = Error;
155
156    fn from_str(s: &str) -> Result<Self> {
157        parse::parse_full(s)
158            .and_then(TryInto::try_into)
159            .error("Incorrect format template")
160    }
161}
162
163impl TryFrom<parse::FormatTemplate<'_>> for FormatTemplate {
164    type Error = Error;
165
166    fn try_from(value: parse::FormatTemplate) -> Result<Self, Self::Error> {
167        value
168            .0
169            .into_iter()
170            .map(TryInto::try_into)
171            .collect::<Result<Arc<[_]>>>()
172            .map(Self)
173    }
174}
175
176impl TryFrom<parse::TokenList<'_>> for TokenList {
177    type Error = Error;
178
179    fn try_from(value: parse::TokenList) -> Result<Self, Self::Error> {
180        value
181            .0
182            .into_iter()
183            .map(TryInto::try_into)
184            .collect::<Result<Vec<_>>>()
185            .map(Self)
186    }
187}
188
189impl TryFrom<parse::Token<'_>> for Token {
190    type Error = Error;
191
192    fn try_from(value: parse::Token) -> Result<Self, Self::Error> {
193        Ok(match value {
194            parse::Token::Text(text) => Self::Text(text),
195            parse::Token::Placeholder(placeholder) => Self::Placeholder {
196                name: placeholder.name.to_owned(),
197                formatter: placeholder
198                    .formatter
199                    .map(|fmt| new_formatter(fmt.name, &fmt.args))
200                    .transpose()?,
201            },
202            parse::Token::Icon(icon) => Self::Icon {
203                name: icon.to_owned(),
204            },
205            parse::Token::Recursive(rec) => Self::Recursive(rec.try_into()?),
206        })
207    }
208}