1use std::io::Write;
2
3use nils_common::shell::{SingleQuoteEscapeStyle, quote_posix_single_with_style};
4
5pub fn show() -> i32 {
6 let stdout = std::io::stdout();
7 let mut stdout = stdout.lock();
8 show_with_io(&mut stdout)
9}
10
11pub fn show_with_io(stdout: &mut impl Write) -> i32 {
12 let snapshot = crate::runtime::config_snapshot();
13
14 let _ = writeln!(stdout, "GEMINI_CLI_MODEL={}", snapshot.model);
15 let _ = writeln!(stdout, "GEMINI_CLI_REASONING={}", snapshot.reasoning);
16 let _ = writeln!(
17 stdout,
18 "GEMINI_ALLOW_DANGEROUS_ENABLED={}",
19 snapshot.allow_dangerous_enabled_raw
20 );
21
22 if let Some(path) = snapshot.secret_dir {
23 let _ = writeln!(stdout, "GEMINI_SECRET_DIR={}", path.to_string_lossy());
24 } else {
25 let _ = writeln!(stdout, "GEMINI_SECRET_DIR=");
26 }
27
28 if let Some(path) = snapshot.auth_file {
29 let _ = writeln!(stdout, "GEMINI_AUTH_FILE={}", path.to_string_lossy());
30 } else {
31 let _ = writeln!(stdout, "GEMINI_AUTH_FILE=");
32 }
33
34 if let Some(path) = snapshot.secret_cache_dir {
35 let _ = writeln!(stdout, "GEMINI_SECRET_CACHE_DIR={}", path.to_string_lossy());
36 } else {
37 let _ = writeln!(stdout, "GEMINI_SECRET_CACHE_DIR=");
38 }
39
40 let _ = writeln!(
41 stdout,
42 "GEMINI_PROMPT_SEGMENT_ENABLED={}",
43 snapshot.prompt_segment_enabled
44 );
45 let _ = writeln!(
46 stdout,
47 "GEMINI_AUTO_REFRESH_ENABLED={}",
48 snapshot.auto_refresh_enabled
49 );
50 let _ = writeln!(
51 stdout,
52 "GEMINI_AUTO_REFRESH_MIN_DAYS={}",
53 snapshot.auto_refresh_min_days
54 );
55
56 0
57}
58
59pub fn set(key: &str, value: &str) -> i32 {
60 let stdout = std::io::stdout();
61 let mut stdout = stdout.lock();
62 let stderr = std::io::stderr();
63 let mut stderr = stderr.lock();
64 set_with_io(key, value, &mut stdout, &mut stderr)
65}
66
67pub fn set_with_io(
68 key: &str,
69 value: &str,
70 stdout: &mut impl Write,
71 stderr: &mut impl Write,
72) -> i32 {
73 match key {
74 "model" | "GEMINI_CLI_MODEL" => {
75 let _ = writeln!(
76 stdout,
77 "export GEMINI_CLI_MODEL={}",
78 quote_posix_single(value)
79 );
80 0
81 }
82 "reasoning" | "reason" | "GEMINI_CLI_REASONING" => {
83 let _ = writeln!(
84 stdout,
85 "export GEMINI_CLI_REASONING={}",
86 quote_posix_single(value)
87 );
88 0
89 }
90 "dangerous" | "allow-dangerous" | "GEMINI_ALLOW_DANGEROUS_ENABLED" => {
91 let lowered = value.trim().to_ascii_lowercase();
92 if lowered != "true" && lowered != "false" {
93 let _ = writeln!(
94 stderr,
95 "gemini-cli config: dangerous must be true|false (got: {})",
96 value
97 );
98 return 64;
99 }
100 let _ = writeln!(stdout, "export GEMINI_ALLOW_DANGEROUS_ENABLED={}", lowered);
101 0
102 }
103 _ => {
104 let _ = writeln!(stderr, "gemini-cli config: unknown key: {key}");
105 let _ = writeln!(stderr, "gemini-cli config: keys: model|reasoning|dangerous");
106 64
107 }
108 }
109}
110
111fn quote_posix_single(raw: &str) -> String {
112 quote_posix_single_with_style(raw, SingleQuoteEscapeStyle::DoubleQuoteBoundary)
113}
114
115#[cfg(test)]
116mod tests {
117 use super::quote_posix_single;
118
119 #[test]
120 fn quote_posix_single_handles_single_quotes_and_empty() {
121 assert_eq!(quote_posix_single(""), "''");
122 assert_eq!(quote_posix_single("abc"), "'abc'");
123 assert_eq!(quote_posix_single("a'b"), "'a'\"'\"'b'");
124 }
125}