advotracker: restructure project using crate tree

* advotracker: the framework crate
* crate/advotrackerdb: crate implementing the database backend
* crate/advotrackerd: the backend daemon
* crate/adovtracker: the application (CLI and GUI)

Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
This commit is contained in:
2021-03-07 18:38:50 +01:00
parent 1ce1d029e5
commit 4c88167bef
185 changed files with 2542983 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
name: advotrackerd
version: "1.0"
author: Networkx GmbH <info@networkx.de>
about: capture relevant data encountered during an online legal advice
after_help: in Zusammenarbeit mit HIEDEMANN Rechtsanwälte
args:
- config:
short: c
long: config
value_name: FILE
help: Sets a custom config file
takes_value: true
- dbdriver:
short: D
long: dbdriver
help: Driver used to connect to database
possible_values:
- mysql
- postgres
- sqlite
takes_value: true
- verbose:
short: v
long: verbose
multiple: true
help: Sets the level of verbosity
subcommands:
- config:
about: configure the program environment
version: "0.1.0"
author: Ralf Zerres <ralf.zerres@networkx.de>
value_name: "FILE"
default_value: advotracker.conf
- test:
about: run testing features
version: "0.1.0"
author: Ralf Zerres <ralf.zerres@networkx.de>
args:
- debug:
short: d
help: print debug information

View File

@@ -0,0 +1,327 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2019 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
// module: db
#![warn(clippy::pedantic)]
#![warn(clippy::nursery)]
//#[cfg(test)]
use diesel::debug_query;
use diesel::dsl::*;
use diesel::prelude::*;
//use diesel::sql_query;
use diesel::sqlite::Sqlite;
use diesel::sqlite::SqliteConnection;
use dotenv::dotenv;
//use serde_derive;
use serde_json;
use std::env;
use std::error::Error;
// modules
pub mod models;
pub mod schema;
pub mod types;
//use crate::db::models::Users;
use crate::db::models::Harm;
use models::User;
use models::UserRole;
use schema::*;
// functions
pub fn establish_connection() -> SqliteConnection {
dotenv().ok();
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
SqliteConnection::establish(&database_url)
.unwrap_or_else(|_| panic!("Error connecting to {}", &database_url))
/*
* WIP: integrate tracing!
trace!(
target: "diesel",
type: "Sqlite3",
status: "connected"
);
*/
}
/*
* base functions
pub fn users_insert_default_values(conn: &SqliteConnection) -> QueryResult<usize> {
//use schema::users::dsl::*;
insert_into(users).default_values().execute(conn)
}
#[test]
fn examine_sql_users_insert_default_values() {
use schema::users::dsl::*;
let query = insert_into(users).default_values();
let sql = "INSERT INTO `users` DEFAULT VALUES -- binds: []";
assert_eq!(sql, debug_query::<Sqlite, _>(&query).to_string());
}
pub fn user_roles_insert_default_values(conn: &SqliteConnection) -> QueryResult<usize> {
use schema::user_roles::dsl::*;
insert_into(user_roles).default_values().execute(conn)
}
#[test]
fn examine_sql_user_roles_insert_default_values() {
use schema::user_roles::dsl::*;
let query = insert_into(user_roles).default_values();
let sql = "INSERT INTO `user_roles` DEFAULT VALUES -- binds: []";
assert_eq!(sql, debug_query::<Sqlite, _>(&query).to_string());
}
pub fn user_harms_insert_default_values(conn: &SqliteConnection) -> QueryResult<usize> {
use schema::harms::dsl::*;
insert_into(harms).default_values().execute(conn)
}
#[test]
fn examine_sql_harms_insert_default_values() {
use schema::harms::dsl::*;
let query = insert_into(harms).default_values();
let sql = "INSERT INTO `harms` DEFAULT VALUES -- binds: []";
assert_eq!(sql, debug_query::<Sqlite, _>(&query).to_string());
}
pub fn users_insert_single_column(conn: &SqliteConnection) -> QueryResult<usize> {
use schema::users::dsl::*;
insert_into(users)
.values(alias.eq("Hiedemann"))
.execute(conn)
}
#[test]
fn examine_sql_users_insert_single_column() {
use schema::users::dsl::*;
let query = insert_into(users).values(alias.eq("Hiedemann"));
let sql = "INSERT INTO `users` (`alias`) VALUES (?) \
-- binds: [\"Hiedemann\"]";
assert_eq!(sql, debug_query::<Sqlite, _>(&query).to_string());
}
pub fn users_insert_multiple_columns(conn: &SqliteConnection) -> QueryResult<usize> {
use schema::users::dsl::*;
insert_into(users)
.values((
first_name.eq("Lothar"),
last_name.eq("Schlömer"),
alias.eq("102"),
))
.execute(conn)
}
#[test]
fn examine_sql_user_insert_multiple_columns() {
use models::Users;
//use crate::db::models::Users;
use crate::db::schema::users::dsl::*;
let query = insert_into(users).values((
first_name.eq("Lothar"),
last_name.eq("Schlömer"),
alias.eq("102"),
));
let sql = "INSERT INTO `users` (`first_name`, `last_name`, `alias`) \
VALUES (?, ?, ?) \
-- binds: [\"Lothar\", \"Schlömer\", \"102\"]";
assert_eq!(sql, debug_query::<Sqlite, _>(&query).to_string());
}
pub fn user_insert_struct_via_json(conn: &SqliteConnection) -> Result<(), Box<dyn Error>> {
use models::insert::NewUser;
use schema::users::dsl::*;
let json = r#"{ "alias": "Daniela", "first_name": "Daniela","last_name": "Knocke" }"#;
let user_struct = serde_json::from_str::<NewUser>(json)?;
insert_into(users).values(&user_struct).execute(conn)?;
Ok(())
}
*/
/*
#[test]
fn examine_sql_user_insert_struct_via_json() {
use models::NewUser;
use schema::users::dsl::*;
let json = r#"{ "last_name": "Knocke", "first_name": "Daniela", "alias": "Sekretariat" }"#;
let user_form = serde_json::from_str::<NewUser>(json).unwrap();
let query = insert_into(users).values(&user_form);
let sql = "INSERT INTO `users` (`last_name`, `first_name`, `alias`) \
VALUES (?, ?, ?) \
-- binds: [\"Knocke\", \"Daniela\", \"Sekretariat\"]";
assert_eq!(sql, debug_query::<Sqlite, _>(&query).to_string());
}
*/
/*
pub fn user_insert_struct_json_option(conn: &SqliteConnection) -> Result<(), Box<dyn Error>> {
use models::NewUser;
use schema::users::dsl::*;
let json = r#"{ "alias": "Networkx", "email_confirmed": true,
"email": "support@networkx.de", "first_name": null, "last_name": null }"#; let user_form =
serde_json::from_str::<NewUser>(json)?;
insert_into(users).values(&user_form).execute(conn)?;
Ok(())
}
*/
/*
#[test]
fn examine_sql_user_insert_struct_json_option() {
use schema::users::dsl::*;
use models::NewUser;
let json = r#"{ "alias": "Networkx", "email_confirmed": true, "email": "support@networkx.de", "first_name": null, "last_name": null }"#;
let user_form = serde_json::from_str::<NewUser>(json).unwrap();
let query = insert_into(users).values(&user_form);
let sql = "INSERT INTO `users` (`alias`, `email`, `email_confirmed`) \
VALUES (?, ?, ?) \
-- binds: [\"Networkx\", \"support@networkx.de\", \"$true\"]";
assert_eq!(sql, debug_query::<Sqlite, _>(&query).to_string());
}
*/
// WIP
/*
#[test]
fn examine_sql_users_insert_get_results() {
use diesel::result::Error;
let conn = establish_connection();
conn.test_transaction::<_, Error, _>(|| {
use diesel::select;
use chrono::NaiveDateTime;
use schema::users::dsl::*;
//use schema::users::*;
let now = select(diesel::dsl::now).get_result::<NaiveDateTime>(&conn)?;
let inserted_users = conn.transaction::<_, Error, _>(|| {
let inserted_count = insert_into(users)
.values(&vec![
(id.eq(1), last_name.eq("Hiedemann"), first_name.eq("Eric")),
(id.eq(2), last_name.eq("Schlömer"), first_name.eq("Lothar")),
//(id.eq(1), first_name.eq("Eric"), last_name.eq("Hiedemann"), alias.eq("Eric.Hiedemann")),
//(id.eq(2), first_name.eq("Lothar"), last_name.eq("Schlömer"), alias.eq("othar.Schlömer")),
])
.execute(&conn)?;
/*
Ok(users
.order(id.desc())
.limit(inserted_count as i64)
.load(&conn)?
.into_iter()
.rev()
.collect::<Vec<_>>())
*/
})?;
/*
let expected_users = vec![
User {
id: 1,
last_name: "Hiedemann".into(),
first_name: "Eric".into(),
alias: "Eric.Hiedemann".into(),
email: None,
email_confirmed: false,
password_hash: None,
initials: None,
date_created: now,
date_updated: now,
},
User {
id: 2,
last_name: "Schlömer".into(),
first_name: "Lothar".into(),
alias: "Lothar.Schlömer".into(),
email: None,
email_confirmed: false,
password_hash: None,
initials: None,
date_created: now,
date_updated: now,
},
];
assert_eq!(expected_users, inserted_users);
*/
Ok(())
});
}
*/
/*
pub fn create_numberharm<'a>(connection: &SqliteConnection, NumberHarm: &'a str) {
let numberHarm = models::NewNumberHarm { NumberHarm };
diesel::insert_into(schema::NumberHarm::table)
.values(&numberHarm)
.execute(connection)
.expect("Error inserting new task");
}
*/
/*
pub fn create_userid<'a>(connection: &SqliteConnection, User: &'a str) {
let user = models::NewUser { User };
diesel::insert_into(schema::User::table)
.values(&user)
.execute(connection)
.expect("Error inserting new task");
}
*/
/*
pub fn create_userclaimid<'a>(connection: &SqliteConnection, UserClaim: &'a str) {
let userClaim = models::NewUserClaim { UserClaim };
diesel::insert_into(schema::UserClaim::table)
.values(&userClaim)
.execute(connection)
.expect("Error inserting new task");
}
pub fn create_userrole<'a>(connection: &SqliteConnection, UserRole: &'a str) {
let userRole = models::NewUserRole { UserRole };
diesel::insert_into(schema::UserRole::table)
.values(&userRole)
.execute(connection)
.expect("Error inserting new task");
}
pub fn create_useruserrole<'a>(connection: &SqliteConnection, UserUserRole: &'a str) {
let userUserRole = models::NewUserUserRole { UserUserRole };
diesel::insert_into(schema::UserUserRole::table)
.values(&userUserRole)
.execute(connection)
.expect("Error inserting new task");
}
*/

View File

@@ -0,0 +1,43 @@
/*
* advotracker - Hotline tackingtool for Advocats
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: 0BSD, MIT
*/
#![warn(missing_docs, rust_2018_idioms, rust_2018_compatibility)]
//! advotrackerd: crate documentation
// This function adds two integers (given as arguments) and returns the sum.
//
// # Examples
//
// ```
// assert_eq!(8, advotrackerd::internal_adder(4, 4));
// ```
fn internal_adder(a: i32, b: i32) -> i32 {
a + b
}
/// This function adds two to its argument.
///
/// # Examples
///
/// ```
/// assert_eq!(4, advotrackerd::add_two(2));
/// ```
pub fn add_two(a: i32) -> i32 {
internal_adder(a, 2)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn internal_test() {
assert_eq!(8, internal_adder(4, 4));
}
}

View File

@@ -0,0 +1,213 @@
/* advotracker infrastructure.
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
// Rust nightly supports procedural macros!
//#![feature(proc_macro)]
//#![deny(rust_2018_idioms)]
/// advotrackerd: the main binary
//use log::info; //substituted with tracing
use dotenv::dotenv;
use locales::t;
use serde::Deserialize;
//use std::{env, process};
use std::env;
//use tracing::{debug, info, instrument, span, Level};
//use tracing::{debug, info, instrument, Level, Metadata, span::{Id, Attributes, Record}, trace};
//use tracing::{debug, instrument, span::*, trace, Level};
use tracing::{debug, trace, Level};
//use diesel::prelude::*;
//use diesel::sqlite::SqliteConnection;
mod parse_args;
#[derive(Deserialize, Debug)]
struct Environment {
test_lang: String,
}
/*
pub struct AdvoTrackerSubscriber;
impl tracing::Subscriber for AdvoTrackerSubscriber{
fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(0) }
fn record(&self, _: &Id, _: &Record) {}
fn event(&self, _: &tracing::Event) {}
fn record_follows_from(&self, _: &Id, _: &Id) {}
fn enabled(&self, _: &Metadata) -> bool { false }
fn enter(&self, _: &Id) {}
fn exit(&self, _: &Id) {}
}
impl AdvoTrackerSubscriber {
fn new() -> Self { AdvoTrackerSubscriber }
}
#[instrument]
fn run_subcommand(matches: &ArgMatches) -> Result<(), String> {
//info!("inside run_subcommand");
match matches.subcommand() {
("config", Some(cmd)) => run_config(cmd),
("test", Some(cmd)) => run_test(cmd),
_ => Ok(()),
}
}
fn run_config(matches: &ArgMatches) -> Result<(), String> {
//info!("inside run_config");
let _input = matches.value_of("FILE").unwrap();
Ok(())
}
fn run_test(matches: &ArgMatches) -> Result<(), String> {
//info!("inside run_test");
if let Some(matches) = matches.subcommand_matches("test") {
if matches.is_present("debug") {
println!("test: Printing debug info...");
} else {
println!("test: Printing normally...");
}
}
Ok(())
}
*/
/// `advotrackerd` is the daemon that keeps track of legal mandate data
/// that are backed up in a database.
fn main() -> Result<(), Box<dyn std::error::Error>> {
use parse_args::parse_args;
use tracing_subscriber::fmt;
use viperus::Viperus;
//let advotracker_subscriber = AdvoTrackerSubscriber::new();
//tracing::subscriber::set_global_default(advotracker_subscriber)
// .expect("setting tracing default failed");
// initialize the tracing subsystem
// a drop in replacement for classical logging
// reference: https://tokio.rs/blog/2019-08-tracing/
let span = tracing::span!(Level::TRACE, "advotracker_main");
let _enter = span.enter();
let subscriber = fmt::Subscriber::builder()
.with_env_filter("advotracker=trace")
.finish();
// initialize logger
// TODO: exchange with tracing!
//env_logger::init();
//info!("Commencing the proxy!");
tracing::subscriber::with_default(subscriber, || {
// get system environment
let mut lang = env::var("LANG").unwrap_or("en".to_string());
let mut res = t!("parse.environment", lang);
let mut state = t!("state.started", lang);
trace!(target: "advotracker", message = ?res, state = ?state);
//debug!(message = ?res, state = ?state);
trace!(target: "advotracker", environment = "system", lang = ?lang);
// get testing environment (.env)
dotenv().ok();
match envy::from_env::<Environment>() {
Ok(environment) => {
if environment.test_lang != lang { lang = environment.test_lang; }
},
Err(e) => { debug!(target: "advotracker", "{}", e); }
}
res = t!("parse.environment", lang);
trace!(target: "advotracker", environment = "envy", lang = ?lang);
state = t!("state.finished", lang);
trace!(target: "advotracker", message = ?res, state = ?state);
//debug!(message = ?res, state = ?state);
// initialize viperus structure
let mut v = Viperus::new();
// parse commandline arguments
res = t!("parse.arguments", lang);
state = t!("state.started", lang);
trace!(target: "advotracker", process = ?res, state = ?state);
//info!(target: "advotracker", "{}", res);
//debug!(message = ?res, state = ?state);
//info!(target: "advotracker", "parsing commandline args");
let _ = parse_args(&mut v);
state = t!("state.finished", lang);
trace!(target: "advotracker", process = ?res, state = ?state);
//trace!(target: "Viperus", "Config results: {:?}", v);
/*
if verbose > 1 {
for (key, value) in env::vars() {
println!("{}={}", key, value);
}
}
*/
//state = t!("state.finished", lang);
//trace!(target: "advotracker", process = ?res, state = ?state);
//debug!(message = ?res, state = ?state);
/*
// handle subcommands
if let Err(e) = run_subcommand(&matches) {
println!("Subcommand error: {}", e);
process::exit(1);
}
*/
// Starting the program logic
res = t!("main.started", lang);
state = t!("state.started", lang);
//info!(target: "advotracker", "{}", res);
trace!(target: "advotracker", process = ?res, state = ?state);
//use advotracker_backend::*;
//use advotracker_backend::schema::users::dsl::*;
/*
let connection = establish_connection();
trace!(
target: "advotracker",
process = "Sqlite3",
status = "connected",
);
*/
/*
//User::table.load(&connection);
//user::belonging_to(users).load(&connection);
use advotracker_backend::models::*;
let results = users
//.filter(published.eq(true))
.limit(5)
.load::<User>(&connection)
.expect("Error loading users");
println!("Displaying {} users", results.len());
for user in results {
println!("{}", user.user_id);
println!("----------\n");
println!("{}", user.first_name);
println!("{}", user.last_name);
println!("{}", user.alias);
println!("{}", user.email);
}
*/
state = t!("state.finished", lang);
res = t!("main.finished", lang);
//info!(target: "advotracker", "{}", res);
trace!(target: "advotracker", process = ?res, state = ?state);
});
Ok(())
}

View File

@@ -0,0 +1,145 @@
/* advotracker infrastructure.
*
* Copyright 2020 Ralf Zerres <ralf.zerres@networkx.de>
* SPDX-License-Identifier: (0BSD or MIT)
*/
use viperus::Viperus;
/// The given Viperus structure will be muted according to the
/// processed default, environment and commandline arguments
pub fn parse_args(v: &mut Viperus) -> Result<(), Box<dyn std::error::Error>> {
//use log::{debug, info, trace, warn};
//use log::trace;
use tracing::{trace, Level};
use std::env;
if cfg!(feature = "yaml") {
trace!(target:"feature", "Clap feature 'yaml' enabled.");
println!("Using feature yaml.");
}
if cfg!(feature = "fmt-clap") {
trace!(target: "Viperus", "Viperus feature 'fmt-clap' enabled.");
println!("Using feature fmt-clap");
}
// preset default key/value pairs (lowest priority)
v.add_default("config_file", String::from("advotrackerd.yml"));
v.add_default("db_driver", String::from("sqlite"));
v.add_default("verbose", 0);
//if cfg!(feature = "fmt-clap") {
println!("With fmt-clap...");
// parse CLI commandline arguments with clap
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
// CLI arguments are defined inline
let matches = App::new("advotrackerd")
.name(crate_name!())
.version(crate_version!())
.author(crate_authors!())
.about(crate_description!())
.after_help("in Zusammenarbeit mit Hiedemann Rechtsanwälte <info@hiedemann.de>")
.template(
"\
{bin} v{version}
{about}
{all-args}
(C) 2020 {author}
{after_help}",
)
.arg(
Arg::with_name("configFile")
.short("c")
.long("configFile")
.value_name("FILE")
.help("Sets a custom config file")
.takes_value(true),
)
.arg(
Arg::with_name("dbdriver")
.short("d")
.long("dbdriver")
.value_name("DatabaseDriver")
.help("Driver used to connect to database")
.possible_values(&["mysql", "postgres", "sqlite"])
.takes_value(true),
)
.arg(
Arg::with_name("verbose")
.short("v")
.long("verbose")
.help("Sets verbosity level")
.multiple(true),
)
.get_matches();
//}
if matches.occurrences_of("verbose") > 0 {
// clap is using i64, viperus i32
let n = matches.occurrences_of("verbose") as i32;
v.add("verbose", n);
}
//}
// preset the prefix for relevant environment variables ("ADVOTRACKERD_")
//if cfg!(feature = "fmt-clap") {
let mut env_prefix: String = crate_name!().to_uppercase();
env_prefix.push_str("_");
v.set_env_prefix(&env_prefix);
//}
// respect dotenv environment (e.g for testing)
// -> overwrites the preset default values
println!(
"RUST_LOG={}",
dotenv::var("RUST_LOG").unwrap_or_else(|_| String::from("None"))
);
//v.load_file(".env", v.Format::ENV).unwrap();
// enable caching and automatic update of environment values
if cfg!(feature = "fmt-cache") {
v.cache(true);
}
if cfg!(feature = "fmt-env") {
v.automatic_env(true);
}
//if cfg!(feature = "fmt-clap") {
// load user selected call arguments
// -> overwrites values given via environment variables
v.load_clap(matches)?;
// bond the clap names to camel_case rust variable names
v.bond_clap("configFile", "config_file");
v.bond_clap("dbdriver", "db_driver");
v.bond_clap("verbose", "verbose");
//}
trace!("verbose {:?}", v.get::<i32>("verbose").unwrap());
if v.get::<i32>("verbose").unwrap() > 0 {
println!(
"config_file: {:?}",
v.get::<String>("config_file").unwrap_or_default()
);
println!(
"db_driver: {:?}",
v.get::<String>("db_driver").unwrap_or_default()
);
println!(
"verbosity level: {:?}",
v.get::<i32>("verbose").unwrap_or_default()
);
}
if v.get::<i32>("verbose").unwrap() > 1 {
println!("\nEnvironment:");
for (key, value) in env::vars() {
println!("{}={}", key, value);
}
}
Ok(())
}