A Rust CLI for publishing thought records. Designed to work with thought.stream.
1use anyhow::{Context, Result};
2use directories::ProjectDirs;
3use serde::{Deserialize, Serialize};
4use std::fs;
5use std::path::PathBuf;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Credentials {
9 pub username: String,
10 pub password: String,
11 pub pds_uri: String,
12}
13
14impl Credentials {
15 pub fn new(username: String, password: String, pds_uri: String) -> Self {
16 Self {
17 username,
18 password,
19 pds_uri,
20 }
21 }
22}
23
24pub struct CredentialStore {
25 config_dir: PathBuf,
26}
27
28impl CredentialStore {
29 pub fn new() -> Result<Self> {
30 let project_dirs = ProjectDirs::from("com", "thoughtstream", "think")
31 .context("Failed to determine project directories")?;
32
33 let config_dir = project_dirs.config_dir().to_path_buf();
34
35 // Create config directory if it doesn't exist
36 fs::create_dir_all(&config_dir)
37 .context("Failed to create config directory")?;
38
39 Ok(Self { config_dir })
40 }
41
42 fn credentials_path(&self) -> PathBuf {
43 self.config_dir.join("credentials.json")
44 }
45
46 pub fn store(&self, credentials: &Credentials) -> Result<()> {
47 let json = serde_json::to_string_pretty(credentials)
48 .context("Failed to serialize credentials")?;
49
50 fs::write(self.credentials_path(), json)
51 .context("Failed to write credentials file")?;
52
53 println!("Credentials stored successfully");
54 Ok(())
55 }
56
57 pub fn load(&self) -> Result<Option<Credentials>> {
58 let path = self.credentials_path();
59
60 if !path.exists() {
61 return Ok(None);
62 }
63
64 let json = fs::read_to_string(&path)
65 .context("Failed to read credentials file")?;
66
67 let credentials: Credentials = serde_json::from_str(&json)
68 .context("Failed to parse credentials file")?;
69
70 Ok(Some(credentials))
71 }
72
73 pub fn clear(&self) -> Result<()> {
74 let path = self.credentials_path();
75
76 if path.exists() {
77 fs::remove_file(&path)
78 .context("Failed to remove credentials file")?;
79 println!("Credentials cleared successfully");
80 } else {
81 println!("No credentials found to clear");
82 }
83
84 Ok(())
85 }
86
87 pub fn exists(&self) -> bool {
88 self.credentials_path().exists()
89 }
90}