generated from ElnuDev/rust-project
parent
33e42c17bb
commit
b995846e12
@ -1,2 +1,3 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = ["dyesub-tool"]
|
members = ["dyesub-tool"]
|
||||||
|
resolver = "2"
|
@ -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<Option<kle_serial::Keyboard>>,
|
||||||
|
) -> impl IntoView {
|
||||||
|
let file_input = create_node_ref::<Input>(cx);
|
||||||
|
let (file, set_file) = create_signal(cx, Option::<File>::None);
|
||||||
|
let (result_message, set_result_message) = create_signal(cx, Option::<ResultMessageData>::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 "<b>{&keyboard.metadata.name}</b>" 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: <ReadKleError as Into<&str>>::into(err).to_string(),
|
||||||
|
colorway: Colorway::Bad,
|
||||||
|
}));
|
||||||
|
file_input().unwrap().set_value("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
view! { cx,
|
||||||
|
<h3>"Load KLE JSON from file"</h3>
|
||||||
|
<ResultMessage message=result_message />
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept="application/json"
|
||||||
|
node_ref=file_input
|
||||||
|
on:change=move |_| {
|
||||||
|
set_file(file_input().unwrap().files().map(|files| files.get(0)).flatten());
|
||||||
|
on_submit();
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_kle_from_file(
|
||||||
|
file: ReadSignal<Option<File>>,
|
||||||
|
) -> Result<kle_serial::Keyboard, ReadKleError> {
|
||||||
|
Ok(serde_json::from_str(&read_file(file).await?)?)
|
||||||
|
}
|
@ -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<kle_serial::Keyboard, FetchKleError> {
|
||||||
|
let layout_string = fetch_file(url).await?;
|
||||||
|
Ok(serde_json::from_str(&layout_string)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn KeyboardFromWeb(
|
||||||
|
cx: Scope,
|
||||||
|
set_keyboard: WriteSignal<Option<kle_serial::Keyboard>>,
|
||||||
|
) -> impl IntoView {
|
||||||
|
let (result_message, set_result_message) = create_signal(cx, Option::<ResultMessageData>::None);
|
||||||
|
let input_element: NodeRef<Input> = 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 "<b>{&keyboard.metadata.name}</b>" 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: <FetchKleError as Into<&str>>::into(err).to_string(),
|
||||||
|
colorway: Colorway::Bad,
|
||||||
|
}));
|
||||||
|
input_element().unwrap().set_value("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
view! { cx,
|
||||||
|
<h3>"Load KLE JSON from web"</h3>
|
||||||
|
<ResultMessage message=result_message />
|
||||||
|
<form class="f-row align-items:center" on:submit=on_submit>
|
||||||
|
<input class="width:100%" type="text" placeholder="KLE layout, gist URL, gist ID, or direct URL to JSON file" required node_ref=input_element />
|
||||||
|
<input type="submit" value="Load" />
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
mod resultmessage;
|
||||||
|
pub use resultmessage::{ResultMessage, ResultMessageData};
|
||||||
|
|
||||||
|
mod keyboardfromfile;
|
||||||
|
pub use keyboardfromfile::KeyboardFromFile;
|
||||||
|
|
||||||
|
mod keyboardfromweb;
|
||||||
|
pub use keyboardfromweb::KeyboardFromWeb;
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
mod colorway;
|
||||||
|
pub use colorway::Colorway;
|
@ -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<Option<File>>,
|
||||||
|
) -> Result<String, ReadFileError> {
|
||||||
|
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<String, FetchFileError> {
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue