A Rust CLI for publishing thought records. Designed to work with thought.stream.
at main 3.6 kB view raw
1use anyhow::{Context, Result}; 2use directories::ProjectDirs; 3use serde::{Deserialize, Serialize}; 4use std::fs; 5use std::path::PathBuf; 6 7use crate::client::Session; 8 9#[derive(Debug, Clone, Serialize, Deserialize)] 10pub struct Credentials { 11 pub username: String, 12 pub password: String, 13 pub pds_uri: String, 14} 15 16impl Credentials { 17 pub fn new(username: String, password: String, pds_uri: String) -> Self { 18 Self { 19 username, 20 password, 21 pds_uri, 22 } 23 } 24} 25 26pub struct CredentialStore { 27 config_dir: PathBuf, 28} 29 30impl CredentialStore { 31 pub fn new() -> Result<Self> { 32 let project_dirs = ProjectDirs::from("com", "thoughtstream", "think") 33 .context("Failed to determine project directories")?; 34 35 let config_dir = project_dirs.config_dir().to_path_buf(); 36 37 // Create config directory if it doesn't exist 38 fs::create_dir_all(&config_dir) 39 .context("Failed to create config directory")?; 40 41 Ok(Self { config_dir }) 42 } 43 44 fn credentials_path(&self) -> PathBuf { 45 self.config_dir.join("credentials.json") 46 } 47 48 fn session_path(&self) -> PathBuf { 49 self.config_dir.join("session.json") 50 } 51 52 pub fn store(&self, credentials: &Credentials) -> Result<()> { 53 let json = serde_json::to_string_pretty(credentials) 54 .context("Failed to serialize credentials")?; 55 56 fs::write(self.credentials_path(), json) 57 .context("Failed to write credentials file")?; 58 59 println!("Credentials stored successfully"); 60 Ok(()) 61 } 62 63 pub fn load(&self) -> Result<Option<Credentials>> { 64 let path = self.credentials_path(); 65 66 if !path.exists() { 67 return Ok(None); 68 } 69 70 let json = fs::read_to_string(&path) 71 .context("Failed to read credentials file")?; 72 73 let credentials: Credentials = serde_json::from_str(&json) 74 .context("Failed to parse credentials file")?; 75 76 Ok(Some(credentials)) 77 } 78 79 pub fn clear(&self) -> Result<()> { 80 let path = self.credentials_path(); 81 82 if path.exists() { 83 fs::remove_file(&path) 84 .context("Failed to remove credentials file")?; 85 println!("Credentials cleared successfully"); 86 } else { 87 println!("No credentials found to clear"); 88 } 89 90 self.clear_session()?; 91 92 Ok(()) 93 } 94 95 pub fn exists(&self) -> bool { 96 self.credentials_path().exists() 97 } 98 99 pub fn store_session(&self, session: &Session) -> Result<()> { 100 let json = serde_json::to_string_pretty(session) 101 .context("Failed to serialize session")?; 102 103 fs::write(self.session_path(), json) 104 .context("Failed to write session file")?; 105 106 Ok(()) 107 } 108 109 pub fn load_session(&self) -> Result<Option<Session>> { 110 let path = self.session_path(); 111 112 if !path.exists() { 113 return Ok(None); 114 } 115 116 let json = fs::read_to_string(&path) 117 .context("Failed to read session file")?; 118 119 let session: Session = serde_json::from_str(&json) 120 .context("Failed to parse session file")?; 121 122 Ok(Some(session)) 123 } 124 125 pub fn clear_session(&self) -> Result<()> { 126 let path = self.session_path(); 127 128 if path.exists() { 129 fs::remove_file(&path) 130 .context("Failed to remove session file")?; 131 } 132 133 Ok(()) 134 } 135 136 pub fn session_exists(&self) -> bool { 137 self.session_path().exists() 138 } 139}