1pub mod cmd;
2pub mod config;
3pub mod renderer;
4pub mod utils;
5
6use std::{
7 env,
8 io::{self, Stdout},
9 panic,
10 sync::LazyLock,
11};
12
13use color_eyre::{
14 config::HookBuilder,
15 eyre::{self, Result},
16};
17use ratatui::{
18 Terminal,
19 backend::CrosstermBackend,
20 crossterm::{
21 self,
22 event::{DisableMouseCapture, EnableMouseCapture},
23 terminal::{self, EnterAlternateScreen, LeaveAlternateScreen},
24 },
25};
26use unic_langid::LanguageIdentifier;
27
28fluent_templates::static_loader! {
29 static LOCALES = {
30 locales: "./locales",
31 fallback_language: "en-US",
32 customise: |bundle| bundle.set_use_isolating(false),
35 };
36}
37
38pub static LANG_ID: LazyLock<LanguageIdentifier> = LazyLock::new(|| {
39 let mut locale = sys_locale::get_locale().unwrap_or_else(|| {
40 eprintln!("Failed to get active locale for the system, use `en-US`");
41 String::from("en-US")
42 });
43
44 if locale == "zh-CN" {
45 locale = "zh-Hans".to_string();
46 } else if locale == "zh-HK" || locale == "zh-TW" {
47 locale = "zh-Hant".to_string();
48 } else if locale == "C" {
49 locale = "en-US".to_string();
50 }
51
52 match locale.parse::<LanguageIdentifier>() {
53 Ok(lang_id) => lang_id,
54 Err(error) => {
55 eprintln!("Failed to parse LanguageIdentifier: {error}, use `en-US`");
56 "en-US".parse::<LanguageIdentifier>().unwrap()
57 }
58 }
59});
60
61pub fn init_error_hooks(restore: bool) -> Result<()> {
62 if env::var("RUST_SPANTRACE").is_err() {
63 unsafe {
64 env::set_var("RUST_SPANTRACE", "0");
65 }
66 }
67
68 let (panic_hook, eyre_hook) = HookBuilder::default().into_hooks();
69
70 let panic_hook = panic_hook.into_panic_hook();
71 panic::set_hook(Box::new(move |panic_info| {
72 if restore {
73 let _ = restore_terminal();
74 }
75 panic_hook(panic_info);
76 }));
77
78 let eyre_hook = eyre_hook.into_eyre_hook();
79 eyre::set_hook(Box::new(move |e| {
80 if restore {
81 let _ = restore_terminal();
82 }
83 eyre_hook(e)
84 }))?;
85
86 Ok(())
87}
88
89pub(crate) type Tui = Terminal<CrosstermBackend<Stdout>>;
90
91pub(crate) fn init_terminal() -> Result<Tui> {
92 crossterm::execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture)?;
93 terminal::enable_raw_mode()?;
94 Ok(Terminal::new(CrosstermBackend::new(io::stdout()))?)
95}
96
97pub(crate) fn restore_terminal() -> Result<()> {
98 crossterm::execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
99 terminal::disable_raw_mode()?;
100 Ok(())
101}