Skip to main content

claude_codex/
paths.rs

1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3
4#[derive(Debug, Clone)]
5pub struct DirResolverEnv {
6    pub platform: String,
7    pub env: HashMap<String, String>,
8    pub home: String,
9}
10
11impl Default for DirResolverEnv {
12    fn default() -> Self {
13        Self {
14            platform: std::env::consts::OS.into(),
15            env: std::env::vars().collect(),
16            home: std::env::var("HOME")
17                .or_else(|_| std::env::var("USERPROFILE"))
18                .unwrap_or_else(|_| "/".to_string()),
19        }
20    }
21}
22
23pub fn resolve_config_dir(deps: &DirResolverEnv) -> PathBuf {
24    if let Some(override_dir) = deps.env.get("CCP_CONFIG_DIR") {
25        return Path::new(override_dir).to_path_buf();
26    }
27
28    if deps.platform == "win32" {
29        let appdata = deps
30            .env
31            .get("APPDATA")
32            .cloned()
33            .unwrap_or_else(|| format!("{}\\AppData\\Roaming", deps.home));
34        return join_with_sep(&appdata, &["claude-code-proxy"], true);
35    }
36
37    if deps.platform == "darwin" {
38        return join_with_sep(&deps.home, &[".config", "claude-code-proxy"], false);
39    }
40
41    let base = deps.env.get("XDG_CONFIG_HOME").cloned().unwrap_or_else(|| {
42        join_with_sep(&deps.home, &[".config"], false)
43            .to_string_lossy()
44            .into_owned()
45    });
46    join_with_sep(&base, &["claude-code-proxy"], false)
47}
48
49pub fn resolve_state_dir(deps: &DirResolverEnv) -> PathBuf {
50    if deps.platform == "win32" {
51        let local = deps
52            .env
53            .get("LOCALAPPDATA")
54            .cloned()
55            .unwrap_or_else(|| format!("{}\\AppData\\Local", deps.home));
56        return join_with_sep(&local, &["claude-code-proxy"], true);
57    }
58
59    let base = deps.env.get("XDG_STATE_HOME").cloned().unwrap_or_else(|| {
60        join_with_sep(&deps.home, &[".local", "state"], false)
61            .to_string_lossy()
62            .into_owned()
63    });
64    join_with_sep(&base, &["claude-code-proxy"], false)
65}
66
67pub fn legacy_config_dir(deps: &DirResolverEnv) -> PathBuf {
68    join_with_sep(&deps.home, &[".config", "claude-code-proxy"], false)
69}
70
71pub fn config_dir() -> PathBuf {
72    resolve_config_dir(&DirResolverEnv::default())
73}
74
75pub fn state_dir() -> PathBuf {
76    resolve_state_dir(&DirResolverEnv::default())
77}
78
79pub fn codex_auth_file(deps: &DirResolverEnv) -> PathBuf {
80    resolve_config_dir(deps).join("codex").join("auth.json")
81}
82
83pub fn kimi_auth_file(deps: &DirResolverEnv) -> PathBuf {
84    resolve_config_dir(deps).join("kimi").join("auth.json")
85}
86
87pub fn cursor_auth_file(deps: &DirResolverEnv) -> PathBuf {
88    resolve_config_dir(deps).join("cursor").join("auth.json")
89}
90
91pub fn kimi_device_id_file(deps: &DirResolverEnv) -> PathBuf {
92    resolve_config_dir(deps).join("kimi").join("device_id")
93}
94
95pub fn log_file() -> PathBuf {
96    resolve_state_dir(&DirResolverEnv::default()).join("proxy.log")
97}
98
99pub fn provider_auth_file(provider: &str) -> PathBuf {
100    let deps = DirResolverEnv::default();
101    resolve_config_dir(&deps).join(provider).join("auth.json")
102}
103
104pub fn provider_legacy_auth_file(provider: &str) -> PathBuf {
105    let deps = DirResolverEnv::default();
106    legacy_config_dir(&deps).join(provider).join("auth.json")
107}
108
109fn join_with_sep(base: &str, parts: &[&str], win32: bool) -> PathBuf {
110    let sep = '/';
111    let _ = win32;
112    let mut out = String::new();
113    for part in std::iter::once(base).chain(parts.iter().copied()) {
114        if !out.is_empty() && !out.ends_with(sep) {
115            out.push(sep);
116        }
117        out.push_str(part);
118    }
119    Path::new(&out).to_path_buf()
120}
121
122pub fn resolve_config_dir_for_env(
123    platform: &str,
124    home: &str,
125    env: &HashMap<String, String>,
126) -> PathBuf {
127    resolve_config_dir(&DirResolverEnv {
128        platform: platform.to_string(),
129        env: env.clone(),
130        home: home.to_string(),
131    })
132}
133
134pub fn resolve_state_dir_for_env(
135    platform: &str,
136    home: &str,
137    env: &HashMap<String, String>,
138) -> PathBuf {
139    resolve_state_dir(&DirResolverEnv {
140        platform: platform.to_string(),
141        env: env.clone(),
142        home: home.to_string(),
143    })
144}