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)]
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}