1use super::key_value;
7use crate::{
8 platform::prelude::*,
9 settings::{Color, Field, Gradient, SettingsDescription, Value},
10 timing::formatter,
11 util::PopulateString,
12 Timer,
13};
14use alloc::borrow::Cow;
15use core::mem;
16use serde::{Deserialize, Serialize};
17
18#[cfg(test)]
19mod tests;
20
21#[derive(Default, Clone)]
25pub struct Component {
26 settings: Settings,
27}
28
29#[derive(Clone, Serialize, Deserialize)]
31#[serde(default)]
32pub struct Settings {
33 pub background: Gradient,
35 pub display_two_rows: bool,
38 pub left_center_color: Option<Color>,
42 pub right_color: Option<Color>,
46 pub text: Text,
48}
49
50#[derive(Clone, Serialize, Deserialize)]
52pub enum Text {
53 Center(String),
55 Split(String, String),
58 Variable(String, bool),
62}
63
64#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
66pub enum TextState {
67 Center(String),
69 Split(String, String),
72}
73
74impl Default for TextState {
75 fn default() -> Self {
76 TextState::Center(String::new())
77 }
78}
79
80impl Text {
81 pub const fn is_split(&self) -> bool {
83 match *self {
84 Text::Split(_, _) => true,
85 Text::Center(_) => false,
86 Text::Variable(_, is_split) => is_split,
87 }
88 }
89
90 pub fn set_center<S: PopulateString>(&mut self, text: S) {
93 if let Text::Center(inner) = self {
94 text.populate(inner);
95 } else {
96 *self = Text::Center(text.into_string());
97 }
98 }
99
100 pub fn set_left<S: PopulateString>(&mut self, text: S) {
103 if let Text::Split(inner, _) = self {
104 text.populate(inner);
105 } else {
106 *self = Text::Split(text.into_string(), String::from(""));
107 }
108 }
109
110 pub fn set_right<S: PopulateString>(&mut self, text: S) {
113 if let Text::Split(_, inner) = self {
114 text.populate(inner);
115 } else {
116 *self = Text::Split(String::from(""), text.into_string());
117 }
118 }
119}
120
121#[derive(Serialize, Deserialize, Default)]
123pub struct State {
124 pub background: Gradient,
126 pub display_two_rows: bool,
129 pub left_center_color: Option<Color>,
133 pub right_color: Option<Color>,
137 pub text: TextState,
139}
140
141impl Default for Settings {
142 fn default() -> Self {
143 Self {
144 background: key_value::DEFAULT_GRADIENT,
145 display_two_rows: false,
146 left_center_color: None,
147 right_color: None,
148 text: Text::Center(String::from("")),
149 }
150 }
151}
152
153#[cfg(feature = "std")]
154impl State {
155 pub fn write_json<W>(&self, writer: W) -> serde_json::Result<()>
157 where
158 W: std::io::Write,
159 {
160 serde_json::to_writer(writer, self)
161 }
162}
163
164impl Component {
165 pub fn new() -> Self {
167 Default::default()
168 }
169
170 pub const fn with_settings(settings: Settings) -> Self {
172 Self { settings }
173 }
174
175 pub const fn settings(&self) -> &Settings {
177 &self.settings
178 }
179
180 pub fn settings_mut(&mut self) -> &mut Settings {
182 &mut self.settings
183 }
184
185 pub fn name(&self) -> Cow<'_, str> {
187 let name: Cow<'_, str> = match &self.settings.text {
188 Text::Center(text) => text.as_str().into(),
189 Text::Split(left, right) => {
190 let mut name = String::with_capacity(left.len() + right.len() + 1);
191 name.push_str(left);
192 if !left.is_empty() && !right.is_empty() {
193 name.push(' ');
194 }
195 name.push_str(right);
196 name.into()
197 }
198 Text::Variable(var_name, _) => var_name.as_str().into(),
199 };
200
201 if name.trim().is_empty() {
202 "Text".into()
203 } else {
204 name
205 }
206 }
207
208 pub fn update_state(&self, state: &mut State, timer: &Timer) {
210 state.background = self.settings.background;
211 state.display_two_rows = self.settings.text.is_split() && self.settings.display_two_rows;
212 state.left_center_color = self.settings.left_center_color;
213 state.right_color = self.settings.right_color;
214
215 let (left_center, right) = match &self.settings.text {
216 Text::Center(center) => (center.as_str(), None),
217 Text::Split(left, right) => (left.as_str(), Some(right.as_str())),
218 Text::Variable(var_name, is_split) => {
219 let value = timer
220 .run()
221 .metadata()
222 .custom_variable(var_name)
223 .map(|var| var.value.as_str())
224 .filter(|value| !value.trim_start().is_empty())
225 .unwrap_or(formatter::DASH);
226
227 if *is_split {
228 (var_name.as_str(), Some(value))
229 } else {
230 (value, None)
231 }
232 }
233 };
234
235 match (&mut state.text, left_center, right) {
238 (TextState::Center(old), new, None) => {
239 old.clear();
240 old.push_str(new);
241 }
242 (TextState::Center(old), new_l, Some(new_r)) => {
243 let mut new_l_mem = mem::take(old);
244 new_l_mem.clear();
245 new_l_mem.push_str(new_l);
246 state.text = TextState::Split(new_l_mem, new_r.to_owned());
247 }
248 (TextState::Split(l, r), new_l, Some(new_r)) => {
249 l.clear();
250 l.push_str(new_l);
251 r.clear();
252 r.push_str(new_r);
253 }
254 (TextState::Split(l, _), new_center, None) => {
255 let mut new_center_mem = mem::take(l);
256 new_center_mem.clear();
257 new_center_mem.push_str(new_center);
258 state.text = TextState::Center(new_center_mem);
259 }
260 }
261 }
262
263 pub fn state(&self, timer: &Timer) -> State {
265 let mut state = Default::default();
266 self.update_state(&mut state, timer);
267 state
268 }
269
270 pub fn settings_description(&self) -> SettingsDescription {
273 let (first, second, is_variable, is_split, left_color, right_color) =
274 match &self.settings.text {
275 Text::Center(text) => (
276 Field::new("Text".into(), text.to_string().into()),
277 None,
278 false,
279 false,
280 "Text Color",
281 "",
282 ),
283 Text::Split(left, right) => (
284 Field::new("Left".into(), left.to_string().into()),
285 Some(Field::new("Right".into(), right.to_string().into())),
286 false,
287 true,
288 "Left Color",
289 "Right Color",
290 ),
291 Text::Variable(var_name, is_split) => (
292 Field::new("Variable".into(), var_name.to_string().into()),
293 None,
294 true,
295 *is_split,
296 if *is_split {
297 "Name Color"
298 } else {
299 "Value Color"
300 },
301 "Value Color",
302 ),
303 };
304
305 let mut fields = vec![
306 Field::new("Background".into(), self.settings.background.into()),
307 Field::new("Use Variable".into(), is_variable.into()),
308 Field::new("Split".into(), is_split.into()),
309 first,
310 Field::new(left_color.into(), self.settings.left_center_color.into()),
311 ];
312
313 if let Some(second) = second {
314 fields.push(second);
315 }
316
317 if is_split {
318 fields.push(Field::new(
319 right_color.into(),
320 self.settings.right_color.into(),
321 ));
322 fields.push(Field::new(
323 "Display 2 Rows".into(),
324 self.settings.display_two_rows.into(),
325 ));
326 }
327
328 SettingsDescription::with_fields(fields)
329 }
330
331 pub fn set_value(&mut self, mut index: usize, value: Value) {
339 if index >= 5 {
340 if let Text::Variable(_, _) = &self.settings.text {
341 index += 1;
342 }
343 }
344
345 match index {
346 0 => self.settings.background = value.into(),
347 1 => {
348 self.settings.text = match (value.into_bool().unwrap(), &mut self.settings.text) {
349 (false, Text::Variable(name, true)) => {
350 Text::Split(mem::take(name), String::new())
351 }
352 (false, Text::Variable(name, false)) => Text::Center(mem::take(name)),
353 (true, Text::Center(center)) => Text::Variable(mem::take(center), false),
354 (true, Text::Split(left, _)) => Text::Variable(mem::take(left), true),
355 _ => return,
356 };
357 }
358 2 => {
359 self.settings.text = match (value.into_bool().unwrap(), &mut self.settings.text) {
360 (true, Text::Center(center)) => {
361 self.settings.right_color = self.settings.left_center_color;
362 self.settings.display_two_rows = false;
363
364 Text::Split(mem::take(center), String::new())
365 }
366 (false, Text::Split(left, right)) => {
367 let mut value = mem::take(left);
368 let right = mem::take(right);
369 if !value.is_empty() && !right.is_empty() {
370 value.push(' ');
371 }
372 value.push_str(&right);
373
374 Text::Center(value)
375 }
376 (should_be_split, Text::Variable(_, is_split)) => {
377 *is_split = should_be_split;
378 return;
379 }
380 _ => return,
381 };
382 }
383 3 => match &mut self.settings.text {
384 Text::Center(center) => *center = value.into(),
385 Text::Split(left, _) => *left = value.into(),
386 Text::Variable(var_name, _) => *var_name = value.into(),
387 },
388 4 => self.settings.left_center_color = value.into(),
389 5 => match &mut self.settings.text {
390 Text::Center(_) => panic!("Can't set right text when there's only a center text"),
391 Text::Split(_, right) => *right = value.into(),
392 Text::Variable(_, _) => {
393 unreachable!("Shouldn't be able to set value for a variable")
394 }
395 },
396 6 => self.settings.right_color = value.into(),
397 7 => self.settings.display_two_rows = value.into(),
398 _ => panic!("Unsupported Setting Index"),
399 }
400 }
401}