159 lines
4.7 KiB
Rust
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),
|
|
}
|