use serde::{Deserialize, Serialize};
use std::env::home_dir;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
use tracing::{debug, warn};
use crate::WORK_DIR_FILE;
const DEFAULT: &str = include_str!("default.toml");
pub static CONFIG: LazyLock<Config> = LazyLock::new(|| Config::new());
#[derive(Debug, Default, Serialize, Deserialize)]
struct SSHKey {
public: PathBuf,
private: PathBuf,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct Config {
sshkey: Option<SSHKey>,
}
impl Config {
pub fn new() -> Self {
let config = if cfg!(test) {
WORK_DIR_FILE("test.config.toml")
} else {
WORK_DIR_FILE("config.toml")
};
if !config.is_file() {
std::fs::write(&config, DEFAULT).expect("Failed to write config.toml");
warn!(
file = ?config,
"💡 The first run creates a default config"
)
}
Self::load_from_file(config.as_path())
}
pub fn load_from_file(f: &Path) -> Self {
debug!(file = ?f, "Loading config");
let content = std::fs::read_to_string(f).expect("Failed to read config.toml");
let mut config: Config = toml::from_str(&content).expect("Failed to parse config.toml");
if config.sshkey.is_none() {
let home = home_dir().unwrap();
config.sshkey = Some(SSHKey {
public: home.join(".ssh").join("id_rsa.pub"),
private: home.join(".ssh").join("id_rsa"),
});
warn!(
"💡 No sshkey specified, using default at {:?}/.ssh/id_rsa",
home
);
}
config
}
pub fn get_public_key(&self) -> &Path {
self.sshkey.as_ref().unwrap().public.as_path()
}
pub fn get_private_key(&self) -> &Path {
self.sshkey.as_ref().unwrap().private.as_path()
}
pub fn read_public_key(&self) -> Result<String, Error> {
let key = self.get_public_key();
if !key.is_file() {
return Err(Error::new(
ErrorKind::NotFound,
"public key not found, you can generate it by `ssh-keygen` and set it to config",
));
}
std::fs::read_to_string(key)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config() {
let config = &CONFIG;
println!("config: {:#?}", config.sshkey);
assert!(config.sshkey.is_some());
}
}