Files
escarmouche/src/main.rs
T
2022-12-13 09:29:27 -05:00

325 lines
9.6 KiB
Rust

use std::path::PathBuf;
use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
use log::{debug, info};
use rusqlite::Connection;
use serde::Serialize;
use simple_logger::SimpleLogger;
use crate::migration::apply_migrations;
use crate::securities::list_securities;
mod migration;
mod users;
mod securities;
mod account;
#[derive(Debug, Serialize)]
struct Output<T> {
status: String,
result: T,
}
impl<T> Output<T> {
fn success(shit: T) -> Output<T> {
return Output { status: "success".to_string(), result: shit };
}
}
trait CommandLineUserLogic {
fn create_user(&mut self, conn: &mut Connection, name: &String, public_key: &String);
fn list_user(&mut self, conn: &Connection);
fn list_securities(&mut self, conn: &Connection);
}
fn verbosity_arg() -> Arg {
return Arg::new("verbosity")
.short('v')
.action(ArgAction::Count)
.default_value("0");
}
fn get_verbosity(m: &ArgMatches) -> log::LevelFilter {
match m.get_one::<u8>("verbosity") {
None => log::LevelFilter::Error,
Some(1) => log::LevelFilter::Warn,
Some(2) => log::LevelFilter::Info,
Some(3) => log::LevelFilter::Debug,
Some(4) => log::LevelFilter::Trace,
_ => log::LevelFilter::Error,
}
}
fn db_path_arg() -> Arg {
return Arg::new("db-path")
.long("db-path")
.value_name("FILE")
.value_parser(value_parser!(PathBuf))
.default_value( "ledger.sqlite");
}
fn get_db_path(m: & ArgMatches) -> & PathBuf{
return m.get_one::<PathBuf>("db-path").unwrap();
}
fn new_user_command_line_parser() -> Command {
return Command::new("new")
.long_flag("new")
.about("Create new user.")
.arg(Arg::new("name").long("name").required(true).help("name of new user.").action(ArgAction::Set))
.arg(Arg::new("public_key").required(true).long("public-key").help("public key of users.").action(ArgAction::Set));
}
fn ls_user_command_line_parser() -> Command {
return Command::new("ls")
.long_flag("ls")
.about("List all users.");
}
fn user_command_line_parser() -> Command {
return Command::new("users")
.subcommand(new_user_command_line_parser())
.subcommand(ls_user_command_line_parser());
}
fn ls_securities_command_line_parser() -> Command {
return Command::new("ls")
.long_flag("ls")
.about("List all securities.");
}
fn securities_command_line_parser() -> Command {
return Command::new("securities")
.subcommand(ls_securities_command_line_parser());
}
fn command_line_parser() -> Command {
return Command::new("prog")
.arg(verbosity_arg())
.arg(db_path_arg())
.subcommand(user_command_line_parser())
.subcommand(securities_command_line_parser());
}
fn process_new_user_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
debug!("Processing new user command line {:?}.", m);
let name = m.get_one::<String>("name").unwrap();
let pk = m.get_one::<String>("public_key").unwrap();
logic.create_user(conn, name, pk);
}
fn process_list_user_command_line(conn: &Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
debug!("Processing list users command line {:?}.", m);
logic.list_user(conn);
}
fn process_user_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
debug!("Processing user command line {:?}.", m);
match m.subcommand() {
Some(("new", new_user_match)) => {
process_new_user_command_line(conn, new_user_match, logic);
}
Some(("ls", list_user_match)) => {
process_list_user_command_line(conn, list_user_match, logic);
}
_ => unreachable!(),
}
}
fn process_list_securities_command_line(conn: &Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
debug!("Processing list securities command line {:?}.", m);
logic.list_securities(conn);
}
fn process_securities_command_line(conn: &Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
debug!("Processing securities command line {:?}.", m);
match m.subcommand() {
Some(("ls", list_securities_match)) => {
process_list_securities_command_line(conn, list_securities_match, logic);
}
_ => unreachable!(),
}
}
fn process_command_line(conn: &mut Connection, m: &ArgMatches, logic: &mut dyn CommandLineUserLogic) {
debug!("Processing command line {:?}.", m);
match m.subcommand() {
Some(("users", user_match)) => {
process_user_command_line(conn, user_match, logic);
}
Some(("securities", securities_match)) => {
process_securities_command_line(conn, securities_match, logic);
}
_ => {
println!("Received unparsable command. {:?}", m.subcommand());
unreachable!();
},
}
}
#[cfg(test)]
mod test {
use super::*;
struct TestCommandLineLogic {
list_user_count: usize,
create_user_count: usize,
list_securities_count: usize,
}
impl TestCommandLineLogic {
fn new() -> TestCommandLineLogic {
return TestCommandLineLogic {
list_user_count: 0,
create_user_count: 0,
list_securities_count: 0,
};
}
}
impl CommandLineUserLogic for TestCommandLineLogic {
fn create_user(&mut self, _conn: &mut Connection, _name: &String, _public_key: &String) {
self.create_user_count += 1;
}
fn list_user(&mut self, _conn: &Connection) {
self.list_user_count += 1;
}
fn list_securities(&mut self, _conn: &Connection) {
self.list_securities_count += 1;
}
}
#[test]
fn verbosity() {
{
let cmd = Command::new("test").arg(verbosity_arg());
let m = cmd.get_matches_from(vec!["test", "-vv"]);
assert_eq!(get_verbosity(&m), log::LevelFilter::Info);
}
{
let cmd = Command::new("test").arg(verbosity_arg());
let m = cmd.get_matches_from(vec!["test"]);
assert_eq!(get_verbosity(&m), log::LevelFilter::Error);
}
}
#[test]
fn db_path() {
{
let cmd = Command::new("test").arg(db_path_arg());
let m = cmd.get_matches_from(vec!["test", "--db-path", "/foo/bar"]);
let expt = PathBuf::from("/foo/bar");
assert_eq!(get_db_path(&m), &expt);
}
{
let cmd = Command::new("test").arg(db_path_arg());
let m = cmd.get_matches_from(vec!["test"]);
let expt = PathBuf::from("ledger.sqlite");
assert_eq!(get_db_path(&m), &expt);
}
}
#[test]
fn create_user() {
let cmd = command_line_parser();
let m = cmd.get_matches_from(vec!["test", "users", "new", "--name", "paul", "--public-key", "123abc"]);
let mut logic = TestCommandLineLogic::new();
let mut conn = Connection::open_in_memory().unwrap();
process_command_line(&mut conn, &m, &mut logic);
assert_eq!(logic.create_user_count, 1);
}
#[test]
fn list_users() {
let cmd = command_line_parser();
let m = cmd.get_matches_from(vec!["test", "users", "ls"]);
let mut logic = TestCommandLineLogic::new();
let mut conn = Connection::open_in_memory().unwrap();
process_command_line(&mut conn, &m, &mut logic);
assert_eq!(logic.list_user_count, 1);
}
#[test]
fn list_securities() {
let cmd = command_line_parser();
let m = cmd.get_matches_from(vec!["test", "securities", "ls"]);
let mut logic = TestCommandLineLogic::new();
let mut conn = Connection::open_in_memory().unwrap();
process_command_line(&mut conn, &m, &mut logic);
assert_eq!(logic.list_securities_count, 1);
}
}
struct ProCommandLineLogic {}
impl ProCommandLineLogic {
fn new() -> ProCommandLineLogic {
return ProCommandLineLogic {};
}
}
impl CommandLineUserLogic for ProCommandLineLogic {
fn create_user(&mut self, conn: &mut Connection, name: &String, public_key: &String) {
let tx = conn.transaction().unwrap();
users::create_user(& tx, name, public_key);
tx.commit().unwrap();
}
fn list_user(&mut self, conn: &Connection) {
let users = users::list_users(conn);
match users {
Ok(u) => {
let out = Output::success(u);
println!("{}", serde_json::to_string(&out).unwrap());
}
Err(e) => {
println!("{:?}", e);
}
}
}
fn list_securities(&mut self, conn: & Connection) {
let securities = list_securities(conn);
match securities {
Ok(s) => {
let out = Output::success(s);
println!("{}", serde_json::to_string(&out).unwrap());
},
Err(e) => {
println!("{:?}", e);
}
};
}
}
fn get_connection(m: &ArgMatches) -> Connection {
let path = get_db_path(&m);
info!("Sqlite database location is {:?}.", path);
return Connection::open(path).unwrap();
}
fn set_logger(m: &ArgMatches) {
SimpleLogger::new().with_level(get_verbosity(&m)).init().unwrap();
}
fn main() {
let command = command_line_parser();
let m = command.get_matches();
set_logger(&m);
let mut conn = get_connection(&m);
apply_migrations(&mut conn).unwrap();
let mut logic = ProCommandLineLogic::new();
process_command_line(&mut conn, &m, &mut logic);
}