diff --git a/Cargo.lock b/Cargo.lock index e982ac4..ae9f83c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -258,7 +258,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -287,7 +287,7 @@ dependencies = [ "sha2", "sqlformat", "sqlx", - "thiserror", + "thiserror 1.0.64", "tokio", "uuid 0.4.0", ] @@ -368,7 +368,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -470,7 +470,7 @@ dependencies = [ "glib", "libc", "once_cell", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -605,7 +605,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -652,7 +652,7 @@ dependencies = [ "cool_asserts", "serde 1.0.210", "serde_json", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -712,7 +712,7 @@ version = "0.1.0" dependencies = [ "nom", "proptest", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -955,7 +955,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -984,7 +984,7 @@ dependencies = [ "serde_derive", "serde_json", "tempfile", - "thiserror", + "thiserror 1.0.64", "uuid 0.8.2", ] @@ -1130,7 +1130,7 @@ dependencies = [ "serde_json", "sha2", "tempdir", - "thiserror", + "thiserror 1.0.64", "tokio", "uuid 0.4.0", "warp", @@ -1153,7 +1153,7 @@ dependencies = [ "glib-build-tools 0.18.0", "gtk4", "libadwaita", - "thiserror", + "thiserror 1.0.64", "tokio", ] @@ -1230,7 +1230,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" dependencies = [ - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -1373,7 +1373,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -1540,7 +1540,7 @@ dependencies = [ "once_cell", "pin-project-lite", "smallvec", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -1576,7 +1576,7 @@ dependencies = [ "memchr", "once_cell", "smallvec", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -1608,7 +1608,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2120,7 +2120,7 @@ version = "0.1.0" dependencies = [ "chrono", "serde 1.0.210", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -2801,7 +2801,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -2836,7 +2836,7 @@ dependencies = [ "serde 1.0.210", "serde_json", "sgf", - "thiserror", + "thiserror 1.0.64", "uuid 0.8.2", ] @@ -3044,7 +3044,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -3084,7 +3084,7 @@ dependencies = [ "nix", "once_cell", "pipewire-sys", - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -3584,7 +3584,7 @@ dependencies = [ name = "result-extended" version = "0.1.0" dependencies = [ - "thiserror", + "thiserror 1.0.64", ] [[package]] @@ -3776,7 +3776,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -3836,7 +3836,7 @@ dependencies = [ "nary_tree", "nom", "serde 1.0.210", - "thiserror", + "thiserror 1.0.64", "typeshare", "uuid 0.8.2", ] @@ -4025,7 +4025,7 @@ dependencies = [ "sha2", "smallvec", "sqlformat", - "thiserror", + "thiserror 1.0.64", "tokio", "tokio-stream", "tracing", @@ -4107,7 +4107,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.64", "tracing", "whoami", ] @@ -4145,7 +4145,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.64", "tracing", "whoami", ] @@ -4209,9 +4209,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.79" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -4302,7 +4302,16 @@ version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.64", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", ] [[package]] @@ -4313,7 +4322,18 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -4438,7 +4458,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -4558,7 +4578,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -4600,7 +4620,7 @@ dependencies = [ "log 0.4.22", "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.64", "url 2.5.2", "utf-8", ] @@ -4654,7 +4674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a615d6c2764852a2e88a4f16e9ce1ea49bb776b5872956309e170d63a042a34f" dependencies = [ "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] @@ -4857,12 +4877,14 @@ name = "visions" version = "0.1.0" dependencies = [ "authdb", + "cool_asserts", "futures", "http 1.1.0", "mime 0.3.17", "mime_guess 2.0.5", "serde 1.0.210", "serde_json", + "thiserror 2.0.3", "tokio", "tokio-stream", "typeshare", @@ -4958,7 +4980,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -4992,7 +5014,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5263,7 +5285,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.79", + "syn 2.0.87", ] [[package]] diff --git a/visions/server/Cargo.toml b/visions/server/Cargo.toml index 4c2c69f..51dfaf0 100644 --- a/visions/server/Cargo.toml +++ b/visions/server/Cargo.toml @@ -19,3 +19,5 @@ futures = "0.3.31" tokio-stream = "0.1.16" typeshare = "1.0.4" urlencoding = "2.1.3" +cool_asserts = "2.0.3" +thiserror = "2.0.3" diff --git a/visions/server/src/asset_db.rs b/visions/server/src/asset_db.rs new file mode 100644 index 0000000..c88a4a0 --- /dev/null +++ b/visions/server/src/asset_db.rs @@ -0,0 +1,90 @@ +use std::{ + collections::HashMap, + fmt::{self, Display}, +}; + +use thiserror::Error; + +#[derive(Debug, Error)] +enum Error { + #[error("Asset could not be found: {0}")] + AssetNotFound(AssetId), +} + +#[derive(Clone, Debug, Hash, Eq, PartialEq)] +pub struct AssetId(String); + +impl Display for AssetId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "AssetId({}", self.0) + } +} + +impl From<&str> for AssetId { + fn from(s: &str) -> Self { + AssetId(s.to_owned()) + } +} + +pub trait Assets { + fn assets(&self) -> Vec; + + fn get(&self, asset_id: AssetId) -> Result, Error>; +} + +pub struct FsAssets { + assets: HashMap, +} + +impl FsAssets { + pub fn new() -> Self { + Self { + assets: HashMap::new(), + } + } + + fn assets<'a>(&'a self) -> impl Iterator { + self.assets.keys() + } +} + +impl Assets for FsAssets { + fn assets(&self) -> Vec { + self.assets.keys().cloned().collect() + } + + fn get(&self, asset_id: AssetId) -> Result, Error> { + unimplemented!() + } +} + +#[cfg(test)] +pub mod mocks { + use std::collections::HashMap; + + use super::*; + + pub struct MemoryAssets { + assets: HashMap, + } + + impl MemoryAssets { + pub fn new(data: Vec<(AssetId, String)>) -> Self { + let mut m = HashMap::new(); + data.into_iter().for_each(|(asset, path)| { + m.insert(asset, path); + }); + Self { assets: m } + } + } + + impl Assets for MemoryAssets { + fn assets(&self) -> Vec { + self.assets.keys().cloned().collect() + } + + fn get(&self, asset_id: AssetId) -> Result, Error> { + unimplemented!() + } + } +} diff --git a/visions/server/src/core.rs b/visions/server/src/core.rs index a399112..95f93ef 100644 --- a/visions/server/src/core.rs +++ b/visions/server/src/core.rs @@ -9,32 +9,42 @@ use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}; use urlencoding::decode; use uuid::Uuid; -use crate::types::{AppError, Message, Tabletop, RGB}; +use crate::{ + asset_db::{AssetId, Assets}, + types::{AppError, Message, Tabletop, RGB}, +}; #[derive(Debug)] struct WebsocketClient { sender: Option>, } -#[derive(Debug)] pub struct AppState { - pub image_base: PathBuf, - pub tabletop: Tabletop, + pub assets: Box, pub clients: HashMap, + + pub tabletop: Tabletop, } -#[derive(Clone, Debug)] -pub struct Core(pub Arc>); +#[derive(Clone)] +pub struct Core(Arc>); impl Core { - pub fn new() -> Self { + pub fn new(assetdb: A) -> Self + where + A: Assets + Sync + Send + 'static, + { Self(Arc::new(RwLock::new(AppState { - image_base: PathBuf::from("/home/savanni/Pictures"), + assets: Box::new(assetdb), + clients: HashMap::new(), tabletop: Tabletop { - background_color: RGB{ red: 0xca, green: 0xb9, blue: 0xbb }, + background_color: RGB { + red: 0xca, + green: 0xb9, + blue: 0xbb, + }, background_image: None, }, - clients: HashMap::new(), }))) } @@ -68,21 +78,29 @@ impl Core { } } - pub fn get_file(&self, file_name: String) -> Vec { + pub fn tabletop(&self) -> Tabletop { + self.0.read().unwrap().tabletop.clone() + } + + pub async fn get_file(&self, file_name: String) -> Vec { + /* let file_name = decode(&file_name).expect("UTF-8"); let mut full_path = self.0.read().unwrap().image_base.clone(); full_path.push(file_name.to_string()); - let mut content: Vec = Vec::new(); let mut file = std::fs::File::open(&full_path).unwrap(); file.read_to_end(&mut content).unwrap(); content + */ + unimplemented!() } - pub fn available_images(&self) -> Vec { - std::fs::read_dir(&self.0.read().unwrap().image_base) + pub fn available_images(&self) -> Vec { + self.0.read().unwrap().assets.assets() + /* + Ok(std::fs::read_dir(&self.0.read().unwrap().image_base) .unwrap() .filter_map(|entry| match entry { Ok(entry_) => match mime_guess::from_path(entry_.path()).first() { @@ -98,7 +116,8 @@ impl Core { }, Err(_) => None, }) - .collect() + .collect()) + */ } pub fn set_background_image(&self, path: String) -> Result<(), AppError> { @@ -121,3 +140,24 @@ impl Core { }); } } + +#[cfg(test)] +mod test { + use super::*; + use crate::asset_db::mocks::MemoryAssets; + + #[tokio::test] + async fn it_lists_available_images() { + let assets = MemoryAssets::new(vec![ + (AssetId::from("asset_1"), "asset_1".to_owned()), + (AssetId::from("asset_2"), "asset_2".to_owned()), + (AssetId::from("asset_3"), "asset_3".to_owned()), + (AssetId::from("asset_4"), "asset_4".to_owned()), + (AssetId::from("asset_5"), "asset_5".to_owned()), + ]); + let core = Core::new(assets); + + let image_paths = core.available_images(); + assert_eq!(image_paths.len(), 5); + } +} diff --git a/visions/server/src/handlers.rs b/visions/server/src/handlers.rs index 90ab6cf..de3ed83 100644 --- a/visions/server/src/handlers.rs +++ b/visions/server/src/handlers.rs @@ -33,7 +33,7 @@ pub async fn handle_auth( pub async fn handle_file(core: Core, file_name: String) -> impl Reply { let mimetype = mime_guess::from_path(&file_name).first().unwrap(); - let bytes = core.get_file(file_name); + let bytes = core.get_file(file_name).await; Response::builder() .header("application-type", mimetype.to_string()) .body(bytes) @@ -41,10 +41,13 @@ pub async fn handle_file(core: Core, file_name: String) -> impl Reply { } pub async fn handle_available_images(core: Core) -> impl Reply { + let image_paths: Vec = core.available_images().into_iter() + .map(|path| format!("{}", path)).collect(); + Response::builder() .header("Access-Control-Allow-Origin", "*") .header("Content-Type", "application/json") - .body(serde_json::to_string(&core.available_images()).unwrap()) + .body(serde_json::to_string(&image_paths).unwrap()) .unwrap() } @@ -82,7 +85,6 @@ pub async fn handle_connect_websocket( ws: warp::ws::Ws, client_id: String, ) -> impl Reply { - // println!("handle_connect_websocket: {}", client_id); ws.on_upgrade(move |socket| { let core = core.clone(); async move { @@ -90,7 +92,7 @@ pub async fn handle_connect_websocket( let mut receiver = core.connect_client(client_id.clone()); tokio::task::spawn(async move { - let tabletop = core.0.read().unwrap().tabletop.clone(); + let tabletop = core.tabletop(); let _ = ws_sender .send(Message::text( serde_json::to_string(&crate::types::Message::UpdateTabletop(tabletop)) diff --git a/visions/server/src/main.rs b/visions/server/src/main.rs index fc1e8b0..94644f0 100644 --- a/visions/server/src/main.rs +++ b/visions/server/src/main.rs @@ -1,3 +1,4 @@ +use asset_db::FsAssets; use authdb::AuthError; use handlers::{ handle_available_images, handle_connect_websocket, handle_file, handle_register_client, handle_set_background_image, handle_unregister_client, RegisterRequest @@ -13,10 +14,9 @@ use warp::{ Filter, }; +mod asset_db; mod core; - mod handlers; -// use handlers::handle_auth; mod types; @@ -96,7 +96,7 @@ async fn handle_rejection(err: warp::Rejection) -> Result