Going somewhere.

This commit is contained in:
didier amyot committed 2023-02-25 12:04:40 -05:00
1 parent 9f86d53d11
commit 0bbe716d8d
9 files changed
+158 -57

No files matched your search

+1 -1
View File
@@ -22,7 +22,7 @@
<option value="create_security">Create Security</option> <option value="create_security">Create Security</option>
</select> </select>
</div> </div>
<input type="hidden" name="initiator" value="bob le chef">
<div id="action-form"></div> <div id="action-form"></div>
</fieldset> </fieldset>
</form> </form>
+19
View File
@@ -9,4 +9,23 @@
</div> </div>
</div> </div>
<form>
<div class="uk-margin">
<label>
Username
<input class="uk-input" type="text">
</label>
</div>
<div class="uk-margin">
<label>
Password
<input class="uk-input" type="password">
</label>
</div>
<div class="uk-margin">
<button class="uk-button uk-button-default">Login</button>
</div>
</form>
{% endblock %} {% endblock %}
@@ -1,16 +1,32 @@
<div class="uk-grid-small" uk-grid>
<div class="uk-width-3-4@s">
<div class="uk-margin">
<label class="uk-form-label">Name <label class="uk-form-label">Name
<input class="uk-input" type="text" placeholder="Narnia Dollar" aria-label="Default" <input class="uk-input"
name="event.Event1.name"> type="text"
maxlength="40"
placeholder="Narnia Dollar"
aria-label="Default"
name="event.Event.name">
</label> </label>
</div> </div>
<div class="uk-width-1-4@s">
<label class="uk-form-label"> Symbol
<input class="uk-input"
type="text"
placeholder="ND"
maxlength="5"
aria-label="Default"
name="event.Event.symbol">
</label>
</div>
</div>
<div class="uk-margin"> <div class="uk-margin">
<label class="uk-form-label"> Age <label class="uk-form-label"> Type
<input class="uk-input" type="number" placeholder="Narnia Dollar" aria-label="Default" <select name="event.Event.symbol" class="uk-select">
name="event.Event1.age"> <option>Currency</option>
<option>Bond</option>
</select>
</label> </label>
</div> </div>
@@ -5,5 +5,5 @@
hx-target="#event-table-first-row" hx-target="#event-table-first-row"
hx-ext="json-enc" hx-ext="json-enc"
hx-swap="outerHTML"> hx-swap="outerHTML">
Submit ss Submit
</button> </button>
+38 -31
View File
@@ -1,7 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
use clap::parser::ValuesRef; use clap::parser::ValuesRef;
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
use log::{debug, error, info}; use log::{debug, error, info};
use rusqlite::Connection; use rusqlite::Connection;
use serde::Serialize; use serde::Serialize;
@@ -11,7 +11,7 @@ use db::securities::list_securities;
use crate::db::users::{create_user, list_users}; use crate::db::users::{create_user, list_users};
use crate::migration::apply_migrations; use crate::migration::apply_migrations;
use crate::util::{CustomResult, Error}; use crate::util::{CustomResult, Error, SQLiteConnectionFactory};
use crate::webui::web_ui_main; use crate::webui::web_ui_main;
mod db; mod db;
@@ -57,7 +57,7 @@ trait CommandLineUserLogic {
fn run_webui( fn run_webui(
&mut self, &mut self,
conn: &mut Connection, conn: SQLiteConnectionFactory,
port: u16, port: u16,
enable_template_autoreload: bool, enable_template_autoreload: bool,
) -> CustomResult<()>; ) -> CustomResult<()>;
@@ -251,14 +251,14 @@ fn process_process_events_command_line(
} }
fn process_run_wui_command_line( fn process_run_wui_command_line(
conn: &mut Connection, connFact: SQLiteConnectionFactory,
m: &ArgMatches, m: &ArgMatches,
logic: &mut dyn CommandLineUserLogic, logic: &mut dyn CommandLineUserLogic,
) -> CustomResult<()> { ) -> CustomResult<()> {
debug!("Processing run wui command line {:?}.", m); debug!("Processing run wui command line {:?}.", m);
let port = m.get_one::<u16>("port").unwrap_or(&2222); let port = m.get_one::<u16>("port").unwrap_or(&2222);
let enable_template_autoreload = m.get_flag("enable-template-autoreload"); let enable_template_autoreload = m.get_flag("enable-template-autoreload");
logic.run_webui(conn, *port, enable_template_autoreload) logic.run_webui(connFact, *port, enable_template_autoreload)
} }
fn process_securities_command_line( fn process_securities_command_line(
@@ -290,30 +290,31 @@ fn process_events_command_line(
} }
fn process_wui_command_line( fn process_wui_command_line(
conn: &mut Connection, connFact: SQLiteConnectionFactory,
m: &ArgMatches, m: &ArgMatches,
logic: &mut dyn CommandLineUserLogic, logic: &mut dyn CommandLineUserLogic,
) -> CustomResult<()> { ) -> CustomResult<()> {
debug!("Processing wui command line {:?}.", m); debug!("Processing wui command line {:?}.", m);
match m.subcommand() { match m.subcommand() {
Some(("run", run_wui_match)) => process_run_wui_command_line(conn, run_wui_match, logic), Some(("run", run_wui_match)) => process_run_wui_command_line(connFact, run_wui_match, logic),
_ => unreachable!(), _ => unreachable!(),
} }
} }
fn process_command_line( fn process_command_line(
conn: &mut Connection, connFact: SQLiteConnectionFactory,
m: &ArgMatches, m: &ArgMatches,
logic: &mut dyn CommandLineUserLogic, logic: &mut dyn CommandLineUserLogic,
) -> CustomResult<()> { ) -> CustomResult<()> {
debug!("Processing command line {:?}.", m); debug!("Processing command line {:?}.", m);
let mut conn = connFact.connection()?;
match m.subcommand() { match m.subcommand() {
Some(("users", user_match)) => process_user_command_line(conn, user_match, logic), Some(("users", user_match)) => process_user_command_line(&mut conn, user_match, logic),
Some(("securities", securities_match)) => { Some(("securities", securities_match)) => {
process_securities_command_line(conn, securities_match, logic) process_securities_command_line(&mut conn, securities_match, logic)
} }
Some(("events", events_match)) => process_events_command_line(conn, events_match, logic), Some(("events", events_match)) => process_events_command_line(&mut conn, events_match, logic),
Some(("wui", wui_match)) => process_wui_command_line(conn, wui_match, logic), Some(("wui", wui_match)) => process_wui_command_line(connFact, wui_match, logic),
_ => { _ => {
println!("Received un parsable command. {:?}", m.subcommand()); println!("Received un parsable command. {:?}", m.subcommand());
unreachable!(); unreachable!();
@@ -377,7 +378,7 @@ mod cli_test {
fn run_webui( fn run_webui(
&mut self, &mut self,
_conn: &mut Connection, _conn: SQLiteConnectionFactory,
_port: u16, _port: u16,
_enable_template_autoreload: bool, _enable_template_autoreload: bool,
) -> CustomResult<()> { ) -> CustomResult<()> {
@@ -419,7 +420,7 @@ mod cli_test {
} }
#[test] #[test]
fn create_user() { fn create_user() -> CustomResult<()> {
let cmd = command_line_parser(); let cmd = command_line_parser();
let m = cmd.get_matches_from(vec![ let m = cmd.get_matches_from(vec![
"test", "test",
@@ -432,48 +433,53 @@ mod cli_test {
]); ]);
let mut logic = TestCommandLineLogic::new(); let mut logic = TestCommandLineLogic::new();
let mut conn = Connection::open_in_memory().unwrap(); let mut conn = Connection::open_in_memory().unwrap();
process_command_line(&mut conn, &m, &mut logic); process_command_line(SQLiteConnectionFactory::inMemory(), &m, &mut logic)?;
assert_eq!(logic.create_user_count, 1); assert_eq!(logic.create_user_count, 1);
Ok(())
} }
#[test] #[test]
fn list_users() { fn list_users() -> CustomResult<()> {
let cmd = command_line_parser(); let cmd = command_line_parser();
let m = cmd.get_matches_from(vec!["test", "users", "ls"]); let m = cmd.get_matches_from(vec!["test", "users", "ls"]);
let mut logic = TestCommandLineLogic::new(); let mut logic = TestCommandLineLogic::new();
let mut conn = Connection::open_in_memory().unwrap(); let mut conn = Connection::open_in_memory().unwrap();
process_command_line(&mut conn, &m, &mut logic); process_command_line(SQLiteConnectionFactory::inMemory(), &m, &mut logic)?;
assert_eq!(logic.list_user_count, 1); assert_eq!(logic.list_user_count, 1);
Ok(())
} }
#[test] #[test]
fn list_securities() { fn list_securities() -> CustomResult<()> {
let cmd = command_line_parser(); let cmd = command_line_parser();
let m = cmd.get_matches_from(vec!["test", "securities", "ls"]); let m = cmd.get_matches_from(vec!["test", "securities", "ls"]);
let mut logic = TestCommandLineLogic::new(); let mut logic = TestCommandLineLogic::new();
let mut conn = Connection::open_in_memory().unwrap(); let mut conn = Connection::open_in_memory().unwrap();
process_command_line(&mut conn, &m, &mut logic); process_command_line(SQLiteConnectionFactory::inMemory(), &m, &mut logic)?;
assert_eq!(logic.list_securities_count, 1); assert_eq!(logic.list_securities_count, 1);
Ok(())
} }
#[test] #[test]
fn process_events() { fn process_events() -> CustomResult<()> {
let cmd = command_line_parser(); let cmd = command_line_parser();
let m = cmd.get_matches_from(vec!["test", "events", "process", "/dev/null"]); let m = cmd.get_matches_from(vec!["test", "events", "process", "/dev/null"]);
let mut logic = TestCommandLineLogic::new(); let mut logic = TestCommandLineLogic::new();
let mut conn = Connection::open_in_memory().unwrap(); let mut conn = Connection::open_in_memory().unwrap();
process_command_line(&mut conn, &m, &mut logic); process_command_line(SQLiteConnectionFactory::inMemory(), &m, &mut logic)?;
assert_eq!(logic.process_events_count, 1); assert_eq!(logic.process_events_count, 1);
Ok(())
} }
#[test] #[test]
fn run_web_ui() { fn run_web_ui() -> CustomResult<()> {
let cmd = command_line_parser(); let cmd = command_line_parser();
let m = cmd.get_matches_from(vec!["test", "wui", "run"]); let m = cmd.get_matches_from(vec!["test", "wui", "run"]);
let mut logic = TestCommandLineLogic::new(); let mut logic = TestCommandLineLogic::new();
let mut conn = Connection::open_in_memory().unwrap(); let mut conn = Connection::open_in_memory().unwrap();
process_command_line(&mut conn, &m, &mut logic); process_command_line(SQLiteConnectionFactory::inMemory(), &m, &mut logic)?;
assert_eq!(logic.run_webui_count, 1); assert_eq!(logic.run_webui_count, 1);
Ok(())
} }
} }
@@ -522,19 +528,20 @@ impl CommandLineUserLogic for ProCommandLineLogic {
fn run_webui( fn run_webui(
&mut self, &mut self,
conn: &mut Connection, connFact: SQLiteConnectionFactory,
port: u16, port: u16,
enable_template_autoreload: bool, enable_template_autoreload: bool,
) -> CustomResult<()> { ) -> CustomResult<()> {
info!("Serving web-ui on port {:?}", port); info!("Serving web-ui on port {:?}", port);
web_ui_main(conn, port, enable_template_autoreload) web_ui_main(connFact, port, enable_template_autoreload)
} }
} }
fn get_connection(m: &ArgMatches) -> Connection { fn get_connection(m: &ArgMatches) -> SQLiteConnectionFactory {
let path = get_db_path(&m); let path = get_db_path(&m);
info!("Sqlite database location is {:?}.", path); info!("Sqlite database location is {:?}.", path);
return Connection::open(path).unwrap(); SQLiteConnectionFactory::new(path.clone())
} }
fn set_logger(m: &ArgMatches) { fn set_logger(m: &ArgMatches) {
@@ -544,14 +551,14 @@ fn set_logger(m: &ArgMatches) {
.unwrap(); .unwrap();
} }
fn main() { fn main() -> CustomResult<()> {
let command = command_line_parser(); let command = command_line_parser();
let m = command.get_matches(); let m = command.get_matches();
set_logger(&m); set_logger(&m);
let mut conn = get_connection(&m); let connFactory = get_connection(&m);
apply_migrations(&mut conn).unwrap(); apply_migrations(&mut connFactory.connection()?).unwrap();
let mut logic = ProCommandLineLogic::new(); let mut logic = ProCommandLineLogic::new();
process_command_line(&mut conn, &m, &mut logic); process_command_line(connFactory, &m, &mut logic)
} }
+61 -7
View File
@@ -1,4 +1,5 @@
use std::io; use std::io;
use std::path::PathBuf;
use rusqlite::{Connection, OpenFlags}; use rusqlite::{Connection, OpenFlags};
@@ -17,20 +18,73 @@ pub enum Error {
pub type CustomResult<T, E = Error> = Result<T, E>; pub type CustomResult<T, E = Error> = Result<T, E>;
#[derive(Clone)]
struct PathSQLiteConnectionCreator {
path: PathBuf,
}
impl PathSQLiteConnectionCreator {
fn connection(&self) -> CustomResult<Connection> {
Connection::open(&self.path).map_err(Error::SqlError)
}
fn readonly(&self) -> CustomResult<Connection> {
let flags = OpenFlags::SQLITE_OPEN_READ_ONLY;
Connection::open_with_flags(&self.path, flags).map_err(Error::SqlError)
}
}
#[derive(Clone)]
struct EphemeralSQLiteConnectionCreator {}
// todo: fix this horrible implementation. All connections
// must share the same data.
impl EphemeralSQLiteConnectionCreator {
fn connection(&self) -> CustomResult<Connection> {
Connection::open_in_memory().map_err(Error::SqlError)
}
fn readonly(&self) -> CustomResult<Connection> {
let flags = OpenFlags::SQLITE_OPEN_READ_ONLY;
Connection::open_in_memory_with_flags(flags).map_err(Error::SqlError)
}
}
#[derive(Clone)]
enum SQLiteConnectionCreator {
Path(PathSQLiteConnectionCreator),
Ephemeral(EphemeralSQLiteConnectionCreator),
}
#[derive(Clone)]
pub struct SQLiteConnectionFactory { pub struct SQLiteConnectionFactory {
path: String, creator: SQLiteConnectionCreator,
} }
impl SQLiteConnectionFactory { impl SQLiteConnectionFactory {
pub fn new(path: String) -> SQLiteConnectionFactory { pub fn new(path: PathBuf) -> SQLiteConnectionFactory {
SQLiteConnectionFactory { path } SQLiteConnectionFactory {
creator: SQLiteConnectionCreator::Path(PathSQLiteConnectionCreator { path })
}
} }
fn connection(&self) -> Connection { pub fn inMemory() -> SQLiteConnectionFactory {
Connection::open(&self.path).unwrap() SQLiteConnectionFactory {
creator: SQLiteConnectionCreator::Ephemeral(EphemeralSQLiteConnectionCreator {})
}
} }
fn readonly(&self) -> Connection { pub fn connection(&self) -> CustomResult<Connection> {
Connection::open_with_flags(&self.path, OpenFlags::SQLITE_OPEN_READ_ONLY).unwrap() match & self.creator {
SQLiteConnectionCreator::Path(c) => c.connection(),
SQLiteConnectionCreator::Ephemeral(c) => c.connection(),
}
}
pub fn readonly(&self) -> CustomResult<Connection> {
match & self.creator {
SQLiteConnectionCreator::Path(c) => c.readonly(),
SQLiteConnectionCreator::Ephemeral(c) => c.readonly(),
}
} }
} }
+7 -5
View File
@@ -18,7 +18,7 @@ use minijinja_autoreload::AutoReloader;
use rusqlite::Connection; use rusqlite::Connection;
use crate::events::Event; use crate::events::Event;
use crate::util::{CustomResult, Error}; use crate::util::{CustomResult, Error, SQLiteConnectionFactory};
use crate::webui::worker::{bob, event_processor_loop}; use crate::webui::worker::{bob, event_processor_loop};
mod views; mod views;
@@ -83,20 +83,22 @@ fn create_auto_reloader(enable_template_autoreload: bool) -> AutoReloader {
} }
#[tokio::main] #[tokio::main]
pub async fn web_ui_main(conn: &mut Connection, pub async fn web_ui_main(connFact: SQLiteConnectionFactory,
port: u16, port: u16,
enable_template_autoreload: bool) -> CustomResult<()> { enable_template_autoreload: bool) -> CustomResult<()> {
// The closure is invoked every time the environment is outdated to recreate it. // The closure is invoked every time the environment is outdated to recreate it.
let tmpl_reloader = create_auto_reloader(enable_template_autoreload); let tmpl_reloader = create_auto_reloader(enable_template_autoreload);
let (s, r) = async_channel::unbounded::<Event>(); let (event_sender, event_receiver) = async_channel::unbounded::<Event>();
let tmpl_reloader = web::Data::new(tmpl_reloader); let tmpl_reloader = web::Data::new(tmpl_reloader);
let clonedConnFact = connFact.clone();
let server = HttpServer::new(move || { let server = HttpServer::new(move || {
App::new() App::new()
.app_data(tmpl_reloader.clone()) .app_data(tmpl_reloader.clone())
.app_data(web::Data::new(s.clone())) .app_data(web::Data::new(event_sender.clone()))
.app_data(web::Data::new(clonedConnFact.clone()))
.service( .service(
actix_files::Files::new("/static", "./resources/static/").use_last_modified(true), actix_files::Files::new("/static", "./resources/static/").use_last_modified(true),
) )
@@ -114,7 +116,7 @@ pub async fn web_ui_main(conn: &mut Connection,
.unwrap(); .unwrap();
let t1 = server.run().fuse(); let t1 = server.run().fuse();
let t2 = event_processor_loop(conn, r).fuse(); let t2 = event_processor_loop(connFact, event_receiver).fuse();
let t3 = bob().fuse(); let t3 = bob().fuse();
pin_mut!(t1, t2, t3); pin_mut!(t1, t2, t3);
+2
View File
@@ -7,6 +7,7 @@ use minijinja::context;
use serde::Deserialize; use serde::Deserialize;
use crate::events::Event; use crate::events::Event;
use crate::util::SQLiteConnectionFactory;
use crate::webui::MiniJinjaRenderer; use crate::webui::MiniJinjaRenderer;
#[actix_web::get("/")] #[actix_web::get("/")]
@@ -41,6 +42,7 @@ pub async fn actions_partials(
_req: HttpRequest, _req: HttpRequest,
tmpl_env: MiniJinjaRenderer, tmpl_env: MiniJinjaRenderer,
query: web::Query<HashMap<String, String>>, query: web::Query<HashMap<String, String>>,
connFactory: web::Data<SQLiteConnectionFactory>,
) -> actix_web::Result<impl Responder> { ) -> actix_web::Result<impl Responder> {
let event_type = query let event_type = query
.get("event_type") .get("event_type")
+5 -4
View File
@@ -6,17 +6,18 @@ use tokio::time::sleep;
use crate::event_processor::process_event; use crate::event_processor::process_event;
use crate::events::Event; use crate::events::Event;
use crate::util::CustomResult; use crate::util::{CustomResult, SQLiteConnectionFactory};
use crate::util::Error::FuckingError; use crate::util::Error::FuckingError;
pub async fn event_processor_loop(conn: &mut Connection, // todo, all the "?" and handle the exceptions for real
pub async fn event_processor_loop(conFact: SQLiteConnectionFactory,
r: async_channel::Receiver<Event>) -> CustomResult<()> { r: async_channel::Receiver<Event>) -> CustomResult<()> {
loop { loop {
let res = r.recv().await; let res = r.recv().await;
let mut conn = conFact.connection()?;
match res { match res {
Ok(event) => process_event(conn, event)?, // todo, remove "?" Ok(event) => process_event(&mut conn, event)?,
Err(e) => { Err(e) => {
println!("Error: {:?}", e);
return Err(FuckingError(e.to_string())); return Err(FuckingError(e.to_string()));
} }
} }