use anyhow::Result;
use serde::Deserialize;
use super::opencrabs_install::is_valid_install_id;
use crate::config::{keys_path, write_secret_keys};
const SECTION: &str = "providers.opencrabs";
#[derive(Clone, PartialEq, Eq)]
pub struct StoredInstall {
pub install_id: String,
pub token: String,
}
impl std::fmt::Debug for StoredInstall {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StoredInstall")
.field("install_id", &self.install_id)
.field("token", &"<redacted>")
.finish()
}
}
#[derive(Deserialize, Default)]
struct KeysView {
#[serde(default)]
providers: ProvidersView,
}
#[derive(Deserialize, Default)]
struct ProvidersView {
#[serde(default)]
opencrabs: Option<OpencrabsView>,
}
#[derive(Deserialize)]
struct OpencrabsView {
install_id: Option<String>,
api_key: Option<String>,
}
pub fn load() -> Option<StoredInstall> {
load_from(&keys_path())
}
fn load_from(path: &std::path::Path) -> Option<StoredInstall> {
let raw = match std::fs::read_to_string(path) {
Ok(raw) => raw,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return None,
Err(e) => {
tracing::warn!("Could not read keys.toml for the built-in provider: {e}");
return None;
}
};
let view: KeysView = match toml::from_str(&raw) {
Ok(view) => view,
Err(e) => {
tracing::warn!("keys.toml does not parse, built-in provider needs setup: {e}");
return None;
}
};
let section = view.providers.opencrabs?;
let install_id = section.install_id.filter(|id| is_valid_install_id(id))?;
let token = section.api_key.filter(|t| !t.is_empty())?;
Some(StoredInstall { install_id, token })
}
pub fn save(install: &StoredInstall) -> Result<()> {
write_secret_keys(
SECTION,
&[
("install_id", install.install_id.as_str()),
("api_key", install.token.as_str()),
],
)
}