Store error messages as data

This commit is contained in:
Daniel Sockwell 2019-10-07 23:21:59 -04:00
parent 74a2ac3bb2
commit 503ddfd510
4 changed files with 80 additions and 145 deletions

View File

@ -1,7 +1,7 @@
use crate::{config::deployment_cfg_types::*, err::FromStrOrDie, maybe_update};
use crate::config::deployment_cfg_types::*;
use std::collections::HashMap;
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct DeploymentConfig<'a> {
pub env: Env,
pub log_level: LogLevel,
@ -12,29 +12,12 @@ pub struct DeploymentConfig<'a> {
pub sse_interval: SseInterval,
pub ws_interval: WsInterval,
}
impl Default for DeploymentConfig<'_> {
fn default() -> Self {
Self {
env: Env::Development,
log_level: LogLevel::default(),
address: FlodgattAddr::default(),
port: Port2::default(),
unix_socket: Socket::default(),
cors: Cors {
allowed_methods: vec!["GET", "OPTIONS"],
allowed_headers: vec!["Authorization", "Accept", "Cache-Control"],
},
sse_interval: SseInterval::default(),
ws_interval: WsInterval::default(),
}
}
}
impl DeploymentConfig<'_> {
pub fn from_env(env_vars: HashMap<String, String>) -> Self {
let mut res = Self::default()
.maybe_update_env(env_vars.get("NODE_ENV").map(Env::from_str_or_die))
.maybe_update_env(env_vars.get("RUST_ENV").map(Env::from_str_or_die));
let mut res = Self::default();
res.env = Env::from_env_var_or_die(env_vars.get("NODE_ENV"));
res.env = Env::from_env_var_or_die(env_vars.get("RUST_ENV"));
res.log_level = LogLevel::from_env_var_or_die(env_vars.get("RUST_LOG"));
res.address = FlodgattAddr::from_env_var_or_die(env_vars.get("BIND"));
res.port = Port2::from_env_var_or_die(env_vars.get("PORT"));
@ -45,14 +28,6 @@ impl DeploymentConfig<'_> {
res.log()
}
maybe_update!(maybe_update_env; env: Env);
// maybe_update!(maybe_update_port; port: Port);
maybe_update!(maybe_update_address; address: FlodgattAddr);
// maybe_update!(maybe_update_unix_socket; Some(unix_socket: Socket));
// maybe_update!(maybe_update_log_level; log_level: LogLevel);
// maybe_update!(maybe_update_sse_int; sse_interval: SseInterval);
// maybe_update!(maybe_update_ws_int; ws_interval: WsInterval);
fn log(self) -> Self {
log::warn!("Using deployment configuration:\n {:#?}", &self);
self

View File

@ -1,28 +1,36 @@
use crate::{derive_from_str_or_die, err::FromStrOrDie, from_env_var};
use crate::from_env_var;
use std::{fmt, net::IpAddr, os::unix::net::UnixListener, str::FromStr, time::Duration};
use strum_macros::{EnumString, EnumVariantNames};
/// The current environment, which controls what file to read other ENV vars from
from_env_var!(/// The current environment, which controls what file to read other ENV vars from
Env {
inner: EnvInner::Development; EnvInner,
env_var: "RUST_ENV",
allowed_values: format!("one of: {:?}", EnvInner::variants()),
}
inner_from_str(|s| EnvInner::from_str(s).ok())
);
#[derive(EnumString, EnumVariantNames, Debug)]
#[strum(serialize_all = "snake_case")]
pub enum Env {
pub enum EnvInner {
Production,
Development,
}
derive_from_str_or_die!(Env {
name: "RUST_ENV",
value: format!("one of: {:?}", Self::variants())
});
// The address to run Flodgatt on
from_env_var!(FlodgattAddr {
inner: IpAddr::V4("127.0.0.1".parse().expect("hardcoded")); IpAddr,
env_var: "BIND",
allowed_values: "a valid address (e.g., 127.0.0.1)".to_string(),
}
inner_from_str(|s| s.parse().ok()));
/// How verbosely Flodgatt should log messages
from_env_var!(/// The address to run Flodgatt on
FlodgattAddr {
inner: IpAddr::V4("127.0.0.1".parse().expect("hardcoded")); IpAddr,
env_var: "BIND",
allowed_values: "a valid address (e.g., 127.0.0.1)".to_string(),
}
inner_from_str(|s| s.parse().ok()));
from_env_var!(/// How verbosely Flodgatt should log messages
LogLevel {
inner: LogLevelInner::Warn; LogLevelInner,
env_var: "RUST_LOG",
allowed_values: format!("one of {:?}", LogLevelInner::variants()),
}
inner_from_str(|s| LogLevelInner::from_str(s).ok()));
#[derive(EnumString, EnumVariantNames, Debug)]
#[strum(serialize_all = "snake_case")]
pub enum LogLevelInner {
@ -32,47 +40,37 @@ pub enum LogLevelInner {
Warn,
Error,
}
from_env_var!(LogLevel {
inner: LogLevelInner::Warn; LogLevelInner,
env_var: "RUST_LOG",
allowed_values: format!("one of {:?}", LogLevelInner::variants()),
}
inner_from_str(|s| LogLevelInner::from_str(s).ok()));
// A Unix Socket to use in place of a local address
from_env_var!(Socket{
inner: None; Option<UnixListener>,
env_var: "SOCKET",
allowed_values: "a valid Unix Socket".to_string(),
}
inner_from_str(|s| match UnixListener::bind(s).ok() {
Some(socket) => Some(Some(socket)),
None => None,
}));
// The time between replies sent via WebSocket
from_env_var!(WsInterval {
inner: Duration::from_millis(100); Duration,
env_var: "WS_FREQ",
allowed_values: "a number of milliseconds".to_string(),
}
inner_from_str(|s| s.parse().map(|num| Duration::from_millis(num)).ok()));
// The time between replies sent via Server Sent Events
from_env_var!(SseInterval {
inner: Duration::from_millis(100); Duration,
env_var: "SSE_FREQ",
allowed_values: "a number of milliseconds".to_string(),
}
inner_from_str(|s| s.parse().map(|num| Duration::from_millis(num)).ok()));
// The port to run Flodgatt on
from_env_var!(Port2 {
inner: 4000; u16,
env_var: "PORT",
allowed_values: "a number".to_string(),
}
inner_from_str(|s| s.parse().ok()));
from_env_var!(/// A Unix Socket to use in place of a local address
Socket{
inner: None; Option<UnixListener>,
env_var: "SOCKET",
allowed_values: "a valid Unix Socket".to_string(),
}
inner_from_str(|s| match UnixListener::bind(s).ok() {
Some(socket) => Some(Some(socket)),
None => None,
}));
from_env_var!(/// The time between replies sent via WebSocket
WsInterval {
inner: Duration::from_millis(100); Duration,
env_var: "WS_FREQ",
allowed_values: "a number of milliseconds".to_string(),
}
inner_from_str(|s| s.parse().map(|num| Duration::from_millis(num)).ok()));
from_env_var!(/// The time between replies sent via Server Sent Events
SseInterval {
inner: Duration::from_millis(100); Duration,
env_var: "SSE_FREQ",
allowed_values: "a number of milliseconds".to_string(),
}
inner_from_str(|s| s.parse().map(|num| Duration::from_millis(num)).ok()));
from_env_var!(/// The port to run Flodgatt on
Port2 {
inner: 4000; u16,
env_var: "PORT",
allowed_values: "a number".to_string(),
}
inner_from_str(|s| s.parse().ok()));
/// Permissions for Cross Origin Resource Sharing (CORS)
pub struct Cors<'a> {
@ -88,3 +86,11 @@ impl fmt::Debug for Cors<'_> {
)
}
}
impl std::default::Default for Cors<'_> {
fn default() -> Self {
Self {
allowed_methods: vec!["GET", "OPTIONS"],
allowed_headers: vec!["Authorization", "Accept", "Cache-Control"],
}
}
}

View File

@ -22,42 +22,10 @@ macro_rules! maybe_update {
None => Self { ..self }
}
})}
#[macro_export]
macro_rules! derive_deref {
($name:ident: $type:ty) => {
impl std::ops::Deref for $name {
type Target = $type;
fn deref(&self) -> &$type {
&self.0
}
}
};
}
#[macro_export]
macro_rules! derive_oneline_debug {
($item:ident) => {
impl std::fmt::Debug for $item {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.0)
}
}
};
}
#[macro_export]
macro_rules! derive_from_str_or_die {
($item:ident {name: $name:expr,
value: $value:expr}) => {
impl FromStrOrDie<Self> for $item {
fn name_and_values() -> (&'static str, String) {
($name, $value)
}
}
};
}
#[macro_export]
macro_rules! from_env_var {
($name:ident {
($(#[$outer:meta])*
$name:ident {
inner: $inner:expr; $type:ty,
env_var: $env_var:tt,
allowed_values: $allowed_values:expr,
@ -80,6 +48,15 @@ macro_rules! from_env_var {
&self.inner
}
}
impl std::default::Default for $name {
fn default() -> Self {
$name {
inner: $inner,
env_var: $env_var.to_string(),
allowed_values: $allowed_values,
}
}
}
impl $name {
fn inner_from_str($arg: &str) -> Option<$type> {
$body
@ -88,13 +65,6 @@ macro_rules! from_env_var {
self.inner = inner;
self
}
pub fn default() -> Self {
$name {
inner: $inner,
env_var: $env_var.to_string(),
allowed_values: $allowed_values,
}
}
pub fn from_env_var_or_die(env: Option<&String>) -> Self {
let mut res = Self::default();
if let Some(value) = env {

View File

@ -1,21 +1,5 @@
use serde_derive::Serialize;
use std::fmt::Display;
use std::str::FromStr;
pub trait FromStrOrDie<T: FromStr> {
fn name_and_values() -> (&'static str, String);
fn from_str_or_die(s: &String) -> T {
T::from_str(s).unwrap_or_else(|_| {
die_with_msg(&format!(
"\"{}\" is an invalid value for {}\n {} must be {}",
s,
Self::name_and_values().0,
Self::name_and_values().0,
Self::name_and_values().1,
))
})
}
}
pub fn die_with_msg(msg: impl Display) -> ! {
eprintln!("FATAL ERROR: {}", msg);