From b995846e121aaec5fb393e1fafecf46734c420fe Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Mon, 21 Aug 2023 21:58:46 -0700 Subject: [PATCH] Split into modules, use resolver v2, initial web loader implementation --- Cargo.lock | 11 +- Cargo.toml | 1 + dyesub-tool/Cargo.toml | 9 +- .../src/components/keyboardfromfile.rs | 64 +++++++ dyesub-tool/src/components/keyboardfromweb.rs | 59 +++++++ dyesub-tool/src/components/mod.rs | 8 + dyesub-tool/src/components/resultmessage.rs | 31 ++++ dyesub-tool/src/error/fetchfile.rs | 21 +++ dyesub-tool/src/error/fetchkle.rs | 35 ++++ dyesub-tool/src/error/mod.rs | 11 ++ dyesub-tool/src/error/readfile.rs | 23 +++ dyesub-tool/src/error/readkle.rs | 21 +++ dyesub-tool/src/main.rs | 160 ++---------------- dyesub-tool/src/models/colorway.rs | 25 +++ dyesub-tool/src/models/mod.rs | 2 + dyesub-tool/src/utils.rs | 34 ++++ 16 files changed, 361 insertions(+), 154 deletions(-) create mode 100644 dyesub-tool/src/components/keyboardfromfile.rs create mode 100644 dyesub-tool/src/components/keyboardfromweb.rs create mode 100644 dyesub-tool/src/components/mod.rs create mode 100644 dyesub-tool/src/components/resultmessage.rs create mode 100644 dyesub-tool/src/error/fetchfile.rs create mode 100644 dyesub-tool/src/error/fetchkle.rs create mode 100644 dyesub-tool/src/error/mod.rs create mode 100644 dyesub-tool/src/error/readfile.rs create mode 100644 dyesub-tool/src/error/readkle.rs create mode 100644 dyesub-tool/src/models/colorway.rs create mode 100644 dyesub-tool/src/models/mod.rs create mode 100644 dyesub-tool/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 9321164..ff223b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1448,9 +1448,9 @@ checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[package]] name = "reqwest" -version = "0.11.18" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" +checksum = "20b9b67e2ca7dd9e9f9285b759de30ff538aab981abaaf7bc9bd90b84a0126c3" dependencies = [ "base64 0.21.2", "bytes", @@ -2224,11 +2224,12 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 20dab38..7cb6ede 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,3 @@ [workspace] members = ["dyesub-tool"] +resolver = "2" \ No newline at end of file diff --git a/dyesub-tool/Cargo.toml b/dyesub-tool/Cargo.toml index d8ac557..db45730 100644 --- a/dyesub-tool/Cargo.toml +++ b/dyesub-tool/Cargo.toml @@ -16,4 +16,11 @@ strum = "0.25.0" strum_macros = "0.25.2" wasm-bindgen = "0.2.87" wasm-bindgen-futures = "0.4.37" -web-sys = { version = "0.3.64", features = ["FileList", "Blob"] } + +[dependencies.web-sys] +version = "0.3.64" +features = [ + "File", + "FileList", + "Request" +] diff --git a/dyesub-tool/src/components/keyboardfromfile.rs b/dyesub-tool/src/components/keyboardfromfile.rs new file mode 100644 index 0000000..529f854 --- /dev/null +++ b/dyesub-tool/src/components/keyboardfromfile.rs @@ -0,0 +1,64 @@ +use leptos::{*, html::Input}; +use web_sys::File; + +use crate::{components::ResultMessageData, utils::read_file, error::ReadKleError, models::Colorway}; +use super::ResultMessage; + +#[component] +pub fn KeyboardFromFile( + cx: Scope, + set_keyboard: WriteSignal>, +) -> impl IntoView { + let file_input = create_node_ref::(cx); + let (file, set_file) = create_signal(cx, Option::::None); + let (result_message, set_result_message) = create_signal(cx, Option::::None); + let on_submit = move || { + spawn_local(async move { + match read_kle_from_file(file).await { + Ok(keyboard) => { + set_result_message(Some(ResultMessageData { + title: "Success".to_owned(), + message: view! { cx, + "Loaded KLE layout "{&keyboard.metadata.name}" successfully!" + } + .into_view(cx), + colorway: Colorway::Ok, + })); + set_keyboard(Some(keyboard)); + } + Err(err) => { + set_result_message(Some(ResultMessageData { + message: view! { cx, + { + err.to_string() + } + } + .into_view(cx), + title: >::into(err).to_string(), + colorway: Colorway::Bad, + })); + file_input().unwrap().set_value(""); + } + } + }); + }; + view! { cx, +

"Load KLE JSON from file"

+ + + } +} + +async fn read_kle_from_file( + file: ReadSignal>, +) -> Result { + Ok(serde_json::from_str(&read_file(file).await?)?) +} \ No newline at end of file diff --git a/dyesub-tool/src/components/keyboardfromweb.rs b/dyesub-tool/src/components/keyboardfromweb.rs new file mode 100644 index 0000000..c08e885 --- /dev/null +++ b/dyesub-tool/src/components/keyboardfromweb.rs @@ -0,0 +1,59 @@ +use leptos::{*, html::Input}; +use web_sys::SubmitEvent; + +use crate::{error::FetchKleError, utils::fetch_file, models::Colorway}; +use super::{ResultMessage, ResultMessageData}; + +async fn fetch_layout(url: &str) -> Result { + let layout_string = fetch_file(url).await?; + Ok(serde_json::from_str(&layout_string)?) +} + +#[component] +pub fn KeyboardFromWeb( + cx: Scope, + set_keyboard: WriteSignal>, +) -> impl IntoView { + let (result_message, set_result_message) = create_signal(cx, Option::::None); + let input_element: NodeRef = create_node_ref(cx); + let on_submit = move |e: SubmitEvent| { + e.prevent_default(); + spawn_local(async move { + let url = input_element.get().unwrap().value(); + match fetch_layout(&url).await { + Ok(keyboard) => { + set_result_message(Some(ResultMessageData { + title: "Success".to_owned(), + message: view! { cx, + "Loaded KLE layout "{&keyboard.metadata.name}" successfully!" + } + .into_view(cx), + colorway: Colorway::Ok, + })); + set_keyboard(Some(keyboard)); + }, + Err(err) => { + set_result_message(Some(ResultMessageData { + message: view! { cx, + { + err.to_string() + } + } + .into_view(cx), + title: >::into(err).to_string(), + colorway: Colorway::Bad, + })); + input_element().unwrap().set_value(""); + } + } + }) + }; + view! { cx, +

"Load KLE JSON from web"

+ +
+ + +
+ } +} \ No newline at end of file diff --git a/dyesub-tool/src/components/mod.rs b/dyesub-tool/src/components/mod.rs new file mode 100644 index 0000000..07277d9 --- /dev/null +++ b/dyesub-tool/src/components/mod.rs @@ -0,0 +1,8 @@ +mod resultmessage; +pub use resultmessage::{ResultMessage, ResultMessageData}; + +mod keyboardfromfile; +pub use keyboardfromfile::KeyboardFromFile; + +mod keyboardfromweb; +pub use keyboardfromweb::KeyboardFromWeb; \ No newline at end of file diff --git a/dyesub-tool/src/components/resultmessage.rs b/dyesub-tool/src/components/resultmessage.rs new file mode 100644 index 0000000..8c0fec3 --- /dev/null +++ b/dyesub-tool/src/components/resultmessage.rs @@ -0,0 +1,31 @@ +use leptos::*; +use class_list::class_list; + +use crate::models::Colorway; + +#[derive(Clone)] +pub struct ResultMessageData { + pub title: String, + pub message: View, + pub colorway: Colorway, +} + +#[component] +pub fn ResultMessage(cx: Scope, message: ReadSignal>) -> impl IntoView { + let (open, set_open) = create_signal(cx, true); + create_effect(cx, move |_| { + message.track(); + set_open(true); + }); + move || view! { cx, + + {move || message().map(|ResultMessageData { title, message, colorway }| view! { cx, +
+ {title} + +

{message}

+
+ })} +
+ } +} \ No newline at end of file diff --git a/dyesub-tool/src/error/fetchfile.rs b/dyesub-tool/src/error/fetchfile.rs new file mode 100644 index 0000000..5538855 --- /dev/null +++ b/dyesub-tool/src/error/fetchfile.rs @@ -0,0 +1,21 @@ +use derive_more::From; +use strum_macros::IntoStaticStr; +use wasm_bindgen::JsValue; + +#[derive(From, IntoStaticStr)] +pub enum FetchFileError { + #[strum(serialize = "Failed to fetch file")] + Request(JsValue), + #[strum(serialize = "Failed to read fetched file to string")] + ReadToString, +} + +impl ToString for FetchFileError { + fn to_string(&self) -> String { + use FetchFileError::*; + match self { + Request(error) => error.as_string(), + _ => None, + }.unwrap_or_else(|| "".to_string()) + } +} \ No newline at end of file diff --git a/dyesub-tool/src/error/fetchkle.rs b/dyesub-tool/src/error/fetchkle.rs new file mode 100644 index 0000000..a711c13 --- /dev/null +++ b/dyesub-tool/src/error/fetchkle.rs @@ -0,0 +1,35 @@ +use derive_more::From; +use strum_macros::IntoStaticStr; + +use super::FetchFileError; + +#[derive(From, IntoStaticStr)] +pub enum FetchKleError { + #[strum(serialize = "Invalid source. Must be KLE layout, gist URL, gist ID, or direct URL to JSON file")] + InvalidSource, + #[strum(serialize = "Failed to fetch file")] + FetchFile(FetchFileError), + #[strum(serialize = "Failed to parse KLE JSON")] + Serde(serde_json::Error), +} + +impl ToString for FetchKleError { + fn to_string(&self) -> String { + use FetchKleError::*; + match self { + FetchFile(error) => Some(match error { + FetchFileError::Request(error_info) => { + let mut full_error = <&FetchFileError as Into<&str>>::into(error).to_string(); + if let Some(error_info) = error_info.as_string() { + full_error.push_str(": "); + full_error.push_str(&error_info); + } + full_error + }, + FetchFileError::ReadToString => error.to_string(), + }), + Serde(error) => Some(error.to_string()), + _ => None, + }.unwrap_or_else(|| "".to_string()) + } +} \ No newline at end of file diff --git a/dyesub-tool/src/error/mod.rs b/dyesub-tool/src/error/mod.rs new file mode 100644 index 0000000..1f2303b --- /dev/null +++ b/dyesub-tool/src/error/mod.rs @@ -0,0 +1,11 @@ +mod fetchfile; +pub use fetchfile::FetchFileError; + +mod readfile; +pub use readfile::ReadFileError; + +mod fetchkle; +pub use fetchkle::FetchKleError; + +mod readkle; +pub use readkle::ReadKleError; \ No newline at end of file diff --git a/dyesub-tool/src/error/readfile.rs b/dyesub-tool/src/error/readfile.rs new file mode 100644 index 0000000..10ac191 --- /dev/null +++ b/dyesub-tool/src/error/readfile.rs @@ -0,0 +1,23 @@ +use derive_more::From; +use strum_macros::IntoStaticStr; +use wasm_bindgen::JsValue; + +#[derive(From, IntoStaticStr)] +pub enum ReadFileError { + #[strum(serialize = "No file chosen")] + NoFile, + #[strum(serialize = "Failed to open file")] + TextAwait(JsValue), + #[strum(serialize = "Failed to parse file to string")] + ParseToString, +} + +impl ToString for ReadFileError { + fn to_string(&self) -> String { + use ReadFileError::*; + match self { + TextAwait(error) => error.as_string(), + _ => None + }.unwrap_or_else(|| "".to_string()) + } +} \ No newline at end of file diff --git a/dyesub-tool/src/error/readkle.rs b/dyesub-tool/src/error/readkle.rs new file mode 100644 index 0000000..dc8aa5f --- /dev/null +++ b/dyesub-tool/src/error/readkle.rs @@ -0,0 +1,21 @@ +use derive_more::From; +use strum_macros::IntoStaticStr; + +use super::ReadFileError; + +#[derive(From, IntoStaticStr)] +pub enum ReadKleError { + ReadFile(ReadFileError), + #[strum(serialize = "Failed to parse KLE JSON")] + Serde(serde_json::Error), +} + +impl ToString for ReadKleError { + fn to_string(&self) -> String { + use ReadKleError::*; + match self { + ReadFile(error) => error.to_string(), + Serde(error) => error.to_string(), + } + } +} \ No newline at end of file diff --git a/dyesub-tool/src/main.rs b/dyesub-tool/src/main.rs index 249d4ab..3e48724 100644 --- a/dyesub-tool/src/main.rs +++ b/dyesub-tool/src/main.rs @@ -1,166 +1,30 @@ #![feature(async_closure)] +#[allow(dead_code)] mod render; -pub mod svg; - -use class_list::class_list; -use derive_more::From; -use leptos::{ev::SubmitEvent, html::Input, *}; -use strum_macros::IntoStaticStr; -use wasm_bindgen::{JsCast, JsValue}; -use web_sys::File; - -#[derive(Clone)] -struct ResultMessageData { - title: String, - message: View, - colorway: Colorway, -} - -#[derive(Default, Clone, Copy, IntoStaticStr)] +mod error; +mod components; +mod utils; +mod models; #[allow(dead_code)] -enum Colorway { - #[default] - #[strum(serialize = "plain")] - Plain, - #[strum(serialize = "info")] - Info, - #[strum(serialize = "ok")] - Ok, - #[strum(serialize = "warn")] - Warn, - #[strum(serialize = "bad")] - Bad, -} - -#[component] -fn ResultMessage(cx: Scope, message: ReadSignal>) -> impl IntoView { - let (open, set_open) = create_signal(cx, true); - create_effect(cx, move |_| { - message.track(); - set_open(true); - }); - move || view! { cx, - - {move || message().map(|ResultMessageData { title, message, colorway }| view! { cx, -
>::into(colorway)] style="position: relative"> - {title} - -

{message}

-
- })} -
- } -} - -#[derive(From, IntoStaticStr)] -enum ReadKleError { - #[strum(serialize = "No file chosen")] - NoFile, - #[strum(serialize = "Failed to open file")] - TextAwait(JsValue), - #[strum(serialize = "Failed to parse file to string")] - ParseToString, - #[strum(serialize = "Failed to parse KLE JSON")] - Serde(serde_json::Error), -} +pub mod svg; -impl ToString for ReadKleError { - fn to_string(&self) -> String { - if let Self::TextAwait(error) = self { - if let Some(error) = error.as_string() { - return error; - } - } else if let Self::Serde(error) = self { - return error.to_string(); - } - "".to_string() - } -} +use leptos::*; +use wasm_bindgen::JsCast; -async fn read_kle_from_file( - file: &ReadSignal>, -) -> Result { - let file = match file() { - Some(file) => file, - None => return Err(ReadKleError::NoFile), - }; - let file_contents = match wasm_bindgen_futures::JsFuture::from(file.text()) - .await? - .as_string() - { - Some(contents) => contents, - None => return Err(ReadKleError::ParseToString), - }; - let keyboard = serde_json::from_str(&file_contents)?; - Ok(keyboard) -} - -#[component] -fn KeyboardFromFile( - cx: Scope, - set_keyboard: WriteSignal>, -) -> impl IntoView { - let file_input = create_node_ref::(cx); - let (file, set_file) = create_signal(cx, Option::::None); - let (result_message, set_result_message) = create_signal(cx, Option::::None); - let on_submit = move || { - spawn_local(async move { - match read_kle_from_file(&file).await { - Ok(keyboard) => { - set_result_message(Some(ResultMessageData { - title: "Success".to_owned(), - message: view! { cx, - "Loaded KLE layout "{&keyboard.metadata.name}" successfully!" - } - .into_view(cx), - colorway: Colorway::Ok, - })); - set_keyboard(Some(keyboard)); - } - Err(err) => { - set_result_message(Some(ResultMessageData { - message: view! { cx, - { - err.to_string() - } - } - .into_view(cx), - title: >::into(err).to_string(), - colorway: Colorway::Bad, - })); - file_input().unwrap().set_value(""); - } - } - }); - }; - view! { cx, -

"Load KLE JSON from file"

- -
- -
- } -} +use components::*; #[component] fn App(cx: Scope) -> impl IntoView { - let (keyboard, set_keyboard) = create_signal(cx, None); + let (_keyboard, set_keyboard) = create_signal(cx, None); view! { cx, + } } -pub fn main() { +fn main() { let root = document().query_selector("main").unwrap().unwrap(); mount_to(root.unchecked_into(), |cx| view! { cx, }); } diff --git a/dyesub-tool/src/models/colorway.rs b/dyesub-tool/src/models/colorway.rs new file mode 100644 index 0000000..cf32ae7 --- /dev/null +++ b/dyesub-tool/src/models/colorway.rs @@ -0,0 +1,25 @@ +use class_list::traits::ClassList; +use strum_macros::IntoStaticStr; + +#[derive(Default, Clone, Copy, IntoStaticStr)] +#[allow(dead_code)] +pub enum Colorway { + #[default] + #[strum(serialize = "plain")] + Plain, + #[strum(serialize = "info")] + Info, + #[strum(serialize = "ok")] + Ok, + #[strum(serialize = "warn")] + Warn, + #[strum(serialize = "bad")] + Bad, +} + +impl ClassList for Colorway { + fn to_class_list(&self, _normalize: bool) -> String { + let class: &str = self.into(); + class.to_string() + } +} \ No newline at end of file diff --git a/dyesub-tool/src/models/mod.rs b/dyesub-tool/src/models/mod.rs new file mode 100644 index 0000000..5aad77f --- /dev/null +++ b/dyesub-tool/src/models/mod.rs @@ -0,0 +1,2 @@ +mod colorway; +pub use colorway::Colorway; \ No newline at end of file diff --git a/dyesub-tool/src/utils.rs b/dyesub-tool/src/utils.rs new file mode 100644 index 0000000..c920213 --- /dev/null +++ b/dyesub-tool/src/utils.rs @@ -0,0 +1,34 @@ +use leptos::*; +use wasm_bindgen_futures::JsFuture; +use web_sys::{File, Window, Request}; + +use crate::error::{ReadFileError, FetchFileError}; + +pub fn window() -> Window { + web_sys::window().unwrap() +} + +pub async fn read_file( + file: ReadSignal>, +) -> Result { + let file = match file() { + Some(file) => file, + None => return Err(ReadFileError::NoFile), + }; + match JsFuture::from(file.text()) + .await? + .as_string() + { + Some(contents) => Ok(contents), + None => Err(ReadFileError::ParseToString), + } +} + +pub async fn fetch_file(url: &str) -> Result { + let request = Request::new_with_str(url)?; + let response = JsFuture::from(window().fetch_with_request(&request)).await?; + match response.as_string() { + Some(string) => Ok(string), + None => Err(FetchFileError::ReadToString), + } +} \ No newline at end of file