Compare commits

...

2 Commits

Author SHA1 Message Date
Savanni D'Gerinel 5535632466 available_images now only lists image files from the asset database 2024-11-21 09:08:36 -05:00
Savanni D'Gerinel 5d66558180 Set up a test to validate the function which gets available images
There's a lot of work here that sets up dependency injection traits
which will make it easier for me to keep writing tests and will make it
easier for me to separate the Core from the support infrastructure.
2024-11-20 09:52:26 -05:00
7 changed files with 238 additions and 75 deletions

98
Cargo.lock generated
View File

@ -258,7 +258,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -287,7 +287,7 @@ dependencies = [
"sha2", "sha2",
"sqlformat", "sqlformat",
"sqlx", "sqlx",
"thiserror", "thiserror 1.0.64",
"tokio", "tokio",
"uuid 0.4.0", "uuid 0.4.0",
] ]
@ -368,7 +368,7 @@ dependencies = [
"regex", "regex",
"rustc-hash", "rustc-hash",
"shlex", "shlex",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -470,7 +470,7 @@ dependencies = [
"glib", "glib",
"libc", "libc",
"once_cell", "once_cell",
"thiserror", "thiserror 1.0.64",
] ]
[[package]] [[package]]
@ -605,7 +605,7 @@ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -652,7 +652,7 @@ dependencies = [
"cool_asserts", "cool_asserts",
"serde 1.0.210", "serde 1.0.210",
"serde_json", "serde_json",
"thiserror", "thiserror 1.0.64",
] ]
[[package]] [[package]]
@ -712,7 +712,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"nom", "nom",
"proptest", "proptest",
"thiserror", "thiserror 1.0.64",
] ]
[[package]] [[package]]
@ -955,7 +955,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -984,7 +984,7 @@ dependencies = [
"serde_derive", "serde_derive",
"serde_json", "serde_json",
"tempfile", "tempfile",
"thiserror", "thiserror 1.0.64",
"uuid 0.8.2", "uuid 0.8.2",
] ]
@ -1130,7 +1130,7 @@ dependencies = [
"serde_json", "serde_json",
"sha2", "sha2",
"tempdir", "tempdir",
"thiserror", "thiserror 1.0.64",
"tokio", "tokio",
"uuid 0.4.0", "uuid 0.4.0",
"warp", "warp",
@ -1153,7 +1153,7 @@ dependencies = [
"glib-build-tools 0.18.0", "glib-build-tools 0.18.0",
"gtk4", "gtk4",
"libadwaita", "libadwaita",
"thiserror", "thiserror 1.0.64",
"tokio", "tokio",
] ]
@ -1230,7 +1230,7 @@ version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d" checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d"
dependencies = [ dependencies = [
"thiserror", "thiserror 1.0.64",
] ]
[[package]] [[package]]
@ -1373,7 +1373,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -1540,7 +1540,7 @@ dependencies = [
"once_cell", "once_cell",
"pin-project-lite", "pin-project-lite",
"smallvec", "smallvec",
"thiserror", "thiserror 1.0.64",
] ]
[[package]] [[package]]
@ -1576,7 +1576,7 @@ dependencies = [
"memchr", "memchr",
"once_cell", "once_cell",
"smallvec", "smallvec",
"thiserror", "thiserror 1.0.64",
] ]
[[package]] [[package]]
@ -1608,7 +1608,7 @@ dependencies = [
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -2120,7 +2120,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"serde 1.0.210", "serde 1.0.210",
"thiserror", "thiserror 1.0.64",
] ]
[[package]] [[package]]
@ -2801,7 +2801,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -2836,7 +2836,7 @@ dependencies = [
"serde 1.0.210", "serde 1.0.210",
"serde_json", "serde_json",
"sgf", "sgf",
"thiserror", "thiserror 1.0.64",
"uuid 0.8.2", "uuid 0.8.2",
] ]
@ -3044,7 +3044,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -3084,7 +3084,7 @@ dependencies = [
"nix", "nix",
"once_cell", "once_cell",
"pipewire-sys", "pipewire-sys",
"thiserror", "thiserror 1.0.64",
] ]
[[package]] [[package]]
@ -3584,7 +3584,7 @@ dependencies = [
name = "result-extended" name = "result-extended"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"thiserror", "thiserror 1.0.64",
] ]
[[package]] [[package]]
@ -3776,7 +3776,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -3836,7 +3836,7 @@ dependencies = [
"nary_tree", "nary_tree",
"nom", "nom",
"serde 1.0.210", "serde 1.0.210",
"thiserror", "thiserror 1.0.64",
"typeshare", "typeshare",
"uuid 0.8.2", "uuid 0.8.2",
] ]
@ -4025,7 +4025,7 @@ dependencies = [
"sha2", "sha2",
"smallvec", "smallvec",
"sqlformat", "sqlformat",
"thiserror", "thiserror 1.0.64",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tracing", "tracing",
@ -4107,7 +4107,7 @@ dependencies = [
"smallvec", "smallvec",
"sqlx-core", "sqlx-core",
"stringprep", "stringprep",
"thiserror", "thiserror 1.0.64",
"tracing", "tracing",
"whoami", "whoami",
] ]
@ -4145,7 +4145,7 @@ dependencies = [
"smallvec", "smallvec",
"sqlx-core", "sqlx-core",
"stringprep", "stringprep",
"thiserror", "thiserror 1.0.64",
"tracing", "tracing",
"whoami", "whoami",
] ]
@ -4209,9 +4209,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.79" version = "2.0.87"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -4302,7 +4302,16 @@ version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
dependencies = [ 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]] [[package]]
@ -4313,7 +4322,18 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "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]] [[package]]
@ -4438,7 +4458,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -4558,7 +4578,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -4600,7 +4620,7 @@ dependencies = [
"log 0.4.22", "log 0.4.22",
"rand 0.8.5", "rand 0.8.5",
"sha1", "sha1",
"thiserror", "thiserror 1.0.64",
"url 2.5.2", "url 2.5.2",
"utf-8", "utf-8",
] ]
@ -4654,7 +4674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a615d6c2764852a2e88a4f16e9ce1ea49bb776b5872956309e170d63a042a34f" checksum = "a615d6c2764852a2e88a4f16e9ce1ea49bb776b5872956309e170d63a042a34f"
dependencies = [ dependencies = [
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]
@ -4857,12 +4877,14 @@ name = "visions"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"authdb", "authdb",
"cool_asserts",
"futures", "futures",
"http 1.1.0", "http 1.1.0",
"mime 0.3.17", "mime 0.3.17",
"mime_guess 2.0.5", "mime_guess 2.0.5",
"serde 1.0.210", "serde 1.0.210",
"serde_json", "serde_json",
"thiserror 2.0.3",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"typeshare", "typeshare",
@ -4958,7 +4980,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -4992,7 +5014,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -5263,7 +5285,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.79", "syn 2.0.87",
] ]
[[package]] [[package]]

View File

@ -19,3 +19,5 @@ futures = "0.3.31"
tokio-stream = "0.1.16" tokio-stream = "0.1.16"
typeshare = "1.0.4" typeshare = "1.0.4"
urlencoding = "2.1.3" urlencoding = "2.1.3"
cool_asserts = "2.0.3"
thiserror = "2.0.3"

View File

@ -0,0 +1,106 @@
use std::{
collections::{hash_map::Iter, 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())
}
}
impl From<String> for AssetId {
fn from(s: String) -> Self {
AssetId(s)
}
}
pub struct AssetIter<'a>(Iter<'a, AssetId, String>);
impl <'a> Iterator for AssetIter<'a> {
type Item = (&'a AssetId, &'a String);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
pub trait Assets {
fn assets<'a>(&'a self) -> AssetIter<'a>;
fn get(&self, asset_id: AssetId) -> Result<Vec<u8>, Error>;
}
pub struct FsAssets {
assets: HashMap<AssetId, String>,
}
impl FsAssets {
pub fn new() -> Self {
Self {
assets: HashMap::new(),
}
}
fn assets<'a>(&'a self) -> impl Iterator<Item = &'a AssetId> {
self.assets.keys()
}
}
impl Assets for FsAssets {
fn assets<'a>(&'a self) -> AssetIter<'a> {
AssetIter(self.assets.iter())
}
fn get(&self, asset_id: AssetId) -> Result<Vec<u8>, Error> {
unimplemented!()
}
}
#[cfg(test)]
pub mod mocks {
use std::collections::HashMap;
use super::*;
pub struct MemoryAssets {
assets: HashMap<AssetId, String>,
}
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<'a>(&'a self) -> AssetIter<'a> {
AssetIter(self.assets.iter())
}
fn get(&self, asset_id: AssetId) -> Result<Vec<u8>, Error> {
unimplemented!()
}
}
}

View File

@ -9,32 +9,42 @@ use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use urlencoding::decode; use urlencoding::decode;
use uuid::Uuid; use uuid::Uuid;
use crate::types::{AppError, Message, Tabletop, RGB}; use crate::{
asset_db::{AssetId, Assets},
types::{AppError, Message, Tabletop, RGB},
};
#[derive(Debug)] #[derive(Debug)]
struct WebsocketClient { struct WebsocketClient {
sender: Option<UnboundedSender<Message>>, sender: Option<UnboundedSender<Message>>,
} }
#[derive(Debug)]
pub struct AppState { pub struct AppState {
pub image_base: PathBuf, pub asset_db: Box<dyn Assets + Sync + Send + 'static>,
pub tabletop: Tabletop,
pub clients: HashMap<String, WebsocketClient>, pub clients: HashMap<String, WebsocketClient>,
pub tabletop: Tabletop,
} }
#[derive(Clone, Debug)] #[derive(Clone)]
pub struct Core(pub Arc<RwLock<AppState>>); pub struct Core(Arc<RwLock<AppState>>);
impl Core { impl Core {
pub fn new() -> Self { pub fn new<A>(assetdb: A) -> Self
where
A: Assets + Sync + Send + 'static,
{
Self(Arc::new(RwLock::new(AppState { Self(Arc::new(RwLock::new(AppState {
image_base: PathBuf::from("/home/savanni/Pictures"), asset_db: Box::new(assetdb),
clients: HashMap::new(),
tabletop: Tabletop { tabletop: Tabletop {
background_color: RGB{ red: 0xca, green: 0xb9, blue: 0xbb }, background_color: RGB {
red: 0xca,
green: 0xb9,
blue: 0xbb,
},
background_image: None, background_image: None,
}, },
clients: HashMap::new(),
}))) })))
} }
@ -68,35 +78,37 @@ impl Core {
} }
} }
pub fn get_file(&self, file_name: String) -> Vec<u8> { pub fn tabletop(&self) -> Tabletop {
self.0.read().unwrap().tabletop.clone()
}
pub async fn get_file(&self, file_name: String) -> Vec<u8> {
/*
let file_name = decode(&file_name).expect("UTF-8"); let file_name = decode(&file_name).expect("UTF-8");
let mut full_path = self.0.read().unwrap().image_base.clone(); let mut full_path = self.0.read().unwrap().image_base.clone();
full_path.push(file_name.to_string()); full_path.push(file_name.to_string());
let mut content: Vec<u8> = Vec::new(); let mut content: Vec<u8> = Vec::new();
let mut file = std::fs::File::open(&full_path).unwrap(); let mut file = std::fs::File::open(&full_path).unwrap();
file.read_to_end(&mut content).unwrap(); file.read_to_end(&mut content).unwrap();
content content
*/
unimplemented!()
} }
pub fn available_images(&self) -> Vec<String> { pub fn available_images(&self) -> Vec<AssetId> {
std::fs::read_dir(&self.0.read().unwrap().image_base) self.0
.read()
.unwrap() .unwrap()
.filter_map(|entry| match entry { .asset_db
Ok(entry_) => match mime_guess::from_path(entry_.path()).first() { .assets()
Some(mime) if mime.type_() == mime::IMAGE => Some( .filter_map(|(asset_id, value)| {
entry_ println!("[{:?}] {}", mime_guess::from_path(&value).first(), value);
.path() match mime_guess::from_path(&value).first() {
.file_name() Some(mime) if mime.type_() == mime::IMAGE => Some(asset_id.clone()),
.and_then(|filename| filename.to_str())
.and_then(|filename| Some(filename.to_owned()))
.unwrap(),
),
_ => None, _ => None,
}, }
Err(_) => None,
}) })
.collect() .collect()
} }
@ -121,3 +133,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.png".to_owned()),
(AssetId::from("asset_2"), "asset_2.jpg".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(), 2);
}
}

View File

@ -1,8 +1,5 @@
use std::{pin::Pin, time::Duration};
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio_stream::wrappers::UnboundedReceiverStream;
use warp::{http::Response, reply::Reply, ws::Message}; use warp::{http::Response, reply::Reply, ws::Message};
use crate::core::Core; use crate::core::Core;
@ -33,7 +30,7 @@ pub async fn handle_auth(
pub async fn handle_file(core: Core, file_name: String) -> impl Reply { pub async fn handle_file(core: Core, file_name: String) -> impl Reply {
let mimetype = mime_guess::from_path(&file_name).first().unwrap(); 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() Response::builder()
.header("application-type", mimetype.to_string()) .header("application-type", mimetype.to_string())
.body(bytes) .body(bytes)
@ -41,10 +38,13 @@ pub async fn handle_file(core: Core, file_name: String) -> impl Reply {
} }
pub async fn handle_available_images(core: Core) -> impl Reply { pub async fn handle_available_images(core: Core) -> impl Reply {
let image_paths: Vec<String> = core.available_images().into_iter()
.map(|path| format!("{}", path)).collect();
Response::builder() Response::builder()
.header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Origin", "*")
.header("Content-Type", "application/json") .header("Content-Type", "application/json")
.body(serde_json::to_string(&core.available_images()).unwrap()) .body(serde_json::to_string(&image_paths).unwrap())
.unwrap() .unwrap()
} }
@ -56,7 +56,7 @@ pub struct RegisterResponse {
url: String, url: String,
} }
pub async fn handle_register_client(core: Core, request: RegisterRequest) -> impl Reply { pub async fn handle_register_client(core: Core, _request: RegisterRequest) -> impl Reply {
let client_id = core.register_client(); let client_id = core.register_client();
Response::builder() Response::builder()
@ -82,7 +82,6 @@ pub async fn handle_connect_websocket(
ws: warp::ws::Ws, ws: warp::ws::Ws,
client_id: String, client_id: String,
) -> impl Reply { ) -> impl Reply {
// println!("handle_connect_websocket: {}", client_id);
ws.on_upgrade(move |socket| { ws.on_upgrade(move |socket| {
let core = core.clone(); let core = core.clone();
async move { async move {
@ -90,7 +89,7 @@ pub async fn handle_connect_websocket(
let mut receiver = core.connect_client(client_id.clone()); let mut receiver = core.connect_client(client_id.clone());
tokio::task::spawn(async move { tokio::task::spawn(async move {
let tabletop = core.0.read().unwrap().tabletop.clone(); let tabletop = core.tabletop();
let _ = ws_sender let _ = ws_sender
.send(Message::text( .send(Message::text(
serde_json::to_string(&crate::types::Message::UpdateTabletop(tabletop)) serde_json::to_string(&crate::types::Message::UpdateTabletop(tabletop))

View File

@ -1,3 +1,4 @@
use asset_db::FsAssets;
use authdb::AuthError; use authdb::AuthError;
use handlers::{ use handlers::{
handle_available_images, handle_connect_websocket, handle_file, handle_register_client, handle_set_background_image, handle_unregister_client, RegisterRequest 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, Filter,
}; };
mod asset_db;
mod core; mod core;
mod handlers; mod handlers;
// use handlers::handle_auth;
mod types; mod types;
@ -96,7 +96,7 @@ async fn handle_rejection(err: warp::Rejection) -> Result<impl Reply, Infallible
#[tokio::main] #[tokio::main]
pub async fn main() { pub async fn main() {
let core = core::Core::new(); let core = core::Core::new(FsAssets::new());
let log = warp::log("visions::api"); let log = warp::log("visions::api");
let route_image = warp::path!("api" / "v1" / "image" / String) let route_image = warp::path!("api" / "v1" / "image" / String)

View File

@ -3,6 +3,7 @@ use typeshare::typeshare;
#[derive(Debug)] #[derive(Debug)]
pub enum AppError { pub enum AppError {
NotFound(String),
JsonError(serde_json::Error), JsonError(serde_json::Error),
} }