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