Before deleting redundnat macros

This commit is contained in:
Daniel Sockwell 2019-10-07 22:39:52 -04:00
parent 0a82bbe11b
commit 74a2ac3bb2
5 changed files with 170 additions and 141 deletions

View File

@ -1,13 +1,13 @@
use crate::{config::deployment_cfg_types::*, err::FromStrOrDie, maybe_update};
use std::{collections::HashMap, time::Duration};
use std::collections::HashMap;
#[derive(Debug)]
pub struct DeploymentConfig<'a> {
pub env: Env,
pub log_level: LogLevel,
pub address: FlodgattAddr,
pub port: u16,
pub unix_socket: Option<Socket>,
pub port: Port2,
pub unix_socket: Socket,
pub cors: Cors<'a>,
pub sse_interval: SseInterval,
pub ws_interval: WsInterval,
@ -16,40 +16,42 @@ impl Default for DeploymentConfig<'_> {
fn default() -> Self {
Self {
env: Env::Development,
log_level: LogLevel::Warn,
address: FlodgattAddr::from_str_or_die(&"127.0.0.1".to_string()),
port: 4000,
unix_socket: None,
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(Duration::from_millis(100)),
ws_interval: WsInterval(Duration::from_millis(100)),
sse_interval: SseInterval::default(),
ws_interval: WsInterval::default(),
}
}
}
impl DeploymentConfig<'_> {
pub fn from_env(env_vars: HashMap<String, String>) -> Self {
Self::default()
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))
.maybe_update_address(env_vars.get("BIND").map(FlodgattAddr::from_str_or_die))
.maybe_update_port(env_vars.get("PORT").map(u16::from_str_or_die))
.maybe_update_unix_socket(env_vars.get("SOCKET").map(Socket::from_str_or_die))
.maybe_update_log_level(env_vars.get("RUST_LOG").map(LogLevel::from_str_or_die))
.maybe_update_sse_interval(env_vars.get("SSE_FREQ").map(SseInterval::from_str_or_die))
.maybe_update_ws_interval(env_vars.get("WS_FREQ").map(WsInterval::from_str_or_die))
.log()
.maybe_update_env(env_vars.get("RUST_ENV").map(Env::from_str_or_die));
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"));
res.unix_socket = Socket::from_env_var_or_die(env_vars.get("SOCKET"));
res.sse_interval = SseInterval::from_env_var_or_die(env_vars.get("SSE_FREQ"));
res.ws_interval = WsInterval::from_env_var_or_die(env_vars.get("WS_FREQ"));
res.log()
}
maybe_update!(maybe_update_env; env: Env);
maybe_update!(maybe_update_port; port: u16);
// 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_interval; sse_interval: SseInterval);
maybe_update!(maybe_update_ws_interval; ws_interval: WsInterval);
// 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);

View File

@ -1,140 +1,80 @@
use crate::err::FromStrOrDie;
use std::{
fmt, net::IpAddr, ops::Deref, os::unix::net::UnixListener, str::FromStr, time::Duration,
};
use crate::{derive_from_str_or_die, err::FromStrOrDie, 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
#[derive(EnumString, EnumVariantNames, Debug)]
#[strum(serialize_all = "snake_case")]
pub enum Env {
Production,
Development,
}
impl FromStrOrDie<Self> for Env {
fn name_and_values() -> (&'static str, String) {
("RUST_ENV", format!("one of: {:?}", Self::variants()))
}
}
derive_from_str_or_die!(Env {
name: "RUST_ENV",
value: format!("one of: {:?}", Self::variants())
});
pub struct FlodgattAddr(pub IpAddr);
impl Deref for FlodgattAddr {
type Target = IpAddr;
fn deref(&self) -> &IpAddr {
&self.0
}
}
impl FromStr for FlodgattAddr {
type Err = std::net::AddrParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(|num| Self(num))
}
}
impl FromStrOrDie<Self> for FlodgattAddr {
fn name_and_values() -> (&'static str, String) {
("BIND", "a valid address (e.g., 127.0.0.1)".to_string())
}
}
impl fmt::Debug for FlodgattAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let addr = match self.0 {
IpAddr::V4(addr) => addr.to_string(),
IpAddr::V6(addr) => addr.to_string(),
};
write!(f, "{}", addr)
}
// 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
#[derive(EnumString, EnumVariantNames, Debug)]
#[strum(serialize_all = "snake_case")]
pub enum LogLevel {
pub enum LogLevelInner {
Trace,
Debug,
Info,
Warn,
Error,
}
impl FromStrOrDie<Self> for LogLevel {
fn name_and_values() -> (&'static str, String) {
("RUST_LOG", format!("one of: {:?}", Self::variants()))
}
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()));
#[derive(Debug)]
pub struct Socket(pub UnixListener);
impl Deref for Socket {
type Target = UnixListener;
fn deref(&self) -> &UnixListener {
&self.0
}
}
impl FromStr for Socket {
type Err = std::io::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
UnixListener::bind(s).map(|socket| (Self(socket)))
}
}
impl FromStrOrDie<Self> for Socket {
fn name_and_values() -> (&'static str, String) {
("SOCKET", "a valid Unix Socket".to_string())
}
// 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
pub struct WsInterval(pub Duration);
impl Deref for WsInterval {
type Target = Duration;
fn deref(&self) -> &Duration {
&self.0
}
}
impl FromStr for WsInterval {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(|num| Self(Duration::from_millis(num)))
}
}
impl FromStrOrDie<Self> for WsInterval {
fn name_and_values() -> (&'static str, String) {
("WS_FREQ", "a number of milliseconds".to_string())
}
}
impl fmt::Debug for WsInterval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.0)
}
// 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
pub struct SseInterval(pub Duration);
impl Deref for SseInterval {
type Target = Duration;
fn deref(&self) -> &Duration {
&self.0
}
}
impl FromStr for SseInterval {
type Err = std::num::ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(|num| Self(Duration::from_millis(num)))
}
}
impl FromStrOrDie<Self> for SseInterval {
fn name_and_values() -> (&'static str, String) {
("SSE_FREQ", "a number of milliseconds".to_string())
}
}
impl fmt::Debug for SseInterval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.0)
}
// 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()));
impl FromStrOrDie<Self> for u16 {
fn name_and_values() -> (&'static str, String) {
("PORT", "a number".to_string())
}
// 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()));
/// Permissions for Cross Origin Resource Sharing (CORS)
pub struct Cors<'a> {
pub allowed_headers: Vec<&'a str>,
pub allowed_methods: Vec<&'a str>,

View File

@ -1,6 +1,3 @@
//! Configuration defaults. All settings with the prefix of `DEFAULT_` can be overridden
//! by an environmental variable of the same name without that prefix (either by setting
//! the variable at runtime or in the `.env` file)
mod deployment_cfg;
mod deployment_cfg_types;
mod postgres_cfg;
@ -9,11 +6,6 @@ pub use self::{
deployment_cfg::DeploymentConfig, postgres_cfg::PostgresConfig, redis_cfg::RedisConfig,
};
// **NOTE**: Polling Redis is much more time consuming than polling the `Receiver`
// (on the order of 10ms rather than 50μs). Thus, changing this setting
// would be a good place to start for performance improvements at the cost
// of delaying all updates.
#[macro_export]
macro_rules! maybe_update {
($name:ident; $item: tt:$type:ty) => (
@ -30,3 +22,94 @@ 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 {
inner: $inner:expr; $type:ty,
env_var: $env_var:tt,
allowed_values: $allowed_values:expr,
}
inner_from_str(|$arg:ident| $body:expr)
) => {
pub struct $name {
pub inner: $type,
pub env_var: String,
pub allowed_values: String,
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.inner)
}
}
impl std::ops::Deref for $name {
type Target = $type;
fn deref(&self) -> &$type {
&self.inner
}
}
impl $name {
fn inner_from_str($arg: &str) -> Option<$type> {
$body
}
fn update_inner(&mut self, inner: $type) -> &Self {
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 {
res.update_inner(Self::inner_from_str(value).unwrap_or_else(|| {
eprintln!(
"\"{}\" is not a valid value for {}. {} must be {}",
value, res.env_var, res.env_var, res.allowed_values
);
std::process::exit(1);
}));
res
} else {
res
}
}
}
};
}

View File

@ -14,6 +14,10 @@ pub struct RedisConfig {
pub host: String,
pub db: Option<String>,
pub namespace: Option<String>,
// **NOTE**: Polling Redis is much more time consuming than polling the `Receiver`
// (on the order of 1ms rather than 50μs). Thus, changing this setting
// would be a good place to start for performance improvements at the cost
// of delaying all updates.
pub polling_interval: Duration,
}
impl Default for RedisConfig {

View File

@ -79,9 +79,9 @@ fn main() {
.allow_methods(cfg.cors.allowed_methods)
.allow_headers(cfg.cors.allowed_headers);
let server_addr = net::SocketAddr::new(*cfg.address, cfg.port);
let server_addr = net::SocketAddr::new(*cfg.address, cfg.port.inner);
if let Some(_socket) = cfg.unix_socket {
if let Some(_socket) = cfg.unix_socket.inner.as_ref() {
dbg_and_die!("Unix socket support not yet implemented");
} else {
warp::serve(websocket_routes.or(sse_routes).with(cors)).run(server_addr);