use directories::{ProjectDirs, BaseDirs};
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Profile {
Dev,
Prod,
}
impl Profile {
}
pub fn get_config_dir(profile: Profile) -> Option<PathBuf> {
let app_name = match profile {
Profile::Dev => "tnj-dev",
Profile::Prod => "tnj",
};
ProjectDirs::from("com", "tnj", app_name)
.map(|dirs| dirs.config_dir().to_path_buf())
}
pub fn get_data_dir(profile: Profile) -> Option<PathBuf> {
let app_name = match profile {
Profile::Dev => "tnj-dev",
Profile::Prod => "tnj",
};
ProjectDirs::from("com", "tnj", app_name)
.map(|dirs| dirs.data_dir().to_path_buf())
}
pub fn expand_path(path: &str) -> PathBuf {
if path.starts_with("~/") {
if let Some(home) = BaseDirs::new().map(|d| d.home_dir().to_path_buf()) {
return home.join(&path[2..]);
}
}
PathBuf::from(path)
}
pub fn parse_date(date_str: &str) -> Result<chrono::NaiveDate, chrono::ParseError> {
chrono::NaiveDate::parse_from_str(date_str, "%Y-%m-%d")
}
pub fn get_current_date_string() -> String {
chrono::Utc::now().format("%Y-%m-%d").to_string()
}
#[derive(Debug, Clone)]
pub struct ParsedKeyBinding {
pub key_code: crossterm::event::KeyCode,
pub requires_ctrl: bool,
}
pub fn has_primary_modifier(modifiers: crossterm::event::KeyModifiers) -> bool {
#[cfg(target_os = "macos")]
{
modifiers.contains(crossterm::event::KeyModifiers::CONTROL)
|| modifiers.contains(crossterm::event::KeyModifiers::ALT)
}
#[cfg(not(target_os = "macos"))]
{
modifiers.contains(crossterm::event::KeyModifiers::CONTROL)
}
}
pub fn format_key_binding_for_display(key_binding: &str) -> String {
#[cfg(target_os = "macos")]
{
key_binding.replace("Ctrl+", "Opt+")
}
#[cfg(not(target_os = "macos"))]
{
key_binding.to_string()
}
}
pub fn parse_key_binding(key_str: &str) -> Result<ParsedKeyBinding, String> {
let key_str = key_str.trim();
if key_str.starts_with("Ctrl+") {
let key_part = key_str.strip_prefix("Ctrl+")
.expect("strip_prefix should succeed after starts_with check");
let key_code = parse_key_code(key_part)?;
return Ok(ParsedKeyBinding {
key_code,
requires_ctrl: true,
});
}
let key_code = parse_key_code(key_str)?;
Ok(ParsedKeyBinding {
key_code,
requires_ctrl: false,
})
}
fn parse_key_code(key_str: &str) -> Result<crossterm::event::KeyCode, String> {
match key_str {
"Enter" => Ok(crossterm::event::KeyCode::Enter),
"Esc" | "Escape" => Ok(crossterm::event::KeyCode::Esc),
"Backspace" => Ok(crossterm::event::KeyCode::Backspace),
"Tab" => Ok(crossterm::event::KeyCode::Tab),
"Space" | " " => Ok(crossterm::event::KeyCode::Char(' ')),
"Left" => Ok(crossterm::event::KeyCode::Left),
"Right" => Ok(crossterm::event::KeyCode::Right),
"Up" => Ok(crossterm::event::KeyCode::Up),
"Down" => Ok(crossterm::event::KeyCode::Down),
"Home" => Ok(crossterm::event::KeyCode::Home),
"End" => Ok(crossterm::event::KeyCode::End),
"PageUp" => Ok(crossterm::event::KeyCode::PageUp),
"PageDown" => Ok(crossterm::event::KeyCode::PageDown),
"Delete" => Ok(crossterm::event::KeyCode::Delete),
"Insert" => Ok(crossterm::event::KeyCode::Insert),
"F1" => Ok(crossterm::event::KeyCode::F(1)),
"F2" => Ok(crossterm::event::KeyCode::F(2)),
"F3" => Ok(crossterm::event::KeyCode::F(3)),
"F4" => Ok(crossterm::event::KeyCode::F(4)),
"F5" => Ok(crossterm::event::KeyCode::F(5)),
"F6" => Ok(crossterm::event::KeyCode::F(6)),
"F7" => Ok(crossterm::event::KeyCode::F(7)),
"F8" => Ok(crossterm::event::KeyCode::F(8)),
"F9" => Ok(crossterm::event::KeyCode::F(9)),
"F10" => Ok(crossterm::event::KeyCode::F(10)),
"F11" => Ok(crossterm::event::KeyCode::F(11)),
"F12" => Ok(crossterm::event::KeyCode::F(12)),
_ => {
if key_str.len() == 1 {
match key_str.chars().next() {
Some(c) => Ok(crossterm::event::KeyCode::Char(c)),
None => Err(format!("Empty key string after length check (this should not happen)")),
}
} else {
Err(format!("Unknown key binding: {}", key_str))
}
}
}
}