generated from ElnuDev/rust-project
parent
33e42c17bb
commit
b995846e12
@ -1,2 +1,3 @@
|
||||
[workspace]
|
||||
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