Enable configuration files to be used

next
Sayan Nandan 4 years ago
parent 340eaaebcb
commit b06dedc8bd
No known key found for this signature in database
GPG Key ID: C31EFD7DDA12AEE0

@ -49,8 +49,8 @@ impl fmt::Display for DataGroup {
if rc.len() == 1 {
if let Some(rcode) = RespCodes::from_str(&rc, None) {
match rcode {
RespCodes::Okay => terminal::write_okay("(Okay) ")?,
RespCodes::NotFound => terminal::write_okay("(Nil) ")?,
RespCodes::Okay => terminal::write_info("(Okay) ")?,
RespCodes::NotFound => terminal::write_info("(Nil) ")?,
RespCodes::OverwriteError => {
terminal::write_error("(Overwrite Error) ")?
}

@ -38,7 +38,7 @@ pub mod terminal {
}
Ok(())
}
pub fn write_okay<T: fmt::Display>(item: T) -> fmt::Result {
pub fn write_info<T: fmt::Display>(item: T) -> fmt::Result {
write_with_col(item, Some(Color::Cyan))
}
pub fn write_warning<T: fmt::Display>(item: T) -> fmt::Result {

@ -22,11 +22,12 @@
name: TerrabaseDB Server
version: 0.4.0
author: Sayan N. <ohsayan@outlook.com>
about: This is the TerrabaseDB Database server
about: The TerrabaseDB Database server
args:
- config:
short: c
required: false
long: withconfig
value_name: configfile
value_name: cfgfile
help: Use a configuration file to start tdb
takes_value: true
takes_value: true

@ -23,25 +23,97 @@
use libtdb::TResult;
use serde::Deserialize;
use std::fs;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use tokio::net::ToSocketAddrs;
use toml;
/// Gets a `toml` file from `WORKSPACEROOT/examples/config-files`
#[cfg(test)]
fn get_toml_from_examples_dir(filename: String) -> TResult<String> {
use std::path;
let curdir = std::env::current_dir().unwrap();
let workspaceroot = curdir.ancestors().nth(1).unwrap();
let mut fileloc = path::PathBuf::from(workspaceroot);
fileloc.push("examples");
fileloc.push("config-files");
fileloc.push(filename);
Ok(fs::read_to_string(fileloc)?)
}
#[derive(Deserialize, Debug, PartialEq)]
struct Config {
pub struct Config {
server: ServerConfig,
}
#[derive(Deserialize, Debug, PartialEq)]
struct ServerConfig {
pub struct ServerConfig {
host: IpAddr,
port: u16,
noart: Option<bool>,
}
impl Config {
pub fn new(file: String) -> TResult<Self> {
let res: Config = toml::from_str(&file)?;
Ok(res)
#[derive(Debug, PartialEq)]
pub struct ParsedConfig {
host: IpAddr,
port: u16,
noart: bool,
}
impl ParsedConfig {
/// Create a new `ParsedConfig` from a given file in `location`
pub fn new_from_file(location: String) -> TResult<Self> {
let file = fs::read_to_string(location)?;
Ok(ParsedConfig::from_config(toml::from_str(&file)?))
}
fn from_config(cfg: Config) -> Self {
ParsedConfig {
host: cfg.server.host,
port: cfg.server.port,
noart: if let Some(noart) = cfg.server.noart {
noart
} else {
false
},
}
}
pub fn new_from_toml_str(tomlstr: String) -> TResult<Self> {
Ok(ParsedConfig::from_config(toml::from_str(&tomlstr)?))
}
/// Create a new `ParsedConfig` with the default `host` and `noart` settngs
/// and a supplied `port`
pub fn default_with_port(port: u16) -> Self {
ParsedConfig {
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
port,
noart: false,
}
}
/// Create a new `ParsedConfig` with the default `port` and `noart` settngs
/// and a supplied `host`
pub fn default_with_host(host: IpAddr) -> Self {
ParsedConfig::new(host, 2003, false)
}
/// Create a new `ParsedConfig` with all the fields
pub fn new(host: IpAddr, port: u16, noart: bool) -> Self {
ParsedConfig { host, port, noart }
}
/// Create a default `ParsedConfig` with:
/// - `host`: 127.0.0.1
/// - `port` : 2003
/// - `noart` : false
pub fn default() -> Self {
ParsedConfig {
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
port: 2003,
noart: false,
}
}
pub fn get_host_port_tuple(self) -> impl ToSocketAddrs {
((self.host), self.port)
}
pub fn is_artful(&self) -> bool {
!self.noart
}
}
@ -54,15 +126,13 @@ fn test_config_toml_okayport() {
port = 2003
"#
.to_owned();
let cfg = Config::new(file).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
Config {
server: ServerConfig {
port: 2003,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
noart: None,
}
ParsedConfig {
port: 2003,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
noart: false,
}
);
}
@ -75,44 +145,49 @@ fn test_config_toml_badport() {
port = 20033002
"#
.to_owned();
let cfg = Config::new(file);
let cfg = ParsedConfig::new_from_file(file);
assert!(cfg.is_err());
}
#[test]
#[cfg(test)]
fn test_config_file_ok() {
let fileloc = "../examples/config-files/tdb.toml";
let file = std::fs::read_to_string(fileloc).unwrap();
let cfg: Config = Config::new(file).unwrap();
assert_eq!(
cfg,
Config {
server: ServerConfig {
port: 2003,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
noart: None,
}
}
);
let file = get_toml_from_examples_dir("tdb.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(cfg, ParsedConfig::default());
}
#[test]
#[cfg(test)]
fn test_config_file_err() {
let fileloc = "../examples/config-files/badcfg.toml";
let file = std::fs::read_to_string(fileloc).unwrap();
let cfg = Config::new(file);
let file = get_toml_from_examples_dir("tdb.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_file(file);
assert!(cfg.is_err());
}
use clap::{load_yaml, App};
/// Get the command line arguments
pub fn get_config_file() -> Option<String> {
pub enum ConfigType<T> {
Def(T),
Custom(T),
}
/// # Return a `ConfigType<ParsedConfig>`
///
/// This parses a configuration file if it is supplied as a command line argument
/// or it returns the default configuration. **If** the configuration file
/// contains an error, then this returns it as an `Err` variant
pub fn get_config_file_or_return_cfg() -> TResult<ConfigType<ParsedConfig>> {
let cfg_layout = load_yaml!("../cli.yml");
let matches = App::from_yaml(cfg_layout).get_matches();
let filename = matches.value_of("config");
filename.map(|fname| fname.to_owned())
if let Some(filename) = filename {
match ParsedConfig::new_from_file(filename.to_owned()) {
Ok(cfg) => return Ok(ConfigType::Custom(cfg)),
Err(e) => return Err(e),
}
} else {
Ok(ConfigType::Def(ParsedConfig::default()))
}
}
#[test]
@ -123,33 +198,21 @@ fn test_args() {
let matches = App::from_yaml(cfg_layout).get_matches_from(cmdlineargs);
let filename = matches.value_of("config").unwrap();
assert_eq!("../examples/config-files/tdb.toml", filename);
let cfg = Config::new(std::fs::read_to_string(filename).unwrap()).unwrap();
assert_eq!(
cfg,
Config {
server: ServerConfig {
port: 2003,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
noart: None,
}
}
);
let cfg = ParsedConfig::new_from_toml_str(std::fs::read_to_string(filename).unwrap()).unwrap();
assert_eq!(cfg, ParsedConfig::default());
}
#[test]
#[cfg(test)]
fn test_config_file_noart() {
let fileloc = "../examples/config-files/secure-noart.toml";
let file = std::fs::read_to_string(fileloc).unwrap();
let cfg: Config = Config::new(file).unwrap();
let file = get_toml_from_examples_dir("secure-noart.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
Config {
server: ServerConfig {
port: 2003,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
noart: Some(true),
}
ParsedConfig {
port: 2003,
host: IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
noart: true,
}
);
}
@ -157,17 +220,14 @@ fn test_config_file_noart() {
#[test]
#[cfg(test)]
fn test_config_file_ipv6() {
let fileloc = "../examples/config-files/ipv6.toml";
let file = std::fs::read_to_string(fileloc).unwrap();
let cfg: Config = Config::new(file).unwrap();
let file = get_toml_from_examples_dir("ipv6.toml".to_owned()).unwrap();
let cfg = ParsedConfig::new_from_toml_str(file).unwrap();
assert_eq!(
cfg,
Config {
server: ServerConfig {
port: 2003,
host: IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)),
noart: None,
}
ParsedConfig {
port: 2003,
host: IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)),
noart: false,
}
);
}

@ -219,5 +219,5 @@ pub async fn run(listener: TcpListener, sig: impl Future) {
drop(signal);
drop(terminate_tx);
let _ = terminate_rx.recv().await;
terminal::write_okay("Goodbye :)\n").unwrap();
terminal::write_info("Goodbye :)\n").unwrap();
}

@ -33,8 +33,7 @@ use dbnet::run;
use tokio::signal;
#[cfg(test)]
mod tests;
static ADDR: &'static str = "127.0.0.1:2003";
static MSG: &'static str = "TerrabaseDB v0.4.0 | https://github.com/terrabasedb/terrabase\nServer running on terrapipe://127.0.0.1:2003";
static MSG: &'static str = "TerrabaseDB v0.4.0 | https://github.com/terrabasedb/terrabase";
static TEXT: &'static str = "
_______ _ _____ ____
|__ __| | | | __ \\ | _ \\
@ -49,9 +48,32 @@ static TEXT: &'static str = "
";
#[tokio::main]
async fn main() {
let listener = TcpListener::bind(ADDR).await.unwrap();
println!("{}\n{}", TEXT, MSG);
// Start the server which asynchronously waits for a CTRL+C signal
// which will safely shut down the server
run(listener, signal::ctrl_c()).await;
run(check_args_or_connect().await, signal::ctrl_c()).await;
}
use libtdb::util::terminal;
async fn check_args_or_connect() -> TcpListener {
let cfg = config::get_config_file_or_return_cfg();
match cfg {
Ok(config::ConfigType::Custom(cfg)) => {
if cfg.is_artful() {
println!("{}\n{}", TEXT, MSG);
}
terminal::write_info("info: Using settings from config file\n").unwrap();
TcpListener::bind(cfg.get_host_port_tuple()).await.unwrap()
}
Ok(config::ConfigType::Def(cfg)) => {
println!("{}\n{}", TEXT, MSG);
terminal::write_info("info: No configuration file supplied. Using default settings\n")
.unwrap();
TcpListener::bind(cfg.get_host_port_tuple()).await.unwrap()
}
Err(e) => {
terminal::write_error(e).unwrap();
std::process::exit(0x100);
}
}
}

@ -22,7 +22,6 @@
//! This module contains automated tests for queries
use crate::dbnet;
use crate::ADDR;
use std::io::ErrorKind;
use std::net::{Shutdown, SocketAddr};
use std::thread;
@ -30,6 +29,8 @@ use std::time::Duration;
use tokio::net::{TcpListener, TcpStream};
use tokio::prelude::*;
static ADDR: &'static str = "127.0.0.1:2003";
/// Start the server as a background asynchronous task
async fn start_server() -> Option<SocketAddr> {
// HACK(@ohsayan): Since we want to start the server if it is not already

Loading…
Cancel
Save