Skip to main content

logo_parser/
parser.rs

1// SPDX-FileCopyrightText: 2023 CELESTIFYX Team
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use 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}