1use std::path::PathBuf;
4
5pub const APP_NAME: &str = "sequel-mcp";
6pub const KEYCHAIN_SERVICE_PREFIX: &str = "sequel-mcp";
7
8pub const LEGACY_APP_NAME: &str = "sequel-ace-mcp";
11pub const LEGACY_KEYCHAIN_SERVICE_PREFIX: &str = "sequel-ace-mcp";
12
13fn home() -> PathBuf {
14 std::env::var_os("HOME")
15 .map(PathBuf::from)
16 .unwrap_or_else(|| {
17 PathBuf::from("/")
20 })
21}
22
23pub fn home_dir() -> PathBuf {
25 home()
26}
27
28pub fn config_dir() -> PathBuf {
29 match std::env::var_os("XDG_CONFIG_HOME") {
30 Some(xdg) if !xdg.is_empty() => PathBuf::from(xdg).join(APP_NAME),
31 _ => home().join(".config").join(APP_NAME),
32 }
33}
34
35pub fn legacy_config_dir() -> PathBuf {
36 match std::env::var_os("XDG_CONFIG_HOME") {
37 Some(xdg) if !xdg.is_empty() => PathBuf::from(xdg).join(LEGACY_APP_NAME),
38 _ => home().join(".config").join(LEGACY_APP_NAME),
39 }
40}
41
42pub fn config_path() -> PathBuf {
43 config_dir().join("config.json")
44}
45
46pub fn legacy_config_path() -> PathBuf {
47 legacy_config_dir().join("config.json")
48}
49
50pub fn data_dir() -> PathBuf {
51 match std::env::var_os("XDG_DATA_HOME") {
52 Some(xdg) if !xdg.is_empty() => PathBuf::from(xdg).join(APP_NAME),
53 _ => home().join(".local").join("share").join(APP_NAME),
54 }
55}
56
57pub fn audit_db_path() -> PathBuf {
58 data_dir().join("audit.sqlite")
59}
60
61pub fn runtime_dir() -> PathBuf {
63 data_dir().join("runtime")
64}
65
66pub fn keychain_service_name(connection_name: &str) -> String {
67 format!("{KEYCHAIN_SERVICE_PREFIX} : {connection_name}")
68}
69
70pub fn legacy_keychain_service_name(connection_name: &str) -> String {
71 format!("{LEGACY_KEYCHAIN_SERVICE_PREFIX} : {connection_name}")
72}
73
74pub fn sequel_ace_data_dir() -> PathBuf {
75 home()
76 .join("Library")
77 .join("Containers")
78 .join("com.sequel-ace.sequel-ace")
79 .join("Data")
80 .join("Library")
81 .join("Application Support")
82 .join("Sequel Ace")
83 .join("Data")
84}
85
86pub fn sequel_ace_favorites_plist_path() -> PathBuf {
87 sequel_ace_data_dir().join("Favorites.plist")
88}
89
90pub fn sequel_ace_query_history_db_path() -> PathBuf {
91 sequel_ace_data_dir().join("queryHistory.db")
92}
93
94pub fn sequel_ace_legacy_keychain_service_name(favorite_name: &str, favorite_id: i64) -> String {
95 format!("Sequel Ace : {favorite_name} ({favorite_id})")
96}
97
98pub fn sequel_ace_legacy_ssh_keychain_service_name(
99 favorite_name: &str,
100 favorite_id: i64,
101) -> String {
102 format!("Sequel Ace SSHTunnel : {favorite_name} ({favorite_id})")
103}
104
105pub fn expand_tilde(path: &str) -> PathBuf {
107 if path == ":memory:" {
108 return PathBuf::from(path);
109 }
110 if path == "~" {
111 return home();
112 }
113 if let Some(rest) = path.strip_prefix("~/") {
114 return home().join(rest);
115 }
116 PathBuf::from(path)
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn keychain_names_match_legacy() {
125 assert_eq!(keychain_service_name("prod"), "sequel-mcp : prod");
126 assert_eq!(
127 legacy_keychain_service_name("prod"),
128 "sequel-ace-mcp : prod"
129 );
130 assert_eq!(
131 sequel_ace_legacy_keychain_service_name("acme", 12),
132 "Sequel Ace : acme (12)"
133 );
134 assert_eq!(
135 sequel_ace_legacy_ssh_keychain_service_name("acme", 12),
136 "Sequel Ace SSHTunnel : acme (12)"
137 );
138 }
139
140 #[test]
141 fn expand_tilde_variants() {
142 assert_eq!(expand_tilde("~"), home());
143 assert_eq!(expand_tilde("~/x/y"), home().join("x/y"));
144 assert_eq!(expand_tilde("/abs"), PathBuf::from("/abs"));
145 }
146}