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}