cadmus_core/github/
device_flow.rs1use secrecy::{ExposeSecret, SecretString};
2use std::fs::{self, File};
3use std::io::{Read, Write};
4use std::path::PathBuf;
5
6#[cfg(not(feature = "test"))]
11const TOKEN_RELATIVE_PATH: &str = ".adds/cadmus/.github_token";
12
13#[cfg(feature = "test")]
14const TOKEN_RELATIVE_PATH: &str = ".adds/cadmus-tst/.github_token";
15
16pub fn save_token(token: &SecretString) -> Result<(), String> {
24 let path = token_path();
25 tracing::debug!(path = %path.display(), "Saving GitHub token");
26
27 if let Some(parent) = path.parent() {
28 fs::create_dir_all(parent).map_err(|e| format!("Failed to create token dir: {}", e))?;
29 }
30
31 let mut file =
32 File::create(&path).map_err(|e| format!("Failed to create token file: {}", e))?;
33 file.write_all(token.expose_secret().as_bytes())
34 .map_err(|e| format!("Failed to write token: {}", e))?;
35
36 #[cfg(unix)]
37 {
38 use std::os::unix::fs::PermissionsExt;
39 fs::set_permissions(&path, fs::Permissions::from_mode(0o600))
40 .map_err(|e| format!("Failed to set token file permissions: {}", e))?;
41 }
42
43 tracing::info!("GitHub token saved");
44 Ok(())
45}
46
47pub fn load_token() -> Result<Option<SecretString>, String> {
55 let path = token_path();
56 tracing::debug!(path = %path.display(), "Loading GitHub token");
57
58 if !path.exists() {
59 tracing::debug!("No saved token found");
60 return Ok(None);
61 }
62
63 let mut contents = String::new();
64 File::open(&path)
65 .map_err(|e| format!("Failed to open token file: {}", e))?
66 .read_to_string(&mut contents)
67 .map_err(|e| format!("Failed to read token file: {}", e))?;
68
69 let token = contents.trim().to_owned();
70 if token.is_empty() {
71 tracing::warn!("Token file exists but is empty");
72 return Ok(None);
73 }
74
75 tracing::info!("GitHub token loaded from disk");
76 Ok(Some(SecretString::from(token)))
77}
78
79pub fn delete_token() -> Result<(), String> {
90 let path = token_path();
91 tracing::debug!(path = %path.display(), "Deleting GitHub token");
92
93 if !path.exists() {
94 return Ok(());
95 }
96
97 fs::remove_file(&path).map_err(|e| format!("Failed to delete token file: {}", e))?;
98 tracing::info!("GitHub token deleted");
99 Ok(())
100}
101
102fn token_path() -> PathBuf {
103 #[cfg(test)]
104 return std::env::temp_dir()
105 .join("cadmus-test")
106 .join(TOKEN_RELATIVE_PATH);
107
108 #[cfg(all(not(test), feature = "emulator"))]
109 return PathBuf::from("/tmp").join(TOKEN_RELATIVE_PATH);
110
111 #[cfg(all(not(test), not(feature = "emulator")))]
112 return PathBuf::from(crate::settings::INTERNAL_CARD_ROOT).join(TOKEN_RELATIVE_PATH);
113}