1use std::collections::HashMap;
5
6use serde::{
7 Deserialize,
8 Serialize
9};
10
11use chromakitx::AnyColor;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub(super) struct LogoEntry {
15 #[serde(rename = "type")]
16 pub(super) logo_type: String,
17 pub(super) ascii: Vec<String>
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21#[serde(untagged)]
22pub(super) enum ColorValue {
23 Reference(String),
24 Array(Vec<ColorDef>)
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28#[serde(untagged)]
29pub(super) enum ColorDef {
30 Simple(String),
31 Named(HashMap<String, String>)
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub(super) struct LogoConfig {
36 pub(super) logos: Vec<LogoEntry>,
37 pub(super) colors: Vec<ColorValue>
38}
39
40impl LogoConfig {
41 pub(super) fn validate(&self) -> Result<(), String> {
42 if self.logos.is_empty() {
43 return Err("At least one logo is required".to_string());
44 }
45
46 let logo_type: String = self.logos[0].logo_type.to_lowercase();
47
48 if (logo_type != "large") && (logo_type != "small") {
49 return Err(format!("Logo type must be 'large' or 'small', found '{}'", self.logos[0].logo_type));
50 }
51
52 if self.colors.len() != 3 {
53 return Err(format!("Expected exactly 3 color elements, found {}", self.colors.len()));
54 }
55
56 if !matches!(&self.colors[0], ColorValue::Reference(_)) {
57 return Err("First color must be a reference string (e.g., '%0')".to_string());
58 }
59
60 if !matches!(&self.colors[1], ColorValue::Reference(_)) {
61 return Err("Second color must be a reference string (e.g., '%0')".to_string());
62 }
63
64 if !matches!(&self.colors[2], ColorValue::Array(_)) {
65 return Err("Third color element must be an array of color definitions".to_string());
66 }
67
68 Ok(())
69 }
70
71 pub(super) fn get_large_logo(&self) -> Option<Vec<String>> {
72 self.logos.iter().find(|l: &&LogoEntry| l.logo_type.to_lowercase() == "large").map(|l: &LogoEntry| l.ascii.clone())
73 }
74
75 pub(super) fn get_small_logo(&self) -> Option<Vec<String>> {
76 self.logos.iter().find(|l: &&LogoEntry| l.logo_type.to_lowercase() == "small").map(|l: &LogoEntry| l.ascii.clone())
77 }
78
79 pub(super) fn get_title_color_ref(&self) -> Option<String> {
80 if let ColorValue::Reference(s) = &self.colors[0] {
81 Some(s.clone())
82 } else {
83 None
84 }
85 }
86
87 pub(super) fn get_keys_color_ref(&self) -> Option<String> {
88 if let ColorValue::Reference(s) = &self.colors[1] {
89 Some(s.clone())
90 } else {
91 None
92 }
93 }
94
95 pub(super) fn get_color_defs(&self) -> Option<Vec<ColorDef>> {
96 if let ColorValue::Array(arr) = &self.colors[2] {
97 Some(arr.clone())
98 } else {
99 None
100 }
101 }
102}
103
104#[derive(Debug, Clone)]
105pub struct ColorConfig {
106 pub title_color: AnyColor,
107 pub keys_color: AnyColor,
108 pub ascii_colors: Vec<AnyColor>
109}