diff --git a/resources/templates/actions.html b/resources/templates/actions.html
index 6f52ddb..73bd315 100644
--- a/resources/templates/actions.html
+++ b/resources/templates/actions.html
@@ -22,7 +22,7 @@
-
+
-
+
-
diff --git a/resources/templates/partials/actions/submit_button.html b/resources/templates/partials/actions/submit_button.html
index 8c671c2..5926571 100644
--- a/resources/templates/partials/actions/submit_button.html
+++ b/resources/templates/partials/actions/submit_button.html
@@ -5,5 +5,5 @@
hx-target="#event-table-first-row"
hx-ext="json-enc"
hx-swap="outerHTML">
- Submit ss
+ Submit
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
index 857fc6e..b656ec2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,7 +1,7 @@
use std::path::PathBuf;
+use clap::{Arg, ArgAction, ArgMatches, Command, value_parser};
use clap::parser::ValuesRef;
-use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
use log::{debug, error, info};
use rusqlite::Connection;
use serde::Serialize;
@@ -11,7 +11,7 @@ use db::securities::list_securities;
use crate::db::users::{create_user, list_users};
use crate::migration::apply_migrations;
-use crate::util::{CustomResult, Error};
+use crate::util::{CustomResult, Error, SQLiteConnectionFactory};
use crate::webui::web_ui_main;
mod db;
@@ -57,7 +57,7 @@ trait CommandLineUserLogic {
fn run_webui(
&mut self,
- conn: &mut Connection,
+ conn: SQLiteConnectionFactory,
port: u16,
enable_template_autoreload: bool,
) -> CustomResult<()>;
@@ -251,14 +251,14 @@ fn process_process_events_command_line(
}
fn process_run_wui_command_line(
- conn: &mut Connection,
+ connFact: SQLiteConnectionFactory,
m: &ArgMatches,
logic: &mut dyn CommandLineUserLogic,
) -> CustomResult<()> {
debug!("Processing run wui command line {:?}.", m);
let port = m.get_one::
("port").unwrap_or(&2222);
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(
@@ -290,30 +290,31 @@ fn process_events_command_line(
}
fn process_wui_command_line(
- conn: &mut Connection,
+ connFact: SQLiteConnectionFactory,
m: &ArgMatches,
logic: &mut dyn CommandLineUserLogic,
) -> CustomResult<()> {
debug!("Processing wui command line {:?}.", m);
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!(),
}
}
fn process_command_line(
- conn: &mut Connection,
+ connFact: SQLiteConnectionFactory,
m: &ArgMatches,
logic: &mut dyn CommandLineUserLogic,
) -> CustomResult<()> {
debug!("Processing command line {:?}.", m);
+ let mut conn = connFact.connection()?;
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)) => {
- 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(("wui", wui_match)) => process_wui_command_line(conn, wui_match, logic),
+ Some(("events", events_match)) => process_events_command_line(&mut conn, events_match, logic),
+ Some(("wui", wui_match)) => process_wui_command_line(connFact, wui_match, logic),
_ => {
println!("Received un parsable command. {:?}", m.subcommand());
unreachable!();
@@ -377,7 +378,7 @@ mod cli_test {
fn run_webui(
&mut self,
- _conn: &mut Connection,
+ _conn: SQLiteConnectionFactory,
_port: u16,
_enable_template_autoreload: bool,
) -> CustomResult<()> {
@@ -419,7 +420,7 @@ mod cli_test {
}
#[test]
- fn create_user() {
+ fn create_user() -> CustomResult<()> {
let cmd = command_line_parser();
let m = cmd.get_matches_from(vec![
"test",
@@ -432,48 +433,53 @@ mod cli_test {
]);
let mut logic = TestCommandLineLogic::new();
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);
+ Ok(())
}
#[test]
- fn list_users() {
+ fn list_users() -> CustomResult<()> {
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);
+ process_command_line(SQLiteConnectionFactory::inMemory(), &m, &mut logic)?;
assert_eq!(logic.list_user_count, 1);
+ Ok(())
}
#[test]
- fn list_securities() {
+ fn list_securities() -> CustomResult<()> {
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);
+ process_command_line(SQLiteConnectionFactory::inMemory(), &m, &mut logic)?;
assert_eq!(logic.list_securities_count, 1);
+ Ok(())
}
#[test]
- fn process_events() {
+ fn process_events() -> CustomResult<()> {
let cmd = command_line_parser();
let m = cmd.get_matches_from(vec!["test", "events", "process", "/dev/null"]);
let mut logic = TestCommandLineLogic::new();
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);
+ Ok(())
}
#[test]
- fn run_web_ui() {
+ fn run_web_ui() -> CustomResult<()> {
let cmd = command_line_parser();
let m = cmd.get_matches_from(vec!["test", "wui", "run"]);
let mut logic = TestCommandLineLogic::new();
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);
+ Ok(())
}
}
@@ -522,19 +528,20 @@ impl CommandLineUserLogic for ProCommandLineLogic {
fn run_webui(
&mut self,
- conn: &mut Connection,
+ connFact: SQLiteConnectionFactory,
port: u16,
enable_template_autoreload: bool,
) -> CustomResult<()> {
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);
+
info!("Sqlite database location is {:?}.", path);
- return Connection::open(path).unwrap();
+ SQLiteConnectionFactory::new(path.clone())
}
fn set_logger(m: &ArgMatches) {
@@ -544,14 +551,14 @@ fn set_logger(m: &ArgMatches) {
.unwrap();
}
-fn main() {
+fn main() -> CustomResult<()> {
let command = command_line_parser();
let m = command.get_matches();
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();
- process_command_line(&mut conn, &m, &mut logic);
+ process_command_line(connFactory, &m, &mut logic)
}
diff --git a/src/util.rs b/src/util.rs
index 346140b..924125e 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,4 +1,5 @@
use std::io;
+use std::path::PathBuf;
use rusqlite::{Connection, OpenFlags};
@@ -17,20 +18,73 @@ pub enum Error {
pub type CustomResult = Result;
+#[derive(Clone)]
+struct PathSQLiteConnectionCreator {
+ path: PathBuf,
+}
+
+impl PathSQLiteConnectionCreator {
+ fn connection(&self) -> CustomResult {
+ Connection::open(&self.path).map_err(Error::SqlError)
+ }
+
+ fn readonly(&self) -> CustomResult {
+ 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::open_in_memory().map_err(Error::SqlError)
+ }
+
+ fn readonly(&self) -> CustomResult {
+ 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 {
- path: String,
+ creator: SQLiteConnectionCreator,
}
impl SQLiteConnectionFactory {
- pub fn new(path: String) -> SQLiteConnectionFactory {
- SQLiteConnectionFactory { path }
+ pub fn new(path: PathBuf) -> SQLiteConnectionFactory {
+ SQLiteConnectionFactory {
+ creator: SQLiteConnectionCreator::Path(PathSQLiteConnectionCreator { path })
+ }
}
- fn connection(&self) -> Connection {
- Connection::open(&self.path).unwrap()
+ pub fn inMemory() -> SQLiteConnectionFactory {
+ SQLiteConnectionFactory {
+ creator: SQLiteConnectionCreator::Ephemeral(EphemeralSQLiteConnectionCreator {})
+ }
}
- fn readonly(&self) -> Connection {
- Connection::open_with_flags(&self.path, OpenFlags::SQLITE_OPEN_READ_ONLY).unwrap()
+ pub fn connection(&self) -> CustomResult {
+ match & self.creator {
+ SQLiteConnectionCreator::Path(c) => c.connection(),
+ SQLiteConnectionCreator::Ephemeral(c) => c.connection(),
+ }
+ }
+
+ pub fn readonly(&self) -> CustomResult {
+ match & self.creator {
+ SQLiteConnectionCreator::Path(c) => c.readonly(),
+ SQLiteConnectionCreator::Ephemeral(c) => c.readonly(),
+ }
}
}
diff --git a/src/webui/mod.rs b/src/webui/mod.rs
index 2ef4780..86b60a9 100644
--- a/src/webui/mod.rs
+++ b/src/webui/mod.rs
@@ -18,7 +18,7 @@ use minijinja_autoreload::AutoReloader;
use rusqlite::Connection;
use crate::events::Event;
-use crate::util::{CustomResult, Error};
+use crate::util::{CustomResult, Error, SQLiteConnectionFactory};
use crate::webui::worker::{bob, event_processor_loop};
mod views;
@@ -83,20 +83,22 @@ fn create_auto_reloader(enable_template_autoreload: bool) -> AutoReloader {
}
#[tokio::main]
-pub async fn web_ui_main(conn: &mut Connection,
+pub async fn web_ui_main(connFact: SQLiteConnectionFactory,
port: u16,
enable_template_autoreload: bool) -> CustomResult<()> {
// The closure is invoked every time the environment is outdated to recreate it.
let tmpl_reloader = create_auto_reloader(enable_template_autoreload);
- let (s, r) = async_channel::unbounded::();
+ let (event_sender, event_receiver) = async_channel::unbounded::();
let tmpl_reloader = web::Data::new(tmpl_reloader);
+ let clonedConnFact = connFact.clone();
let server = HttpServer::new(move || {
App::new()
.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(
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();
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();
pin_mut!(t1, t2, t3);
diff --git a/src/webui/views.rs b/src/webui/views.rs
index 36d683a..3d274dc 100644
--- a/src/webui/views.rs
+++ b/src/webui/views.rs
@@ -7,6 +7,7 @@ use minijinja::context;
use serde::Deserialize;
use crate::events::Event;
+use crate::util::SQLiteConnectionFactory;
use crate::webui::MiniJinjaRenderer;
#[actix_web::get("/")]
@@ -41,6 +42,7 @@ pub async fn actions_partials(
_req: HttpRequest,
tmpl_env: MiniJinjaRenderer,
query: web::Query>,
+ connFactory: web::Data,
) -> actix_web::Result {
let event_type = query
.get("event_type")
diff --git a/src/webui/worker.rs b/src/webui/worker.rs
index 68357e4..36c9b9c 100644
--- a/src/webui/worker.rs
+++ b/src/webui/worker.rs
@@ -6,17 +6,18 @@ use tokio::time::sleep;
use crate::event_processor::process_event;
use crate::events::Event;
-use crate::util::CustomResult;
+use crate::util::{CustomResult, SQLiteConnectionFactory};
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) -> CustomResult<()> {
loop {
let res = r.recv().await;
+ let mut conn = conFact.connection()?;
match res {
- Ok(event) => process_event(conn, event)?, // todo, remove "?"
+ Ok(event) => process_event(&mut conn, event)?,
Err(e) => {
- println!("Error: {:?}", e);
return Err(FuckingError(e.to_string()));
}
}