pueue/shared/settings.rs
2020-10-15 23:58:20 +02:00

130 lines
3.9 KiB
Rust

use std::collections::HashMap;
use std::fs::{create_dir_all, File};
use std::io::prelude::*;
use anyhow::{anyhow, Result};
use config::Config;
use log::info;
use rand::Rng;
use serde_derive::{Deserialize, Serialize};
use crate::platform::directories::*;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Shared {
pub port: String,
pub secret: String,
pub pueue_directory: String,
pub use_unix_socket: bool,
pub unix_socket_path: String,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Client {
pub read_local_logs: bool,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Daemon {
pub default_parallel_tasks: usize,
#[serde(default = "pause_on_failure_default")]
pub pause_on_failure: bool,
pub callback: Option<String>,
pub groups: HashMap<String, usize>,
}
fn pause_on_failure_default() -> bool {
false
}
/// The struct representation of a full configuration.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Settings {
pub shared: Shared,
pub client: Client,
pub daemon: Daemon,
}
impl Settings {
/// This function creates a new configuration instance and
/// populates it with default values for every option.
/// If a local config file already exists it is parsed and
/// overwrites the default option values.
/// The local config is located at "~/.config/pueue.yml".
pub fn new() -> Result<Settings> {
let mut config = Config::new();
config.set_default("shared.port", "6924")?;
config.set_default("shared.secret", gen_random_secret())?;
config.set_default("shared.use_unix_socket", false)?;
config.set_default("shared.unix_socket_path", get_unix_socket_path()?)?;
// Client specific config
config.set_default("client.read_local_logs", true)?;
// Daemon specific config
config.set_default("daemon.default_parallel_tasks", 1)?;
config.set_default("daemon.pause_on_failure", false)?;
config.set_default("daemon.callback", None::<String>)?;
config.set_default("daemon.groups", HashMap::<String, i64>::new())?;
// Add in the home config file
parse_config(&mut config)?;
// You can deserialize (and thus freeze) the entire configuration
Ok(config.try_into()?)
}
/// Save the current configuration as a file to the configuration path.
/// The file is written to the main configuration directory of the respective OS.
pub fn save(&self) -> Result<()> {
let config_path = default_config_directory()?.join("pueue.yml");
let config_dir = config_path
.parent()
.ok_or_else(|| anyhow!("Couldn't resolve config dir"))?;
// Create the config dir, if it doesn't exist yet
if !config_dir.exists() {
create_dir_all(config_dir)?;
}
let content = serde_yaml::to_string(self)?;
let mut file = File::create(config_path)?;
file.write_all(content.as_bytes())?;
Ok(())
}
}
fn parse_config(settings: &mut Config) -> Result<()> {
info!("Parsing config files");
for directory in get_config_directories()?.into_iter() {
let path = directory.join("pueue.yml");
info!("Checking path: {:?}", &path);
if path.exists() {
info!("Parsing config file at: {:?}", path);
let config_file = config::File::with_name(path.to_str().unwrap());
settings.merge(config_file)?;
}
}
Ok(())
}
fn gen_random_secret() -> String {
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789)(*&^%$#@!~";
const PASSWORD_LEN: usize = 20;
let mut rng = rand::thread_rng();
let secret: String = (0..PASSWORD_LEN)
.map(|_| {
let idx = rng.gen_range(0, CHARSET.len());
CHARSET[idx] as char
})
.collect();
secret
}