1#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub enum PathValueShape {
8 Single,
9 ColonList,
10}
11
12#[derive(Clone, Copy, Debug, Eq, PartialEq)]
13pub struct PathShapedEnv {
14 pub key: &'static str,
15 pub doc_comment: &'static str,
16 pub value_shape: PathValueShape,
17}
18
19impl PathShapedEnv {
20 pub fn path_values<'a>(&self, value: &'a str) -> Vec<&'a str> {
21 match self.value_shape {
22 PathValueShape::Single => vec![value],
23 PathValueShape::ColonList if value.is_empty() => vec![value],
24 PathValueShape::ColonList => value.split(':').collect(),
25 }
26 }
27}
28
29pub fn claude_path_shaped_env(key: &str) -> Option<&'static PathShapedEnv> {
30 CLAUDE_PATH_SHAPED_ENVS
31 .iter()
32 .find(|entry| entry.key == key)
33}
34
35pub const CLAUDE_PATH_SHAPED_ENVS: &[PathShapedEnv] = &[
36 PathShapedEnv {
39 key: "CLAUDE_BG_AUTH_SNAPSHOT_PATH",
40 doc_comment: "Auth snapshot file path. Source: Claude Code source string.",
41 value_shape: PathValueShape::Single,
42 },
43 PathShapedEnv {
45 key: "CLAUDE_CODE_CLIENT_CERT",
46 doc_comment: "mTLS client certificate file. Source: Claude Code environment variable documentation.",
47 value_shape: PathValueShape::Single,
48 },
49 PathShapedEnv {
51 key: "CLAUDE_CODE_CLIENT_KEY",
52 doc_comment: "mTLS client private key file. Source: Claude Code environment variable documentation.",
53 value_shape: PathValueShape::Single,
54 },
55 PathShapedEnv {
57 key: "CLAUDE_CODE_DEBUG_LOGS_DIR",
58 doc_comment: "Debug log file path. Source: Claude Code environment variable documentation.",
59 value_shape: PathValueShape::Single,
60 },
61 PathShapedEnv {
63 key: "CLAUDE_CODE_GIT_BASH_PATH",
64 doc_comment: "Git Bash executable path. Source: Claude Code environment variable documentation.",
65 value_shape: PathValueShape::Single,
66 },
67 PathShapedEnv {
69 key: "CLAUDE_CODE_PLUGIN_CACHE_DIR",
70 doc_comment: "Plugin parent directory. Source: Claude Code environment variable documentation.",
71 value_shape: PathValueShape::Single,
72 },
73 PathShapedEnv {
75 key: "CLAUDE_CODE_PLUGIN_SEED_DIR",
76 doc_comment: "Plugin seed directory list. Source: Claude Code environment variable documentation.",
77 value_shape: PathValueShape::ColonList,
78 },
79 PathShapedEnv {
82 key: "CLAUDE_CODE_SHELL_PREFIX",
83 doc_comment: "Shell command prefix path. Source: Claude Code environment variable documentation.",
84 value_shape: PathValueShape::Single,
85 },
86 PathShapedEnv {
88 key: "CLAUDE_CODE_TMPDIR",
89 doc_comment: "Claude Code temp directory root. Source: Claude Code environment variable documentation.",
90 value_shape: PathValueShape::Single,
91 },
92 PathShapedEnv {
94 key: "CLAUDE_CONFIG_DIR",
95 doc_comment: "Configuration directory containing settings, credentials, history, and plugins. Source: Claude Code environment variable documentation.",
96 value_shape: PathValueShape::Single,
97 },
98 PathShapedEnv {
100 key: "CLAUDE_ENV_FILE",
101 doc_comment: "Shell script path sourced before Bash commands. Source: Claude Code environment variable documentation.",
102 value_shape: PathValueShape::Single,
103 },
104 PathShapedEnv {
107 key: "CLAUDE_PLUGIN_ROOT",
108 doc_comment: "Plugin root directory. Source: Claude Code source string.",
109 value_shape: PathValueShape::Single,
110 },
111 PathShapedEnv {
114 key: "CLAUDE_PROJECT_DIR",
115 doc_comment: "Project directory for hooks and subprocesses. Source: Claude Code source string and convention.",
116 value_shape: PathValueShape::Single,
117 },
118 PathShapedEnv {
121 key: "CLAUDE_SECURESTORAGE_CONFIG_DIR",
122 doc_comment: "Secure storage configuration directory. Source: Claude Code source string.",
123 value_shape: PathValueShape::Single,
124 },
125 PathShapedEnv {
127 key: "CLAUDE_SKILL_DIR",
128 doc_comment: "Skills directory. Source: Claude Code source string.",
129 value_shape: PathValueShape::Single,
130 },
131 PathShapedEnv {
133 key: "CLAUDE_TMPDIR",
134 doc_comment: "Claude temp directory root. Source: Claude Code source string.",
135 value_shape: PathValueShape::Single,
136 },
137];
138
139#[cfg(test)]
140mod tests {
141 use std::collections::BTreeSet;
142
143 use super::*;
144
145 #[test]
146 fn claude_path_shaped_envs_have_frozen_cardinality() {
147 assert_eq!(CLAUDE_PATH_SHAPED_ENVS.len(), 16);
148 }
149
150 #[test]
151 fn claude_path_shaped_envs_have_doc_comments() {
152 for entry in CLAUDE_PATH_SHAPED_ENVS {
153 assert!(
154 !entry.doc_comment.trim().is_empty(),
155 "{} is missing an entry doc comment",
156 entry.key
157 );
158 }
159 }
160
161 #[test]
162 fn claude_path_shaped_envs_are_unique_claude_keys() {
163 let mut seen = BTreeSet::new();
164
165 for entry in CLAUDE_PATH_SHAPED_ENVS {
166 assert!(entry.key.starts_with("CLAUDE_"), "{}", entry.key);
167 assert!(seen.insert(entry.key), "{} is duplicated", entry.key);
168 }
169 }
170
171 #[test]
172 fn colon_lists_preserve_each_declared_path() {
173 let entry = claude_path_shaped_env("CLAUDE_CODE_PLUGIN_SEED_DIR").expect("entry");
174
175 assert_eq!(
176 entry.path_values("/seed/a:/seed/b"),
177 vec!["/seed/a", "/seed/b"]
178 );
179 }
180}