Skip to main content

chess_tui/
skin.rs

1use crate::constants::DisplayMode;
2use ratatui::style::Color;
3use serde::{Deserialize, Serialize};
4use std::fs;
5use std::path::Path;
6
7fn default_piece_style_str() -> String {
8    DisplayMode::DEFAULT.to_string()
9}
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct Skin {
13    pub name: String,
14    #[serde(default = "default_piece_style_str", alias = "display_mode")]
15    pub piece_style: String,
16    pub board_white_color: Color,
17    pub board_black_color: Color,
18    pub piece_white_color: Color,
19    pub piece_black_color: Color,
20    pub cursor_color: Color,
21    pub selection_color: Color,
22    pub last_move_color: Color,
23}
24
25/// Piece characters for a single size (small / compact / extended / large).
26#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
27pub struct PieceStyleSize {
28    pub bishop: String,
29    pub king: String,
30    pub knight: String,
31    pub pawn: String,
32    pub queen: String,
33    pub rook: String,
34}
35
36/// Named piece style with different character sets per size.
37#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
38pub struct PieceStyle {
39    pub name: String,
40    pub small: PieceStyleSize,
41    pub compact: PieceStyleSize,
42    pub extended: PieceStyleSize,
43    pub large: PieceStyleSize,
44}
45
46impl Default for Skin {
47    fn default() -> Self {
48        Self {
49            name: "Default".to_string(),
50            piece_style: DisplayMode::DEFAULT.to_string(),
51            board_white_color: Color::Rgb(160, 160, 160),
52            board_black_color: Color::Rgb(128, 95, 69),
53            piece_white_color: Color::White,
54            piece_black_color: Color::Black,
55            cursor_color: Color::LightBlue,
56            selection_color: Color::LightGreen,
57            last_move_color: Color::LightGreen,
58        }
59    }
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct SkinCollection {
64    pub skins: Vec<Skin>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct PieceStyleCollection {
69    pub piece_styles: Vec<PieceStyle>,
70}
71
72impl Skin {
73    /// Loads a skin from a JSON file.
74    ///
75    /// # Errors
76    ///
77    /// Returns an error if the file cannot be read or parsed.
78    pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
79        let content = fs::read_to_string(path)?;
80        let skin: Skin = serde_json::from_str(&content)?;
81        Ok(skin)
82    }
83
84    /// Loads all skins from a JSON file containing a collection.
85    ///
86    /// # Errors
87    ///
88    /// Returns an error if the file cannot be read or parsed.
89    pub fn load_all_skins<P: AsRef<Path>>(
90        path: P,
91    ) -> Result<Vec<Skin>, Box<dyn std::error::Error>> {
92        let content = fs::read_to_string(path)?;
93        let collection: SkinCollection = serde_json::from_str(&content)?;
94        Ok(collection.skins)
95    }
96
97    #[must_use]
98    pub fn get_skin_by_name(skins: &[Skin], name: &str) -> Option<Skin> {
99        skins.iter().find(|s| s.name == name).cloned()
100    }
101
102    pub fn load_all_piece_styles<P: AsRef<Path>>(
103        path: P,
104    ) -> Result<Vec<PieceStyle>, Box<dyn std::error::Error>> {
105        let content = fs::read_to_string(path)?;
106        let collection: PieceStyleCollection = serde_json::from_str(&content)?;
107        Ok(collection.piece_styles)
108    }
109
110    /// Creates a special "Default" display mode skin entry
111    #[must_use]
112    pub fn default_display_mode() -> Self {
113        Self {
114            name: "Default".to_string(),
115            piece_style: DisplayMode::DEFAULT.to_string(),
116            board_white_color: Color::Rgb(160, 160, 160),
117            board_black_color: Color::Rgb(128, 95, 69),
118            piece_white_color: Color::White,
119            piece_black_color: Color::Black,
120            cursor_color: Color::LightBlue,
121            selection_color: Color::LightGreen,
122            last_move_color: Color::LightGreen,
123        }
124    }
125
126    /// Creates a special "ASCII" display mode skin entry
127    #[must_use]
128    pub fn ascii_display_mode() -> Self {
129        Self {
130            name: "ASCII".to_string(),
131            piece_style: DisplayMode::ASCII.to_string(),
132            board_white_color: Color::Rgb(160, 160, 160),
133            board_black_color: Color::Rgb(128, 95, 69),
134            piece_white_color: Color::White,
135            piece_black_color: Color::Black,
136            cursor_color: Color::LightBlue,
137            selection_color: Color::LightGreen,
138            last_move_color: Color::LightGreen,
139        }
140    }
141}