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 for logo in &self.logos {
47 if !logo.logo_type.eq_ignore_ascii_case("large") && !logo.logo_type.eq_ignore_ascii_case("small") {
48 return Err(format!("Logo type must be 'large' or 'small', found '{}'", logo.logo_type));
49 }
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.eq_ignore_ascii_case("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.eq_ignore_ascii_case("small")).map(|l: &LogoEntry| l.ascii.clone())
77 }
78
79 pub(super) fn get_title_color_ref(&self) -> Option<&str> {
80 match self.colors.first()? {
81 ColorValue::Reference(s) => Some(s),
82 ColorValue::Array(_) => None
83 }
84 }
85
86 pub(super) fn get_keys_color_ref(&self) -> Option<&str> {
87 match self.colors.get(1)? {
88 ColorValue::Reference(s) => Some(s),
89 ColorValue::Array(_) => None
90 }
91 }
92
93 pub(super) fn get_color_defs(&self) -> Option<&[ColorDef]> {
94 match self.colors.get(2)? {
95 ColorValue::Array(arr) => Some(arr),
96 ColorValue::Reference(_) => None
97 }
98 }
99}
100
101#[derive(Debug, Clone)]
102pub struct ColorConfig {
103 pub title_color: AnyColor,
104 pub keys_color: AnyColor,
105 pub ascii_colors: Vec<AnyColor>
106}