use crate::llm::Provider;
use anyhow::Result;
use keyring::Entry;
use serde::{Deserialize, Serialize};
pub trait ProviderSecret: Serialize + for<'de> Deserialize<'de> {
fn validate(&self) -> Result<()>;
fn masked(&self) -> String;
}
const SECRET_SERVICE_NAME: &str = "doum-cli";
pub struct SecretManager;
impl SecretManager {
pub fn save<T: ProviderSecret>(provider: &Provider, secret: &T) -> Result<()> {
secret.validate()?;
let entry = Entry::new(provider.as_str(), SECRET_SERVICE_NAME)
.map_err(|e| anyhow::anyhow!("Failed to access keyring: {}", e))?;
let value = serde_json::to_string(secret)
.map_err(|e| anyhow::anyhow!("Failed to serialize secret: {}", e))?;
entry
.set_password(&value)
.map_err(|e| anyhow::anyhow!("Failed to save to keyring: {}", e))?;
Ok(())
}
pub fn load<T: ProviderSecret>(provider: &Provider) -> Result<T> {
let entry = Entry::new(provider.as_str(), SECRET_SERVICE_NAME)
.map_err(|e| anyhow::anyhow!("Failed to access keyring: {}", e))?;
let secret_json = entry
.get_password()
.map_err(|e| anyhow::anyhow!("Failed to retrieve from keyring: {}", e))?;
serde_json::from_str(&secret_json)
.map_err(|e| anyhow::anyhow!("Failed to parse secret: {}", e))
}
pub fn delete(provider: &Provider) -> Result<()> {
let entry = Entry::new(provider.as_str(), SECRET_SERVICE_NAME)
.map_err(|e| anyhow::anyhow!("Failed to access keyring: {}", e))?;
entry
.delete_credential()
.map_err(|e| anyhow::anyhow!("Failed to delete from keyring: {}", e))?;
Ok(())
}
}