unicode_rs/unicode/
mod.rs

1//! Unicode character library for the editor
2//! Provides categorized Unicode characters for consistent visual design
3
4pub mod arrows;
5pub mod blocks;
6pub mod editor;
7pub mod file_types;
8pub mod git;
9pub mod security;
10pub mod shapes;
11pub mod status;
12pub mod symbols;
13pub mod ui;
14
15// Re-export main types for convenience
16pub use arrows::{Arrow, Navigation};
17pub use blocks::Block;
18pub use editor::{Cursor, Selection};
19pub use file_types::{
20    get_file_type_from_extension, get_file_type_from_filename, FileType, LanguageType,
21};
22pub use git::{GitAction, GitBranch, GitDiff, GitStatus};
23pub use shapes::Shape;
24pub use status::Status;
25pub use symbols::Symbol;
26pub use ui::{Border, Control, Indicator, Separator};
27
28/// Unicode character theme
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
30pub enum UnicodeTheme {
31    /// Minimal ASCII-compatible characters
32    Minimal,
33    /// Basic Unicode symbols
34    Basic,
35    /// Rich Unicode with full symbol set
36    #[default]
37    Rich,
38    /// Fancy decorative Unicode
39    Fancy,
40}
41
42/// Unicode character provider trait
43///
44/// This trait defines the interface for all Unicode symbol types in the library.
45/// It provides a consistent way to get character representations across different
46/// themes and ensures type safety.
47///
48/// # Examples
49///
50/// ```rust
51/// use unicode_rs::prelude::*;
52///
53/// // All symbol enums implement UnicodeProvider
54/// let check = Symbol::Check.get_char(UnicodeTheme::Rich);
55/// let arrow = Arrow::Right.get_char(UnicodeTheme::Rich);
56/// let git_status = GitStatus::Modified.get_char(UnicodeTheme::Rich);
57///
58/// println!("Status: {} {} {}", check, arrow, git_status);
59/// ```
60///
61/// # Implementation Notes
62///
63/// - All implementations should handle all theme variants
64/// - ASCII fallbacks should be provided for Minimal theme
65/// - The trait is object-safe and can be used with dynamic dispatch
66pub trait UnicodeProvider {
67    /// Get character for the given theme
68    ///
69    /// Returns the appropriate Unicode character for the specified theme.
70    /// Implementations should provide meaningful fallbacks for all themes.
71    ///
72    /// # Arguments
73    ///
74    /// * `theme` - The Unicode theme to use for character selection
75    ///
76    /// # Examples
77    ///
78    /// ```rust
79    /// use unicode_rs::prelude::*;
80    ///
81    /// let symbol = Symbol::Check;
82    /// assert_eq!(symbol.get_char(UnicodeTheme::Minimal), 'v');
83    /// assert_eq!(symbol.get_char(UnicodeTheme::Rich), '✓');
84    /// ```
85    fn get_char(&self, theme: UnicodeTheme) -> char;
86
87    /// Get string representation for the given theme
88    fn get_str(&self, theme: UnicodeTheme) -> &'static str {
89        #[allow(clippy::match_single_binding)]
90        match self.get_char(theme) {
91            c => match c {
92                ' ' => " ",
93                '!' => "!",
94                '"' => "\"",
95                '#' => "#",
96                '$' => "$",
97                '%' => "%",
98                '&' => "&",
99                '\'' => "'",
100                '(' => "(",
101                ')' => ")",
102                '*' => "*",
103                '+' => "+",
104                ',' => ",",
105                '-' => "-",
106                '.' => ".",
107                '/' => "/",
108                '0' => "0",
109                '1' => "1",
110                '2' => "2",
111                '3' => "3",
112                '4' => "4",
113                '5' => "5",
114                '6' => "6",
115                '7' => "7",
116                '8' => "8",
117                '9' => "9",
118                ':' => ":",
119                ';' => ";",
120                '<' => "<",
121                '=' => "=",
122                '>' => ">",
123                '?' => "?",
124                '@' => "@",
125                'A' => "A",
126                'B' => "B",
127                'C' => "C",
128                'D' => "D",
129                'E' => "E",
130                'F' => "F",
131                'G' => "G",
132                'H' => "H",
133                'I' => "I",
134                'J' => "J",
135                'K' => "K",
136                'L' => "L",
137                'M' => "M",
138                'N' => "N",
139                'O' => "O",
140                'P' => "P",
141                'Q' => "Q",
142                'R' => "R",
143                'S' => "S",
144                'T' => "T",
145                'U' => "U",
146                'V' => "V",
147                'W' => "W",
148                'X' => "X",
149                'Y' => "Y",
150                'Z' => "Z",
151                '[' => "[",
152                '\\' => "\\",
153                ']' => "]",
154                '^' => "^",
155                '_' => "_",
156                '`' => "`",
157                'a' => "a",
158                'b' => "b",
159                'c' => "c",
160                'd' => "d",
161                'e' => "e",
162                'f' => "f",
163                'g' => "g",
164                'h' => "h",
165                'i' => "i",
166                'j' => "j",
167                'k' => "k",
168                'l' => "l",
169                'm' => "m",
170                'n' => "n",
171                'o' => "o",
172                'p' => "p",
173                'q' => "q",
174                'r' => "r",
175                's' => "s",
176                't' => "t",
177                'u' => "u",
178                'v' => "v",
179                'w' => "w",
180                'x' => "x",
181                'y' => "y",
182                'z' => "z",
183                '{' => "{",
184                '|' => "|",
185                '}' => "}",
186                '~' => "~",
187                _ => "?", // Fallback for Unicode characters
188            },
189        }
190    }
191}
192
193/// Unicode character configuration
194#[derive(Debug, Clone)]
195pub struct UnicodeConfig {
196    /// Current theme
197    pub theme: UnicodeTheme,
198    /// Whether to use fallback ASCII characters
199    pub use_fallback: bool,
200    /// Custom character overrides
201    pub overrides: std::collections::HashMap<String, char>,
202}
203
204#[allow(clippy::derivable_impls)]
205impl Default for UnicodeConfig {
206    fn default() -> Self {
207        Self {
208            theme: UnicodeTheme::default(),
209            use_fallback: false,
210            overrides: std::collections::HashMap::new(),
211        }
212    }
213}
214
215impl UnicodeConfig {
216    /// Create new config with theme
217    pub fn with_theme(theme: UnicodeTheme) -> Self {
218        Self {
219            theme,
220            ..Default::default()
221        }
222    }
223
224    /// Enable fallback mode
225    pub fn with_fallback(mut self) -> Self {
226        self.use_fallback = true;
227        self
228    }
229
230    /// Add character override
231    pub fn with_override(mut self, key: &str, character: char) -> Self {
232        self.overrides.insert(key.to_string(), character);
233        self
234    }
235
236    /// Get character with config applied
237    pub fn get_char<T: UnicodeProvider>(&self, provider: &T, key: Option<&str>) -> char {
238        // Check for override first
239        if let Some(key) = key {
240            if let Some(&override_char) = self.overrides.get(key) {
241                return override_char;
242            }
243        }
244
245        let char = provider.get_char(self.theme);
246
247        // Apply fallback if needed
248        if self.use_fallback && !char.is_ascii() {
249            provider.get_char(UnicodeTheme::Minimal)
250        } else {
251            char
252        }
253    }
254}
255
256use std::sync::{Mutex, OnceLock};
257
258/// Global unicode configuration
259static GLOBAL_UNICODE_CONFIG: OnceLock<Mutex<UnicodeConfig>> = OnceLock::new();
260
261/// Set global unicode configuration
262///
263/// Sets the global Unicode configuration that will be used by [`get_char`] and [`get_str`].
264/// This is thread-safe and can be called from multiple threads.
265///
266/// # Arguments
267///
268/// * `config` - The Unicode configuration to set globally
269///
270/// # Examples
271///
272/// ```rust
273/// use unicode_rs::prelude::*;
274///
275/// // Set minimal theme globally
276/// set_global_config(UnicodeConfig::with_theme(UnicodeTheme::Minimal));
277///
278/// // Set rich theme with fallback
279/// let config = UnicodeConfig::with_theme(UnicodeTheme::Rich)
280///     .with_fallback()
281///     .with_override("custom_bullet", '•');
282/// set_global_config(config);
283/// ```
284///
285/// # Thread Safety
286///
287/// This function is thread-safe and uses internal synchronization.
288pub fn set_global_config(config: UnicodeConfig) {
289    let mutex = GLOBAL_UNICODE_CONFIG.get_or_init(|| Mutex::new(UnicodeConfig::default()));
290    if let Ok(mut guard) = mutex.lock() {
291        *guard = config;
292    }
293}
294
295/// Get global unicode configuration
296///
297/// Returns a copy of the current global Unicode configuration.
298/// If no configuration has been set, returns the default configuration.
299///
300/// # Examples
301///
302/// ```rust
303/// use unicode_rs::prelude::*;
304///
305/// let config = get_global_config();
306/// println!("Current theme: {:?}", config.theme);
307/// ```
308///
309/// # Thread Safety
310///
311/// This function is thread-safe and uses internal synchronization.
312pub fn get_global_config() -> UnicodeConfig {
313    let mutex = GLOBAL_UNICODE_CONFIG.get_or_init(|| Mutex::new(UnicodeConfig::default()));
314    mutex.lock().map(|guard| guard.clone()).unwrap_or_default()
315}
316
317/// Get character using global config
318///
319/// Convenience function that gets a character using the global configuration.
320/// This is equivalent to calling `get_global_config().get_char(provider, key)`.
321///
322/// # Arguments
323///
324/// * `provider` - The Unicode provider (symbol enum)
325/// * `key` - Optional override key for custom character mappings
326///
327/// # Examples
328///
329/// ```rust
330/// use unicode_rs::prelude::*;
331///
332/// // Set global theme
333/// set_global_config(UnicodeConfig::with_theme(UnicodeTheme::Rich));
334///
335/// // Get characters using global config
336/// let check = get_char(&Symbol::Check, None);
337/// let arrow = get_char(&Arrow::Right, None);
338///
339/// // Use custom override
340/// let bullet = get_char(&Symbol::Check, Some("bullet"));
341/// ```
342///
343/// # Thread Safety
344///
345/// This function is thread-safe as it uses the thread-safe global configuration.
346pub fn get_char<T: UnicodeProvider>(provider: &T, key: Option<&str>) -> char {
347    get_global_config().get_char(provider, key)
348}
349
350/// Get string using global config
351pub fn get_str<T: UnicodeProvider>(provider: &T, key: Option<&str>) -> &'static str {
352    let char = get_char(provider, key);
353    // This is a simplified implementation - in practice you'd want a proper string cache
354    match char {
355        ' ' => " ",
356        _ => "?", // Simplified fallback
357    }
358}