1use std::{
5 fs,
6 path::Path,
7 str::FromStr
8};
9
10use chromakitx::{
11 AnsiColor,
12 CssColor,
13 XtermColor,
14 AnyColor
15};
16
17use super::{
18 error::Error,
19
20 config::{
21 LogoConfig,
22 ColorConfig,
23 ColorDef
24 }
25};
26
27pub struct LogoParser;
28
29impl LogoParser {
30 pub fn from(path: &Path) -> Result<(Vec<String>, Vec<String>, ColorConfig), Error> {
31 if !path.exists() {
32 return Err(Error::FileNotFound(path.to_path_buf()));
33 }
34
35 let content: String = fs::read_to_string(path)?;
36
37 let config: LogoConfig = serde_json::from_str(&content)?;
38 config.validate().map_err(Error::ValidationError)?;
39
40 let large: Vec<String> = config.get_large_logo().unwrap_or_else(|| Vec::new());
41 let small: Vec<String> = config.get_small_logo().unwrap_or_else(|| Vec::new());
42
43 if large.is_empty() && small.is_empty() {
44 return Err(Error::ValidationError("No valid logo found".to_string()));
45 }
46
47 let color_config: ColorConfig = Self::parse_colors(&config)?;
48 Ok((large, small, color_config))
49 }
50
51 fn parse_colors(config: &LogoConfig) -> Result<ColorConfig, Error> {
52 let color_defs: Vec<ColorDef> = config.get_color_defs().ok_or_else(|| Error::ValidationError("Color definitions not found".to_string()))?;
53
54 if color_defs.is_empty() {
55 return Err(Error::ValidationError("Color definitions array is empty".to_string()));
56 }
57
58 let title_ref: String = config.get_title_color_ref().ok_or_else(|| Error::ValidationError("Title color reference not found".to_string()))?;
59 let title_color: AnyColor = Self::resolve_color_reference(&title_ref, &color_defs)?;
60
61 let keys_ref: String = config.get_keys_color_ref().ok_or_else(|| Error::ValidationError("Keys color reference not found".to_string()))?;
62 let keys_color: AnyColor = Self::resolve_color_reference(&keys_ref, &color_defs)?;
63
64 let mut ascii_colors: Vec<AnyColor> = Vec::new();
65
66 for (idx, _) in color_defs.iter().enumerate() {
67 let color_ref: String = format!("%{}", idx);
68 let color: AnyColor = Self::resolve_color_reference(&color_ref, &color_defs)?;
69
70 (&mut ascii_colors).push(color);
71 }
72
73 Ok(ColorConfig {
74 title_color,
75 keys_color,
76 ascii_colors
77 })
78 }
79
80 fn resolve_color_reference(reference: &str, color_defs: &[ColorDef]) -> Result<AnyColor, Error> {
81 let reference: &str = reference.trim();
82
83 if reference.starts_with('%') {
84 let idx_str: &str = &reference[1..];
85 let idx: usize = idx_str.parse().map_err(|_| Error::ColorParseError(format!("Invalid color index: {}", idx_str)))?;
86
87 if idx >= color_defs.len() {
88 return Err(Error::ColorParseError(format!("Color index out of bounds: {}", idx), ));
89 }
90
91 return Self::parse_color_def(&color_defs[idx]);
92 }
93
94 Self::parse_color_name(reference)
95 }
96
97 fn parse_color_def(color_def: &ColorDef) -> Result<AnyColor, Error> {
98 match color_def {
99 ColorDef::Simple(name) => Self::parse_color_name(name),
100
101 ColorDef::Named(map) => {
102 if map.len() != 1 {
103 return Err(Error::ColorParseError("Color definition object must have exactly one entry".to_string()));
104 }
105
106 let (color_name, color_type): (&String, &String) = map.iter().next().ok_or_else(|| Error::ColorParseError("Empty color definition".to_string()))?;
107
108 match color_type.to_lowercase().as_str() {
109 "ansi" => Self::parse_ansi_color(color_name),
110 "xterm" => Self::parse_xterm_color(color_name),
111 "css" => Self::parse_css_color(color_name),
112
113 _ => Err(Error::ColorParseError(format!("Unknown color type: {}", color_type)))
114 }
115 }
116 }
117 }
118
119 fn parse_color_name(name: &str) -> Result<AnyColor, Error> {
120 AnsiColor::from_str(name).map(AnyColor::Ansi).or_else(|_| {
121 CssColor::from_str(name).map(AnyColor::Css)
122 }).or_else(|_| {
123 XtermColor::from_str(name).map(AnyColor::Xterm)
124 }).map_err(|_| Error::ColorParseError(format!("Unknown color name: {}", name)))
125 }
126
127 fn parse_ansi_color(name: &str) -> Result<AnyColor, Error> {
128 AnsiColor::from_str(name).map(AnyColor::Ansi).map_err(|_| Error::ColorParseError(format!("Unknown ANSI color: {}", name)))
129 }
130
131 fn parse_xterm_color(name: &str) -> Result<AnyColor, Error> {
132 XtermColor::from_str(name).map(AnyColor::Xterm).map_err(|_| Error::ColorParseError(format!("Unknown Xterm color: {}", name)))
133 }
134
135 fn parse_css_color(name: &str) -> Result<AnyColor, Error> {
136 CssColor::from_str(name).map(AnyColor::Css).map_err(|_| Error::ColorParseError(format!("Unknown CSS color: {}", name)))
137 }
138}