PronounsToday/web/src/configuration.rs

159 lines
4.7 KiB
Rust

use std::path::PathBuf;
use pronouns_today::PronounList;
use pronouns_today::UserPreferences;
use std::fs::OpenOptions;
use std::fs::File;
use std::io::Write;
use pronouns_today::InstanceSettings;
use argh::FromArgs;
use serde::{Deserialize, Serialize};
#[derive(FromArgs, PartialEq, Debug)]
/// Everything you need to selfhost a pronouns.today server
pub struct PronounsTodayArgs {
#[argh(subcommand)]
pub command: SubCommand
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
pub enum SubCommand {
Run(Run),
#[cfg(feature = "embed_static_assets")]
DumpStatics(DumpStatics),
}
#[derive(FromArgs, PartialEq, Debug)]
#[cfg(feature = "embed_static_assets")]
#[argh(subcommand, name = "dump-statics")]
/// Dump necessary static files to a directory to be served by a webserver. Serving
/// static files with a dedicated webserver isn't necessary, but may improve performance.
/// If serving static files, all files should be available under the /static/filename.ext
/// route.
pub struct DumpStatics {
#[argh(option, default = "String::from(\"./static/\")")]
/// the directory to write the static files to
///
/// Defaults to ./static/
pub destination: String,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand, name = "run")]
/// Run the server from the specified config file. Commandline options override config
/// options.
pub struct Run {
#[cfg_attr(feature = "docker",
argh(
option,
default = "PathBuf::from(\"/data/config.yml\")",
short = 'c'))]
#[cfg_attr(not(feature = "docker"),
argh(
option,
default = "PathBuf::from(\"/etc/pronouns_today.yml\")",
short = 'c'))]
/// path to the config file (generated if needed). Defaults to
/// /etc/pronouns_today.yml
pub config: PathBuf,
#[argh(switch)]
/// don't attempt to read or generate a config file, just use command line args. Missing
/// options will be filled in using sane defaults
pub no_read_cfg: bool,
#[argh(option, short = 'p')]
/// the port to listen on
pub port: Option<u16>,
#[argh(option)]
/// default pronoun probabilites (formatted as a prefstring, like the ones in the
/// used in the custom URLs, e.g. "acaqeawdym")
pub default_prefstr: Option<UserPreferences>,
#[argh(option)]
/// the pronouns to be supported by the server. see configuration file documentation
/// for warnings when changing this value. formatted as a list of comma seperated
/// five-form pronouns, e.g. she/her/her/hers/herself,he/him/his/his/himself
pub pronouns: Option<PronounList>,
}
impl Run {
/// Read the config from the provided file, and apply overrides from args
///
/// If `--no-read-cfg` was passed, no config file is present, and there aren't
/// enough args to populate the configuration, returns an [`Err`] with a list of
/// arguments that were missing.
pub fn load_config(self) -> Result<Conf, ConfigError> {
if !self.no_read_cfg {
match File::open(&self.config) {
Ok(raw_cfg) => {
serde_yaml::from_reader(raw_cfg)
.map_err(ConfigError::MalformedConfig)
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
OpenOptions::new()
.write(true)
.create(true)
.open(&self.config)
.and_then(|mut c|
c.write_all(include_bytes!("../assets/default_config.yml"))
)
.map_err(ConfigError::IoError)?;
Err(ConfigError::ConfigCreated(self.config.clone()))
}
Err(e) => {
Err(ConfigError::IoError(e))
}
}
} else {
Ok(Conf::default())
}.map(|config| config.update_with(self))
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
/// A parsed config file for Pronouns.Today web
pub struct Conf {
#[serde(flatten)]
/// General settings for the pronouns.today library
pub instance_settings: InstanceSettings,
/// The port for the server to bind to. Defaults to 1312
pub port: u16,
}
impl Conf {
fn update_with(mut self, args: Run) -> Conf {
self.port = args.port.unwrap_or(self.port);
self.instance_settings.pronoun_list = args.pronouns.map(Into::into).unwrap_or(self.instance_settings.pronoun_list);
self
}
}
impl Default for Conf {
fn default() -> Self {
Conf {
instance_settings: InstanceSettings::default(),
port: 1312,
}
}
}
/// An error occured while trying to load the configuration
pub enum ConfigError {
/// There wase a problem reading/writing the config file
IoError(std::io::Error),
/// There wase a problem deserializing the config file
MalformedConfig(serde_yaml::Error),
/// The config file was missing, but has been created
///
/// The program should now prompt the user to fill the config in, and then exit. The
/// provided [`PathBuf`] contains the path to the new config file
ConfigCreated(PathBuf),
}