[web] Add support for reading from a configuration file
This commit is contained in:
parent
5087d4738d
commit
7f3b9ef2ec
|
@ -12,6 +12,8 @@ log = "0.4"
|
|||
env_logger = "0.9"
|
||||
askama = "0.10"
|
||||
argh = "0.1.6"
|
||||
serde = "1.0"
|
||||
serde_yaml = "0.8"
|
||||
rusttype = { version = "0.9.2", optional = true }
|
||||
image = { version = "0.23.14", optional = true }
|
||||
lazy_static = { version = "1.4.0", optional = true }
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
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
|
||||
|
@ -45,12 +50,14 @@ pub struct Run {
|
|||
option,
|
||||
default = "String::from(\"/etc/pronouns_today.yml\")",
|
||||
short = 'c'))]
|
||||
/// path to the config file (generated if needed)
|
||||
/// path to the config file (generated if needed). Defaults to
|
||||
/// /etc/pronouns_today.yml
|
||||
pub config: String,
|
||||
|
||||
#[argh(switch)]
|
||||
/// don't generate a config file if not present
|
||||
pub no_generate_cfg: bool,
|
||||
/// 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
|
||||
|
@ -61,3 +68,81 @@ pub struct Run {
|
|||
/// used in the custom URLs, e.g. "acaqeawdym")
|
||||
pub default_prefstr: Option<String>,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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 [`String`] contains the path to the new config file
|
||||
ConfigCreated(String),
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ pub mod contrast;
|
|||
pub mod statics;
|
||||
pub mod configuration;
|
||||
|
||||
use configuration::ConfigError;
|
||||
|
||||
use std::process::exit;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
|
@ -142,6 +145,7 @@ async fn not_found() -> impl Responder {
|
|||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
|
||||
env_logger::init();
|
||||
let args: configuration::PronounsTodayArgs = argh::from_env();
|
||||
|
||||
match args.command {
|
||||
|
@ -149,14 +153,30 @@ async fn main() -> std::io::Result<()> {
|
|||
println!("Support for dumping statics not yet implemented");
|
||||
Ok(())
|
||||
}
|
||||
configuration::SubCommand::Run(_subargs) => {
|
||||
configuration::SubCommand::Run(subargs) => {
|
||||
let config = match subargs.load_config() {
|
||||
Ok(config) => config,
|
||||
Err(ConfigError::IoError(e)) => {
|
||||
return Err(e);
|
||||
}
|
||||
Err(ConfigError::MalformedConfig(e)) => {
|
||||
eprintln!("Error parsing config file:\n{}", e);
|
||||
exit(1001);
|
||||
}
|
||||
Err(ConfigError::ConfigCreated(path)) => {
|
||||
println!("A config file has been generated at {}! Please check it out
|
||||
and modify it to your liking, and then run this command
|
||||
again", path);
|
||||
exit(0);
|
||||
}
|
||||
};
|
||||
log::info!("Starting with configuration {:?}", config);
|
||||
start_server().await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn start_server() -> std::io::Result<()> {
|
||||
env_logger::init();
|
||||
println!("Starting pronouns-today-web on 127.0.0.1:8080");
|
||||
HttpServer::new(|| {
|
||||
let logger = Logger::default();
|
||||
|
|
Loading…
Reference in New Issue