Remove old APIs, improve/fix front logging
This commit is contained in:
parent
b7b42a8c6d
commit
44298f660f
20 changed files with 115 additions and 1353 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -196,17 +196,6 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console_log"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
|
@ -1565,7 +1554,6 @@ name = "usdpl-front"
|
|||
version = "0.11.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"console_log",
|
||||
"futures",
|
||||
"futures-channel",
|
||||
"gloo-net",
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
name = "usdpl-back"
|
||||
version = "0.11.0"
|
||||
edition = "2021"
|
||||
authors = ["NGnius <ngniusness@gmail.com>"]
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://github.com/NGnius/usdpl-rs"
|
||||
repository = "https://git.ngni.us/NG-SD-Plugins/usdpl-rs"
|
||||
readme = "../README.md"
|
||||
description = "Universal Steam Deck Plugin Library back-end"
|
||||
|
||||
|
@ -11,7 +12,7 @@ description = "Universal Steam Deck Plugin Library back-end"
|
|||
default = ["blocking"]
|
||||
decky = ["usdpl-core/decky"]
|
||||
blocking = [] # synchronous API for async functionality, using tokio
|
||||
encrypt = ["usdpl-core/encrypt", "obfstr", "hex"]
|
||||
#encrypt = ["usdpl-core", "obfstr", "hex"]
|
||||
|
||||
[dependencies]
|
||||
usdpl-core = { version = "0.11", path = "../usdpl-core"}
|
||||
|
@ -27,7 +28,6 @@ prost = "0.11"
|
|||
ratchet_rs = { version = "0.4", features = [ "deflate" ] }
|
||||
|
||||
# HTTP web framework
|
||||
#warp = { version = "0.3" }
|
||||
bytes = { version = "1.1" }
|
||||
tokio = { version = "1", features = [ "full" ]}
|
||||
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
//! Common low-level file operations
|
||||
use std::fmt::Display;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Write something to a file.
|
||||
/// Useful for kernel configuration files.
|
||||
#[inline]
|
||||
pub fn write_single<P: AsRef<Path>, D: Display>(path: P, display: D) -> Result<(), io::Error> {
|
||||
let mut file = File::create(path)?;
|
||||
write!(file, "{}", display)
|
||||
}
|
||||
|
||||
/// read_single error
|
||||
#[derive(Debug)]
|
||||
pub enum ReadError<E> {
|
||||
/// IO Error
|
||||
Io(io::Error),
|
||||
/// String parsing error
|
||||
Parse(E),
|
||||
}
|
||||
|
||||
impl<E: std::error::Error> std::fmt::Display for ReadError<E> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Io(io) => write!(f, "io: {}", io),
|
||||
Self::Parse(e) => write!(f, "parse: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: std::error::Error> std::error::Error for ReadError<E> {}
|
||||
|
||||
/// Read something from a file.
|
||||
/// Useful for kernel configuration files.
|
||||
#[inline]
|
||||
pub fn read_single<P: AsRef<Path>, D: FromStr<Err = E>, E>(path: P) -> Result<D, ReadError<E>> {
|
||||
let mut file = File::open(path).map_err(ReadError::Io)?;
|
||||
let mut string = String::new();
|
||||
file.read_to_string(&mut string).map_err(ReadError::Io)?;
|
||||
string.trim().parse().map_err(ReadError::Parse)
|
||||
}
|
|
@ -1,2 +1 @@
|
|||
pub mod dirs;
|
||||
pub mod files;
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
name = "usdpl-build"
|
||||
version = "0.11.0"
|
||||
edition = "2021"
|
||||
authors = ["NGnius <ngniusness@gmail.com>"]
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://git.ngni.us/NG-SD-Plugins/usdpl-rs"
|
||||
readme = "../README.md"
|
||||
description = "Universal Steam Deck Plugin Library core"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
|
|
@ -1026,6 +1026,7 @@ impl IServiceGenerator for WasmServiceGenerator {
|
|||
.expect("FileDescriptorSet required for WASM service generator");
|
||||
let service_struct_name = quote::format_ident!("{}Client", service.name);
|
||||
let service_js_name = quote::format_ident!("{}", service.name);
|
||||
let service_str_name = service.name.clone();
|
||||
let service_methods = generate_service_methods(&service, fds);
|
||||
let service_types = generate_service_io_types(&service, fds);
|
||||
let mod_name = quote::format_ident!("js_{}", service.name.to_lowercase());
|
||||
|
@ -1059,9 +1060,11 @@ impl IServiceGenerator for WasmServiceGenerator {
|
|||
impl #service_js_name {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(port: u16) -> Self {
|
||||
usdpl_front::init_usdpl();
|
||||
let implementation = super::#service_struct_name::new(
|
||||
WebSocketHandler::new(port)
|
||||
);
|
||||
log::info!("Initialized ws service {} on port {}", #service_str_name, port);
|
||||
Self {
|
||||
service: implementation,
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
[package]
|
||||
name = "usdpl-core"
|
||||
version = "0.11.0"
|
||||
authors = ["NGnius <ngniusness@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://github.com/NGnius/usdpl-rs"
|
||||
repository = "https://git.ngni.us/NG-SD-Plugins/usdpl-rs"
|
||||
readme = "../README.md"
|
||||
description = "Universal Steam Deck Plugin Library core"
|
||||
description = "Universal Steam Deck Plugin Library core designed for all architectures"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
|
|
@ -2,19 +2,12 @@
|
|||
//! This contains serialization functionality and networking datatypes.
|
||||
#![warn(missing_docs)]
|
||||
|
||||
mod remote_call;
|
||||
|
||||
#[cfg(not(any(feature = "decky")))]
|
||||
mod api_any;
|
||||
mod api_common;
|
||||
#[cfg(all(feature = "decky", not(any(feature = "any"))))]
|
||||
mod api_decky;
|
||||
|
||||
pub mod serdes;
|
||||
pub mod socket;
|
||||
|
||||
pub use remote_call::{RemoteCall, RemoteCallResponse};
|
||||
|
||||
/// USDPL core API.
|
||||
/// This contains functionality used in both the back-end and front-end.
|
||||
pub mod api {
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
use std::io::{Read, Write};
|
||||
|
||||
use crate::serdes::{DumpError, Dumpable, LoadError, Loadable, Primitive};
|
||||
|
||||
/// Remote call packet representing a function to call on the back-end, sent from the front-end
|
||||
pub struct RemoteCall {
|
||||
/// The call id assigned by the front-end
|
||||
pub id: u64,
|
||||
/// The function's name
|
||||
pub function: String,
|
||||
/// The function's input parameters
|
||||
pub parameters: Vec<Primitive>,
|
||||
}
|
||||
|
||||
impl Loadable for RemoteCall {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let (id_num, len0) = u64::load(buffer)?;
|
||||
let (function_name, len1) = String::load(buffer)?;
|
||||
let (params, len2) = Vec::<Primitive>::load(buffer)?;
|
||||
Ok((
|
||||
Self {
|
||||
id: id_num,
|
||||
function: function_name,
|
||||
parameters: params,
|
||||
},
|
||||
len0 + len1 + len2,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for RemoteCall {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
let len0 = self.id.dump(buffer)?;
|
||||
let len1 = self.function.dump(buffer)?;
|
||||
let len2 = self.parameters.dump(buffer)?;
|
||||
Ok(len0 + len1 + len2)
|
||||
}
|
||||
}
|
||||
|
||||
/// Remote call response packet representing the response from a remote call after the back-end has executed it.
|
||||
pub struct RemoteCallResponse {
|
||||
/// The call id from the RemoteCall
|
||||
pub id: u64,
|
||||
/// The function's result
|
||||
pub response: Vec<Primitive>,
|
||||
}
|
||||
|
||||
impl Loadable for RemoteCallResponse {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let (id_num, len0) = u64::load(buffer)?;
|
||||
let (response_var, len1) = Vec::<Primitive>::load(buffer)?;
|
||||
Ok((
|
||||
Self {
|
||||
id: id_num,
|
||||
response: response_var,
|
||||
},
|
||||
len0 + len1,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for RemoteCallResponse {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
let len0 = self.id.dump(buffer)?;
|
||||
let len1 = self.response.dump(buffer)?;
|
||||
Ok(len0 + len1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn remote_call_idempotence_test() {
|
||||
let call = RemoteCall {
|
||||
id: 42,
|
||||
function: "something very long just in case this causes unexpected issues".into(),
|
||||
parameters: vec!["param1".into(), 42f64.into()],
|
||||
};
|
||||
|
||||
let mut buffer = String::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
||||
let len = call.dump_base64(&mut buffer).unwrap();
|
||||
|
||||
println!("base64 dumped: `{}` (len: {})", buffer, len);
|
||||
|
||||
let (loaded_call, loaded_len) = RemoteCall::load_base64(buffer.as_bytes()).unwrap();
|
||||
assert_eq!(len, loaded_len, "Expected load and dump lengths to match");
|
||||
|
||||
assert_eq!(loaded_call.id, call.id, "RemoteCall.id does not match");
|
||||
assert_eq!(
|
||||
loaded_call.function, call.function,
|
||||
"RemoteCall.function does not match"
|
||||
);
|
||||
if let Primitive::String(loaded) = &loaded_call.parameters[0] {
|
||||
if let Primitive::String(original) = &call.parameters[0] {
|
||||
assert_eq!(loaded, original, "RemoteCall.parameters[0] does not match");
|
||||
} else {
|
||||
panic!("Original call parameter 0 is not String")
|
||||
}
|
||||
} else {
|
||||
panic!("Loaded call parameter 0 is not String")
|
||||
}
|
||||
if let Primitive::F64(loaded) = &loaded_call.parameters[1] {
|
||||
if let Primitive::F64(original) = &call.parameters[1] {
|
||||
assert_eq!(loaded, original, "RemoteCall.parameters[1] does not match");
|
||||
} else {
|
||||
panic!("Original call parameter 1 is not f64")
|
||||
}
|
||||
} else {
|
||||
panic!("Loaded call parameter 1 is not f64")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
use std::io::Write;
|
||||
|
||||
use super::{DumpError, Dumpable};
|
||||
|
||||
impl Dumpable for String {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
let str_bytes = self.as_bytes();
|
||||
let len_bytes = (str_bytes.len() as u32).to_le_bytes();
|
||||
let size1 = buffer.write(&len_bytes).map_err(DumpError::Io)?;
|
||||
let size2 = buffer.write(&str_bytes).map_err(DumpError::Io)?;
|
||||
Ok(size1 + size2)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Dumpable> Dumpable for Vec<T> {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
let len_bytes = (self.len() as u32).to_le_bytes();
|
||||
let mut total = buffer.write(&len_bytes).map_err(DumpError::Io)?;
|
||||
for obj in self.iter() {
|
||||
let len = obj.dump(buffer)?;
|
||||
total += len;
|
||||
}
|
||||
Ok(total)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: Dumpable, T1: Dumpable> Dumpable for (T0, T1) {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
Ok(self.0.dump(buffer)? + self.1.dump(buffer)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: Dumpable, T1: Dumpable, T2: Dumpable> Dumpable for (T0, T1, T2) {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
Ok(self.0.dump(buffer)? + self.1.dump(buffer)? + self.2.dump(buffer)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: Dumpable, T1: Dumpable, T2: Dumpable, T3: Dumpable> Dumpable for (T0, T1, T2, T3) {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
Ok(self.0.dump(buffer)?
|
||||
+ self.1.dump(buffer)?
|
||||
+ self.2.dump(buffer)?
|
||||
+ self.3.dump(buffer)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: Dumpable, T1: Dumpable, T2: Dumpable, T3: Dumpable, T4: Dumpable> Dumpable
|
||||
for (T0, T1, T2, T3, T4)
|
||||
{
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
Ok(self.0.dump(buffer)?
|
||||
+ self.1.dump(buffer)?
|
||||
+ self.2.dump(buffer)?
|
||||
+ self.3.dump(buffer)?
|
||||
+ self.4.dump(buffer)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for bool {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
buffer.write(&[*self as u8]).map_err(DumpError::Io)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for u8 {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
buffer.write(&[*self]).map_err(DumpError::Io)
|
||||
}
|
||||
}
|
||||
|
||||
/*impl Dumpable for i8 {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
buffer.write(&self.to_le_bytes()).map_err(DumpError::Io)
|
||||
}
|
||||
}*/
|
||||
|
||||
macro_rules! int_impl {
|
||||
($type:ty) => {
|
||||
impl Dumpable for $type {
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
buffer.write(&self.to_le_bytes()).map_err(DumpError::Io)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int_impl! {u16}
|
||||
int_impl! {u32}
|
||||
int_impl! {u64}
|
||||
int_impl! {u128}
|
||||
|
||||
int_impl! {i8}
|
||||
int_impl! {i16}
|
||||
int_impl! {i32}
|
||||
int_impl! {i64}
|
||||
int_impl! {i128}
|
||||
|
||||
int_impl! {f32}
|
||||
int_impl! {f64}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! test_impl {
|
||||
($fn_name:ident, $data:expr, $expected_len:literal, $expected_dump:expr) => {
|
||||
#[test]
|
||||
fn $fn_name() {
|
||||
let data = $data;
|
||||
let mut buffer = Vec::with_capacity(128);
|
||||
let write_len = data.dump(&mut buffer).expect("Dump not ok");
|
||||
assert_eq!(write_len, $expected_len, "Wrong amount written");
|
||||
assert_eq!(&buffer[..write_len], $expected_dump);
|
||||
println!("Dumped {:?}", buffer.as_slice());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_impl! {string_dump_test, "test".to_string(), 8, &[4, 0, 0, 0, 116, 101, 115, 116]}
|
||||
|
||||
test_impl! {
|
||||
vec_dump_test,
|
||||
vec![
|
||||
"".to_string(),
|
||||
"test1".to_string(),
|
||||
"test2".to_string()
|
||||
],
|
||||
26,
|
||||
&[3, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 5, 0, 0, 0, 116, 101, 115, 116, 50]
|
||||
}
|
||||
|
||||
test_impl! {tuple2_dump_test, (0u8, 1u8), 2, &[0, 1]}
|
||||
test_impl! {tuple3_dump_test, (0u8, 1u8, 2u8), 3, &[0, 1, 2]}
|
||||
test_impl! {tuple4_dump_test, (0u8, 1u8, 2u8, 3u8), 4, &[0, 1, 2, 3]}
|
||||
test_impl! {tuple5_dump_test, (0u8, 1u8, 2u8, 3u8, 4u8), 5, &[0, 1, 2, 3, 4]}
|
||||
|
||||
test_impl! {bool_true_dump_test, true, 1, &[1]}
|
||||
test_impl! {bool_false_dump_test, false, 1, &[0]}
|
||||
|
||||
// testing macro-generated code isn't particularly useful, but do it anyway
|
||||
|
||||
test_impl! {u8_dump_test, 42u8, 1, &[42]}
|
||||
test_impl! {u16_dump_test, 42u16, 2, &[42, 0]}
|
||||
test_impl! {u32_dump_test, 42u32, 4, &[42, 0, 0, 0]}
|
||||
test_impl! {u64_dump_test, 42u64, 8, &[42, 0, 0, 0, 0, 0, 0, 0]}
|
||||
test_impl! {u128_dump_test, 42u128, 16, &[42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}
|
||||
|
||||
test_impl! {i8_dump_test, 42i8, 1, &[42]}
|
||||
test_impl! {i16_dump_test, 42i16, 2, &[42, 0]}
|
||||
test_impl! {i32_dump_test, 42i32, 4, &[42, 0, 0, 0]}
|
||||
test_impl! {i64_dump_test, 42i64, 8, &[42, 0, 0, 0, 0, 0, 0, 0]}
|
||||
test_impl! {i128_dump_test, 42i128, 16, &[42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}
|
||||
|
||||
test_impl! {f32_dump_test, 42f32, 4, &[0, 0, 40, 66]}
|
||||
test_impl! {f64_dump_test, 42f64, 8, &[0, 0, 0, 0, 0, 0, 69, 64]}
|
||||
}
|
|
@ -1,185 +0,0 @@
|
|||
use std::io::Read;
|
||||
|
||||
use super::{LoadError, Loadable};
|
||||
|
||||
impl Loadable for String {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let mut u32_bytes: [u8; 4] = [u8::MAX; 4];
|
||||
buffer.read_exact(&mut u32_bytes).map_err(LoadError::Io)?;
|
||||
let str_size = u32::from_le_bytes(u32_bytes) as usize;
|
||||
//let mut str_buf = String::with_capacity(str_size);
|
||||
let mut str_buf = Vec::with_capacity(str_size);
|
||||
let mut byte_buf = [u8::MAX; 1];
|
||||
for _ in 0..str_size {
|
||||
buffer.read_exact(&mut byte_buf).map_err(LoadError::Io)?;
|
||||
str_buf.push(byte_buf[0]);
|
||||
}
|
||||
//let size2 = buffer.read_to_string(&mut str_buf).map_err(LoadError::Io)?;
|
||||
Ok((
|
||||
String::from_utf8(str_buf).map_err(|_| LoadError::InvalidData)?,
|
||||
str_size + 4,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Loadable> Loadable for Vec<T> {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let mut u32_bytes: [u8; 4] = [u8::MAX; 4];
|
||||
buffer.read_exact(&mut u32_bytes).map_err(LoadError::Io)?;
|
||||
let count = u32::from_le_bytes(u32_bytes) as usize;
|
||||
let mut cursor = 4;
|
||||
let mut items = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
let (obj, len) = T::load(buffer)?;
|
||||
cursor += len;
|
||||
items.push(obj);
|
||||
}
|
||||
Ok((items, cursor))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: Loadable, T1: Loadable> Loadable for (T0, T1) {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let (t0, len0) = T0::load(buffer)?;
|
||||
let (t1, len1) = T1::load(buffer)?;
|
||||
Ok(((t0, t1), len0 + len1))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: Loadable, T1: Loadable, T2: Loadable> Loadable for (T0, T1, T2) {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let (t0, len0) = T0::load(buffer)?;
|
||||
let (t1, len1) = T1::load(buffer)?;
|
||||
let (t2, len2) = T2::load(buffer)?;
|
||||
Ok(((t0, t1, t2), len0 + len1 + len2))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: Loadable, T1: Loadable, T2: Loadable, T3: Loadable> Loadable for (T0, T1, T2, T3) {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let (t0, len0) = T0::load(buffer)?;
|
||||
let (t1, len1) = T1::load(buffer)?;
|
||||
let (t2, len2) = T2::load(buffer)?;
|
||||
let (t3, len3) = T3::load(buffer)?;
|
||||
Ok(((t0, t1, t2, t3), len0 + len1 + len2 + len3))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0: Loadable, T1: Loadable, T2: Loadable, T3: Loadable, T4: Loadable> Loadable
|
||||
for (T0, T1, T2, T3, T4)
|
||||
{
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let (t0, len0) = T0::load(buffer)?;
|
||||
let (t1, len1) = T1::load(buffer)?;
|
||||
let (t2, len2) = T2::load(buffer)?;
|
||||
let (t3, len3) = T3::load(buffer)?;
|
||||
let (t4, len4) = T4::load(buffer)?;
|
||||
Ok(((t0, t1, t2, t3, t4), len0 + len1 + len2 + len3 + len4))
|
||||
}
|
||||
}
|
||||
|
||||
impl Loadable for bool {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let mut byte = [u8::MAX; 1];
|
||||
buffer.read_exact(&mut byte).map_err(LoadError::Io)?;
|
||||
Ok((byte[0] != 0, 1))
|
||||
}
|
||||
}
|
||||
|
||||
impl Loadable for u8 {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let mut byte = [u8::MAX; 1];
|
||||
buffer.read_exact(&mut byte).map_err(LoadError::Io)?;
|
||||
Ok((byte[0], 1))
|
||||
}
|
||||
}
|
||||
|
||||
impl Loadable for i8 {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let mut byte = [u8::MAX; 1];
|
||||
buffer.read_exact(&mut byte).map_err(LoadError::Io)?;
|
||||
Ok((i8::from_le_bytes(byte), 1))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! int_impl {
|
||||
($type:ty, $size:literal) => {
|
||||
impl Loadable for $type {
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let mut bytes: [u8; $size] = [u8::MAX; $size];
|
||||
buffer.read_exact(&mut bytes).map_err(LoadError::Io)?;
|
||||
let i = <$type>::from_le_bytes(bytes);
|
||||
Ok((i, $size))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int_impl! {u16, 2}
|
||||
int_impl! {u32, 4}
|
||||
int_impl! {u64, 8}
|
||||
int_impl! {u128, 16}
|
||||
|
||||
int_impl! {i16, 2}
|
||||
int_impl! {i32, 4}
|
||||
int_impl! {i64, 8}
|
||||
int_impl! {i128, 16}
|
||||
|
||||
int_impl! {f32, 4}
|
||||
int_impl! {f64, 8}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
macro_rules! test_impl {
|
||||
($fn_name:ident, $data:expr, $type:ty, $expected_len:literal, $expected_load:expr) => {
|
||||
#[test]
|
||||
fn $fn_name() {
|
||||
let buffer_data = $data;
|
||||
let mut buffer = Vec::with_capacity(buffer_data.len());
|
||||
buffer.extend_from_slice(&buffer_data);
|
||||
let (obj, read_len) = <$type>::load(&mut Cursor::new(buffer)).expect("Load not ok");
|
||||
assert_eq!(read_len, $expected_len, "Wrong amount read");
|
||||
assert_eq!(obj, $expected_load, "Loaded value not as expected");
|
||||
println!("Loaded {:?}", obj);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_impl! {string_load_test, [4u8, 0, 0, 0, 116, 101, 115, 116, 0, 128], String, 8, "test"}
|
||||
test_impl! {
|
||||
vec_load_test,
|
||||
[3u8, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 5, 0, 0, 0, 116, 101, 115, 116, 50],
|
||||
Vec<String>,
|
||||
26,
|
||||
vec![
|
||||
"".to_string(),
|
||||
"test1".to_string(),
|
||||
"test2".to_string()
|
||||
]
|
||||
}
|
||||
|
||||
test_impl! {tuple2_load_test, [0, 1], (u8, u8), 2, (0, 1)}
|
||||
|
||||
test_impl! {bool_true_load_test, [1], bool, 1, true}
|
||||
test_impl! {bool_false_load_test, [0], bool, 1, false}
|
||||
|
||||
// testing macro-generated code isn't particularly useful, but do it anyway
|
||||
|
||||
test_impl! {u8_load_test, [42], u8, 1, 42u8}
|
||||
test_impl! {u16_load_test, [42, 0], u16, 2, 42u16}
|
||||
test_impl! {u32_load_test, [42, 0, 0, 0], u32, 4, 42u32}
|
||||
test_impl! {u64_load_test, [42, 0, 0, 0, 0, 0, 0, 0], u64, 8, 42u64}
|
||||
test_impl! {u128_load_test, [42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], u128, 16, 42u128}
|
||||
|
||||
test_impl! {i8_load_test, [42], i8, 1, 42i8}
|
||||
test_impl! {i16_load_test, [42, 0], i16, 2, 42i16}
|
||||
test_impl! {i32_load_test, [42, 0, 0, 0], i32, 4, 42i32}
|
||||
test_impl! {i64_load_test, [42, 0, 0, 0, 0, 0, 0, 0], i64, 8, 42i64}
|
||||
test_impl! {i128_load_test, [42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], i128, 16, 42i128}
|
||||
|
||||
test_impl! {f32_load_test, [0, 0, 40, 66], f32, 4, 42f32}
|
||||
test_impl! {f64_load_test, [0, 0, 0, 0, 0, 0, 69, 64], f64, 8, 42f64}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
//! Serialization and deserialization functionality.
|
||||
//! Little endian is preferred.
|
||||
|
||||
mod dump_impl;
|
||||
mod load_impl;
|
||||
mod primitive;
|
||||
mod traits;
|
||||
|
||||
pub use primitive::Primitive;
|
||||
pub use traits::{DumpError, Dumpable, LoadError, Loadable};
|
|
@ -1,163 +0,0 @@
|
|||
use super::{DumpError, Dumpable, LoadError, Loadable};
|
||||
use std::io::{Read, Write};
|
||||
|
||||
/// Primitive types supported for communication between the USDPL back- and front-end.
|
||||
/// These are used for sending over the TCP connection.
|
||||
pub enum Primitive {
|
||||
/// Null or unsupported object
|
||||
Empty,
|
||||
/// String-like
|
||||
String(String),
|
||||
/// f32
|
||||
F32(f32),
|
||||
/// f64
|
||||
F64(f64),
|
||||
/// u32
|
||||
U32(u32),
|
||||
/// u64
|
||||
U64(u64),
|
||||
/// i32
|
||||
I32(i32),
|
||||
/// i64
|
||||
I64(i64),
|
||||
/// boolean
|
||||
Bool(bool),
|
||||
/// Non-primitive in Json format
|
||||
Json(String),
|
||||
}
|
||||
|
||||
impl Primitive {
|
||||
/// Discriminant -- first byte of a dumped primitive
|
||||
const fn discriminant(&self) -> u8 {
|
||||
match self {
|
||||
Self::Empty => 1,
|
||||
Self::String(_) => 2,
|
||||
Self::F32(_) => 3,
|
||||
Self::F64(_) => 4,
|
||||
Self::U32(_) => 5,
|
||||
Self::U64(_) => 6,
|
||||
Self::I32(_) => 7,
|
||||
Self::I64(_) => 8,
|
||||
Self::Bool(_) => 9,
|
||||
Self::Json(_) => 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Loadable for Primitive {
|
||||
fn load(buf: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let mut discriminant_buf = [u8::MAX; 1];
|
||||
buf.read_exact(&mut discriminant_buf)
|
||||
.map_err(LoadError::Io)?;
|
||||
let mut result: (Self, usize) = match discriminant_buf[0] {
|
||||
//0 => (None, 0),
|
||||
1 => (Self::Empty, 0),
|
||||
2 => String::load(buf).map(|(obj, len)| (Self::String(obj), len))?,
|
||||
3 => f32::load(buf).map(|(obj, len)| (Self::F32(obj), len))?,
|
||||
4 => f64::load(buf).map(|(obj, len)| (Self::F64(obj), len))?,
|
||||
5 => u32::load(buf).map(|(obj, len)| (Self::U32(obj), len))?,
|
||||
6 => u64::load(buf).map(|(obj, len)| (Self::U64(obj), len))?,
|
||||
7 => i32::load(buf).map(|(obj, len)| (Self::I32(obj), len))?,
|
||||
8 => i64::load(buf).map(|(obj, len)| (Self::I64(obj), len))?,
|
||||
9 => bool::load(buf).map(|(obj, len)| (Self::Bool(obj), len))?,
|
||||
10 => String::load(buf).map(|(obj, len)| (Self::Json(obj), len))?,
|
||||
_ => return Err(LoadError::InvalidData),
|
||||
};
|
||||
result.1 += 1;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for Primitive {
|
||||
fn dump(&self, buf: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
let size1 = buf.write(&[self.discriminant()]).map_err(DumpError::Io)?;
|
||||
let result = match self {
|
||||
Self::Empty => Ok(0),
|
||||
Self::String(s) => s.dump(buf),
|
||||
Self::F32(x) => x.dump(buf),
|
||||
Self::F64(x) => x.dump(buf),
|
||||
Self::U32(x) => x.dump(buf),
|
||||
Self::U64(x) => x.dump(buf),
|
||||
Self::I32(x) => x.dump(buf),
|
||||
Self::I64(x) => x.dump(buf),
|
||||
Self::Bool(x) => x.dump(buf),
|
||||
Self::Json(x) => x.dump(buf),
|
||||
}?;
|
||||
Ok(size1 + result)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::Into<Primitive> for &str {
|
||||
fn into(self) -> Primitive {
|
||||
Primitive::String(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::Into<Primitive> for () {
|
||||
fn into(self) -> Primitive {
|
||||
Primitive::Empty
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! into_impl {
|
||||
($type:ty, $variant:ident) => {
|
||||
impl std::convert::Into<Primitive> for $type {
|
||||
fn into(self) -> Primitive {
|
||||
Primitive::$variant(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
into_impl! {String, String}
|
||||
into_impl! {bool, Bool}
|
||||
|
||||
into_impl! {u32, U32}
|
||||
into_impl! {u64, U64}
|
||||
|
||||
into_impl! {i32, I32}
|
||||
into_impl! {i64, I64}
|
||||
|
||||
into_impl! {f32, F32}
|
||||
into_impl! {f64, F64}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
#[test]
|
||||
fn string_idempotence_test() {
|
||||
let data = "Test";
|
||||
let primitive = Primitive::String(data.to_string());
|
||||
let mut buffer = Vec::with_capacity(128);
|
||||
let write_len = primitive.dump(&mut buffer).expect("Dump not ok");
|
||||
let (obj, read_len) = Primitive::load(&mut Cursor::new(buffer)).expect("Load not ok");
|
||||
assert_eq!(
|
||||
write_len, read_len,
|
||||
"Amount written and amount read do not match"
|
||||
);
|
||||
if let Primitive::String(result) = obj {
|
||||
assert_eq!(data, result, "Data written and read does not match");
|
||||
} else {
|
||||
panic!("Read non-string primitive");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_idempotence_test() {
|
||||
let primitive = Primitive::Empty;
|
||||
let mut buffer = Vec::with_capacity(128);
|
||||
let write_len = primitive.dump(&mut buffer).expect("Dump not ok");
|
||||
let (obj, read_len) = Primitive::load(&mut Cursor::new(buffer)).expect("Load not ok");
|
||||
assert_eq!(
|
||||
write_len, read_len,
|
||||
"Amount written and amount read do not match"
|
||||
);
|
||||
if let Primitive::Empty = obj {
|
||||
//assert_eq!(data, result, "Data written and read does not match");
|
||||
} else {
|
||||
panic!("Read non-string primitive");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,150 +0,0 @@
|
|||
use base64::{decode_config_buf, encode_config_buf, Config};
|
||||
use std::io::{Cursor, Read, Write};
|
||||
|
||||
const B64_CONF: Config = Config::new(base64::CharacterSet::Standard, true);
|
||||
|
||||
#[cfg(feature = "encrypt")]
|
||||
const ASSOCIATED_DATA: &[u8] = b"usdpl-core-data";
|
||||
|
||||
#[cfg(feature = "encrypt")]
|
||||
use aes_gcm_siv::aead::{AeadInPlace, NewAead};
|
||||
|
||||
/// Errors from Loadable::load
|
||||
#[derive(Debug)]
|
||||
pub enum LoadError {
|
||||
/// Buffer smaller than expected
|
||||
TooSmallBuffer,
|
||||
/// Unexpected/corrupted data encountered
|
||||
InvalidData,
|
||||
/// Encrypted data cannot be decrypted
|
||||
#[cfg(feature = "encrypt")]
|
||||
DecryptionError,
|
||||
/// Read error
|
||||
Io(std::io::Error),
|
||||
/// Unimplemented
|
||||
#[cfg(debug_assertions)]
|
||||
Todo,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LoadError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::TooSmallBuffer => write!(f, "LoadError: TooSmallBuffer"),
|
||||
Self::InvalidData => write!(f, "LoadError: InvalidData"),
|
||||
#[cfg(feature = "encrypt")]
|
||||
Self::DecryptionError => write!(f, "LoadError: DecryptionError"),
|
||||
Self::Io(err) => write!(f, "LoadError: Io({})", err),
|
||||
#[cfg(debug_assertions)]
|
||||
Self::Todo => write!(f, "LoadError: TODO!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Load an object from the buffer
|
||||
pub trait Loadable: Sized {
|
||||
/// Read the buffer, building the object and returning the amount of bytes read.
|
||||
/// If anything is wrong with the buffer, Err should be returned.
|
||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError>;
|
||||
|
||||
/// Load data from a base64-encoded buffer
|
||||
fn load_base64(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
let mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
||||
decode_config_buf(buffer, B64_CONF, &mut buffer2).map_err(|_| LoadError::InvalidData)?;
|
||||
let mut cursor = Cursor::new(buffer2);
|
||||
Self::load(&mut cursor)
|
||||
}
|
||||
|
||||
/// Load data from an encrypted base64-encoded buffer
|
||||
#[cfg(feature = "encrypt")]
|
||||
fn load_encrypted(buffer: &[u8], key: &[u8], nonce: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
//println!("encrypted buffer: {}", String::from_utf8(buffer.to_vec()).unwrap());
|
||||
let key = aes_gcm_siv::Key::from_slice(key);
|
||||
let cipher = aes_gcm_siv::Aes256GcmSiv::new(key);
|
||||
let nonce = aes_gcm_siv::Nonce::from_slice(nonce);
|
||||
let mut decoded_buf = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
||||
base64::decode_config_buf(buffer, B64_CONF, &mut decoded_buf)
|
||||
.map_err(|_| LoadError::InvalidData)?;
|
||||
//println!("Decoded buf: {:?}", decoded_buf);
|
||||
cipher
|
||||
.decrypt_in_place(nonce, ASSOCIATED_DATA, &mut decoded_buf)
|
||||
.map_err(|_| LoadError::DecryptionError)?;
|
||||
//println!("Decrypted buf: {:?}", decoded_buf);
|
||||
let mut cursor = Cursor::new(decoded_buf);
|
||||
Self::load(&mut cursor)
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors from Dumpable::dump
|
||||
#[derive(Debug)]
|
||||
pub enum DumpError {
|
||||
/// Buffer not big enough to dump data into
|
||||
TooSmallBuffer,
|
||||
/// Data cannot be dumped
|
||||
Unsupported,
|
||||
/// Data cannot be encrypted
|
||||
#[cfg(feature = "encrypt")]
|
||||
EncryptionError,
|
||||
/// Write error
|
||||
Io(std::io::Error),
|
||||
/// Unimplemented
|
||||
#[cfg(debug_assertions)]
|
||||
Todo,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DumpError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::TooSmallBuffer => write!(f, "DumpError: TooSmallBuffer"),
|
||||
Self::Unsupported => write!(f, "DumpError: Unsupported"),
|
||||
#[cfg(feature = "encrypt")]
|
||||
Self::EncryptionError => write!(f, "DumpError: EncryptionError"),
|
||||
Self::Io(err) => write!(f, "DumpError: Io({})", err),
|
||||
#[cfg(debug_assertions)]
|
||||
Self::Todo => write!(f, "DumpError: TODO!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dump an object into the buffer
|
||||
pub trait Dumpable {
|
||||
/// Write the object to the buffer, returning the amount of bytes written.
|
||||
/// If anything is wrong, false should be returned.
|
||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError>;
|
||||
|
||||
/// Dump data as base64-encoded.
|
||||
/// Useful for transmitting data as text.
|
||||
fn dump_base64(&self, buffer: &mut String) -> Result<usize, DumpError> {
|
||||
let mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
||||
let len = self.dump(&mut buffer2)?;
|
||||
encode_config_buf(&buffer2[..len], B64_CONF, buffer);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
/// Dump data as an encrypted base64-encoded buffer
|
||||
#[cfg(feature = "encrypt")]
|
||||
fn dump_encrypted(
|
||||
&self,
|
||||
buffer: &mut Vec<u8>,
|
||||
key: &[u8],
|
||||
nonce: &[u8],
|
||||
) -> Result<usize, DumpError> {
|
||||
let mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
||||
let size = self.dump(&mut buffer2)?;
|
||||
buffer2.truncate(size);
|
||||
//println!("Buf: {:?}", buffer2);
|
||||
let key = aes_gcm_siv::Key::from_slice(key);
|
||||
let cipher = aes_gcm_siv::Aes256GcmSiv::new(key);
|
||||
let nonce = aes_gcm_siv::Nonce::from_slice(nonce);
|
||||
cipher
|
||||
.encrypt_in_place(nonce, ASSOCIATED_DATA, &mut buffer2)
|
||||
.map_err(|_| DumpError::EncryptionError)?;
|
||||
//println!("Encrypted slice: {:?}", &buffer2);
|
||||
let mut base64_buf = String::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
||||
encode_config_buf(buffer2.as_slice(), B64_CONF, &mut base64_buf);
|
||||
//println!("base64 len: {}", base64_buf.as_bytes().len());
|
||||
buffer.extend_from_slice(base64_buf.as_bytes());
|
||||
//let string = String::from_utf8(buffer.as_slice().to_vec()).unwrap();
|
||||
//println!("Encoded slice: {}", string);
|
||||
Ok(base64_buf.len())
|
||||
}
|
||||
}
|
|
@ -1,174 +0,0 @@
|
|||
//! Web messaging
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
|
||||
use crate::serdes::{DumpError, Dumpable, LoadError, Loadable};
|
||||
use crate::{RemoteCall, RemoteCallResponse};
|
||||
|
||||
/// Host IP address for web browsers
|
||||
pub const HOST_STR: &str = "localhost";
|
||||
/// Host IP address
|
||||
pub const HOST: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
|
||||
|
||||
/// Standard max packet size
|
||||
pub const PACKET_BUFFER_SIZE: usize = 1024;
|
||||
/// Encryption nonce size
|
||||
pub const NONCE_SIZE: usize = 12;
|
||||
|
||||
/// Address and port
|
||||
#[inline]
|
||||
pub fn socket_addr(port: u16) -> SocketAddr {
|
||||
SocketAddr::V4(SocketAddrV4::new(HOST, port))
|
||||
}
|
||||
|
||||
/// Accepted Packet types and the data they contain
|
||||
pub enum Packet {
|
||||
/// A remote call
|
||||
Call(RemoteCall),
|
||||
/// A reponse to a remote call
|
||||
CallResponse(RemoteCallResponse),
|
||||
/// Unused
|
||||
KeepAlive,
|
||||
/// Invalid
|
||||
Invalid,
|
||||
/// General message
|
||||
Message(String),
|
||||
/// Response to an unsupported packet
|
||||
Unsupported,
|
||||
/// Broken packet type, useful for testing
|
||||
Bad,
|
||||
/// Many packets merged into one
|
||||
Many(Vec<Packet>),
|
||||
/// Translation data dump
|
||||
Translations(Vec<(String, Vec<String>)>),
|
||||
/// Request translations for language
|
||||
Language(String),
|
||||
}
|
||||
|
||||
impl Packet {
|
||||
/// Byte representing the packet type -- the first byte of any packet in USDPL
|
||||
const fn discriminant(&self) -> u8 {
|
||||
match self {
|
||||
Self::Call(_) => 1,
|
||||
Self::CallResponse(_) => 2,
|
||||
Self::KeepAlive => 3,
|
||||
Self::Invalid => 4,
|
||||
Self::Message(_) => 5,
|
||||
Self::Unsupported => 6,
|
||||
Self::Bad => 7,
|
||||
Self::Many(_) => 8,
|
||||
Self::Translations(_) => 9,
|
||||
Self::Language(_) => 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Loadable for Packet {
|
||||
fn load(buf: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||
let mut discriminant_buf = [u8::MAX; 1];
|
||||
buf.read_exact(&mut discriminant_buf)
|
||||
.map_err(LoadError::Io)?;
|
||||
let mut result: (Self, usize) = match discriminant_buf[0] {
|
||||
//0 => (None, 0),
|
||||
1 => {
|
||||
let (obj, len) = RemoteCall::load(buf)?;
|
||||
(Self::Call(obj), len)
|
||||
}
|
||||
2 => {
|
||||
let (obj, len) = RemoteCallResponse::load(buf)?;
|
||||
(Self::CallResponse(obj), len)
|
||||
}
|
||||
3 => (Self::KeepAlive, 0),
|
||||
4 => (Self::Invalid, 0),
|
||||
5 => {
|
||||
let (obj, len) = String::load(buf)?;
|
||||
(Self::Message(obj), len)
|
||||
}
|
||||
6 => (Self::Unsupported, 0),
|
||||
7 => return Err(LoadError::InvalidData),
|
||||
8 => {
|
||||
let (obj, len) = <_>::load(buf)?;
|
||||
(Self::Many(obj), len)
|
||||
}
|
||||
9 => {
|
||||
let (obj, len) = <_>::load(buf)?;
|
||||
(Self::Translations(obj), len)
|
||||
}
|
||||
10 => {
|
||||
let (obj, len) = <_>::load(buf)?;
|
||||
(Self::Language(obj), len)
|
||||
}
|
||||
_ => return Err(LoadError::InvalidData),
|
||||
};
|
||||
result.1 += 1;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for Packet {
|
||||
fn dump(&self, buf: &mut dyn Write) -> Result<usize, DumpError> {
|
||||
let size1 = buf.write(&[self.discriminant()]).map_err(DumpError::Io)?;
|
||||
let result = match self {
|
||||
Self::Call(c) => c.dump(buf),
|
||||
Self::CallResponse(c) => c.dump(buf),
|
||||
Self::KeepAlive => Ok(0),
|
||||
Self::Invalid => Ok(0),
|
||||
Self::Message(s) => s.dump(buf),
|
||||
Self::Unsupported => Ok(0),
|
||||
Self::Bad => return Err(DumpError::Unsupported),
|
||||
Self::Many(v) => v.dump(buf),
|
||||
Self::Translations(tr) => tr.dump(buf),
|
||||
Self::Language(l) => l.dump(buf),
|
||||
}?;
|
||||
Ok(size1 + result)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "encrypt")]
|
||||
#[test]
|
||||
fn encryption_integration_test() {
|
||||
let key =
|
||||
hex_literal::hex!("59C4E408F27250B3147E7724511824F1D28ED7BEF43CF7103ACE747F77A2B265");
|
||||
let nonce = [0u8; NONCE_SIZE];
|
||||
let packet = Packet::Call(RemoteCall {
|
||||
id: 42,
|
||||
function: "test".into(),
|
||||
parameters: Vec::new(),
|
||||
});
|
||||
let mut buffer = Vec::with_capacity(PACKET_BUFFER_SIZE);
|
||||
let len = packet.dump_encrypted(&mut buffer, &key, &nonce).unwrap();
|
||||
println!(
|
||||
"buffer: {}",
|
||||
String::from_utf8(buffer.as_slice()[..len].to_vec()).unwrap()
|
||||
);
|
||||
|
||||
let (packet_out, _len) =
|
||||
Packet::load_encrypted(&buffer.as_slice()[..len], &key, &nonce).unwrap();
|
||||
|
||||
if let Packet::Call(call_out) = packet_out {
|
||||
if let Packet::Call(call_in) = packet {
|
||||
assert_eq!(
|
||||
call_in.id, call_out.id,
|
||||
"Input and output packets do not match"
|
||||
);
|
||||
assert_eq!(
|
||||
call_in.function, call_out.function,
|
||||
"Input and output packets do not match"
|
||||
);
|
||||
assert_eq!(
|
||||
call_in.parameters.len(),
|
||||
call_out.parameters.len(),
|
||||
"Input and output packets do not match"
|
||||
);
|
||||
} else {
|
||||
panic!("Packet in not a Call");
|
||||
}
|
||||
} else {
|
||||
panic!("Packet out not a Call!");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
[package]
|
||||
name = "usdpl-front"
|
||||
version = "0.11.0"
|
||||
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
||||
authors = ["NGnius <ngniusness@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://github.com/NGnius/usdpl-rs"
|
||||
repository = "https://git.ngni.us/NG-SD-Plugins/usdpl-rs"
|
||||
readme = "../README.md"
|
||||
description = "Universal Steam Deck Plugin Library front-end designed for WASM"
|
||||
|
||||
|
@ -14,8 +14,8 @@ crate-type = ["cdylib", "rlib"]
|
|||
[features]
|
||||
default = []
|
||||
decky = ["usdpl-core/decky"]
|
||||
debug = ["console_error_panic_hook", "console_log"]
|
||||
encrypt = ["usdpl-core/encrypt", "obfstr", "hex"]
|
||||
debug = ["console_error_panic_hook"]
|
||||
#encrypt = ["usdpl-core/encrypt", "obfstr", "hex"]
|
||||
|
||||
[dependencies]
|
||||
wasm-bindgen = "0.2"
|
||||
|
@ -23,7 +23,6 @@ wasm-bindgen-futures = "0.4"
|
|||
gloo-net = { version = "0.4", features = ["websocket"] }
|
||||
futures = "0.3"
|
||||
futures-channel = "0.3"
|
||||
console_log = { version = "1.0", optional = true, features = ["color"] }
|
||||
|
||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||
# logging them with `console.error`. This is great for development, but requires
|
||||
|
|
|
@ -32,23 +32,19 @@ async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender<Result<bytes::By
|
|||
};
|
||||
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::log_1(&format!("ws opened successfully with url `{}`", url).into());
|
||||
log::debug!("ws opened successfully with url `{}`", url);
|
||||
|
||||
let (mut input_done, mut output_done) = (false, false);
|
||||
let mut last_ws_state = ws.state();
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::log_1(&format!("ws with url `{}` initial state: {:?}", url, last_ws_state).into());
|
||||
log::debug!("ws with url `{}` initial state: {:?}", url, last_ws_state);
|
||||
let (mut ws_sink, mut ws_stream) = ws.split();
|
||||
let (mut left, mut right) = (input.next(), ws_stream.next());
|
||||
while ws_is_alive(&last_ws_state) {
|
||||
if !input_done && !output_done {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::debug_1(&format!("Input and output streams are both alive").into());
|
||||
log::debug!("Input and output streams are both alive");
|
||||
match select(left, right).await {
|
||||
Either::Left((next, outstanding)) => {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::debug_1(&format!("Got message to send over websocket").into());
|
||||
log::debug!("Got message to send over websocket");
|
||||
if let Some(next) = next {
|
||||
match next {
|
||||
Ok(next) => {
|
||||
|
@ -65,8 +61,7 @@ async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender<Result<bytes::By
|
|||
left = input.next();
|
||||
},
|
||||
Either::Right((response, outstanding)) => {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::debug_1(&format!("Received message from websocket").into());
|
||||
log::debug!("Received message from websocket");
|
||||
if let Some(next) = response {
|
||||
match next {
|
||||
Ok(Message::Bytes(b)) => tx.send(Ok(b.into())).await.unwrap_or(()),
|
||||
|
@ -84,11 +79,9 @@ async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender<Result<bytes::By
|
|||
}
|
||||
}
|
||||
} else if input_done {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::debug_1(&format!("Input stream is complete").into());
|
||||
log::debug!("Input stream is complete");
|
||||
if let Some(next) = right.await {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::debug_1(&format!("Received message from websocket").into());
|
||||
log::debug!("Received message from websocket");
|
||||
match next {
|
||||
Ok(Message::Bytes(b)) => tx.send(Ok(b.into())).await.unwrap_or(()),
|
||||
Ok(_) => tx.send(Err("Message::Text not allowed".into())).await.unwrap_or(()),
|
||||
|
@ -104,11 +97,9 @@ async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender<Result<bytes::By
|
|||
right = ws_stream.next();
|
||||
} else {
|
||||
// output_done is true
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::debug_1(&format!("Output stream is complete").into());
|
||||
log::debug!("Output stream is complete");
|
||||
if let Some(next) = left.await {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::debug_1(&format!("Got message to send over websocket").into());
|
||||
log::debug!("Got message to send over websocket");
|
||||
match next {
|
||||
Ok(next) => {
|
||||
if let Err(e) = ws_sink.send(Message::Bytes(next.into())).await {
|
||||
|
@ -129,38 +120,7 @@ async fn send_recv_ws<'a>(mut tx: futures_channel::mpsc::Sender<Result<bytes::By
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::debug_1(&format!("ws with url `{}` has closed", url).into());
|
||||
/*spawn_local(async move {
|
||||
while let State::Open = ws.state() {
|
||||
if let Some(next) = input.next().await {
|
||||
match next {
|
||||
Ok(next) => {
|
||||
if let Err(e) = ws.send(Message::Bytes(next.into())).await {
|
||||
tx2.send(Err(e.to_string())).await.unwrap_or(());
|
||||
}
|
||||
},
|
||||
Err(e) => tx2.send(Err(e.to_string())).await.unwrap_or(())
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
spawn_local(async move {
|
||||
while let State::Open = ws.state() {
|
||||
if let Some(next) = ws.next().await {
|
||||
match next {
|
||||
Ok(Message::Bytes(b)) => tx.send(Ok(b.into())).await.unwrap_or(()),
|
||||
Ok(_) => tx.send(Err("Message::Text not allowed".into())).await.unwrap_or(()),
|
||||
Err(e) => tx.send(Err(e.to_string())).await.unwrap_or(()),
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
});*/
|
||||
log::debug!("ws with url `{}` has closed", url);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -197,8 +157,7 @@ impl ClientHandler<'static> for WebSocketHandler {
|
|||
"ws://usdpl-ws-{}.localhost:{}/{}.{}/{}",
|
||||
id, self.port, package, service, method,
|
||||
);
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::log_1(&format!("doing send/receive on ws url `{}`", url).into());
|
||||
log::debug!("doing send/receive on ws url `{}`", url);
|
||||
let (tx, rx) = futures_channel::mpsc::channel(CHANNEL_BOUND);
|
||||
spawn_local(send_recv_ws(tx, url, input));
|
||||
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
//use std::net::TcpStream;
|
||||
//use std::io::{Read, Write};
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
|
||||
//use web_sys::{WebSocket, MessageEvent, ErrorEvent};
|
||||
use js_sys::JsString;
|
||||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||
//use wasm_rs_shared_channel::{Expects, spsc::{Receiver, Sender}};
|
||||
|
||||
use usdpl_core::serdes::{Dumpable, Loadable, Primitive};
|
||||
use usdpl_core::socket;
|
||||
|
||||
#[cfg(feature = "encrypt")]
|
||||
const NONCE: [u8; socket::NONCE_SIZE] = [0u8; socket::NONCE_SIZE];
|
||||
|
||||
pub async fn send_recv_packet(
|
||||
id: u64,
|
||||
packet: socket::Packet,
|
||||
port: u16,
|
||||
#[cfg(feature = "encrypt")] key: Vec<u8>,
|
||||
) -> Result<socket::Packet, JsValue> {
|
||||
let mut opts = RequestInit::new();
|
||||
opts.method("POST");
|
||||
opts.mode(RequestMode::Cors);
|
||||
|
||||
let url = format!(
|
||||
"http://usdpl{}.{}:{}/usdpl/call",
|
||||
id,
|
||||
socket::HOST_STR,
|
||||
port
|
||||
);
|
||||
|
||||
#[allow(unused_variables)]
|
||||
let (buffer, len) = dump_to_buffer(
|
||||
packet,
|
||||
#[cfg(feature = "encrypt")]
|
||||
key.as_slice(),
|
||||
)?;
|
||||
let string: String = String::from_utf8_lossy(buffer.as_slice()).into();
|
||||
#[cfg(feature = "debug")]
|
||||
crate::imports::console_log(&format!("Dumped base64 `{}` len:{}", string, len));
|
||||
opts.body(Some(&string.into()));
|
||||
|
||||
let request = Request::new_with_str_and_init(&url, &opts)?;
|
||||
|
||||
//request.headers().set("Accept", "text/base64")?;
|
||||
//.set("Authorization", "wasm TODO_KEY")?;
|
||||
|
||||
let window = web_sys::window().unwrap();
|
||||
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
|
||||
|
||||
let resp: Response = resp_value.dyn_into()?;
|
||||
let text = JsFuture::from(resp.text()?).await?;
|
||||
let string: JsString = text.dyn_into()?;
|
||||
|
||||
let rust_str = string.as_string().unwrap();
|
||||
#[cfg(feature = "debug")]
|
||||
crate::imports::console_log(&format!(
|
||||
"Received base64 `{}` len:{}",
|
||||
rust_str,
|
||||
rust_str.len()
|
||||
));
|
||||
|
||||
#[cfg(not(feature = "encrypt"))]
|
||||
{
|
||||
Ok(socket::Packet::load_base64(rust_str.as_bytes())
|
||||
.map_err(super::convert::str_to_js)?
|
||||
.0)
|
||||
}
|
||||
|
||||
#[cfg(feature = "encrypt")]
|
||||
{
|
||||
Ok(
|
||||
socket::Packet::load_encrypted(rust_str.as_bytes(), key.as_slice(), &NONCE)
|
||||
.map_err(super::convert::str_to_js)?
|
||||
.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_call(
|
||||
id: u64,
|
||||
packet: socket::Packet,
|
||||
port: u16,
|
||||
#[cfg(feature = "encrypt")] key: Vec<u8>,
|
||||
) -> Result<Vec<Primitive>, JsValue> {
|
||||
let packet = send_recv_packet(
|
||||
id,
|
||||
packet,
|
||||
port,
|
||||
#[cfg(feature = "encrypt")]
|
||||
key,
|
||||
)
|
||||
.await?;
|
||||
|
||||
match packet {
|
||||
socket::Packet::CallResponse(resp) => Ok(resp.response),
|
||||
_ => {
|
||||
//imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url()));
|
||||
Err("Expected call response message, got something else".into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "encrypt")]
|
||||
fn dump_to_buffer(packet: socket::Packet, key: &[u8]) -> Result<(Vec<u8>, usize), JsValue> {
|
||||
let mut buffer = Vec::with_capacity(socket::PACKET_BUFFER_SIZE);
|
||||
//buffer.extend_from_slice(&[0u8; socket::PACKET_BUFFER_SIZE]);
|
||||
let len = packet
|
||||
.dump_encrypted(&mut buffer, key, &NONCE)
|
||||
.map_err(super::convert::str_to_js)?;
|
||||
Ok((buffer, len))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "encrypt"))]
|
||||
fn dump_to_buffer(packet: socket::Packet) -> Result<(Vec<u8>, usize), JsValue> {
|
||||
let mut buffer = String::with_capacity(socket::PACKET_BUFFER_SIZE);
|
||||
//buffer.extend_from_slice(&[0u8; socket::PACKET_BUFFER_SIZE]);
|
||||
let len = packet
|
||||
.dump_base64(&mut buffer)
|
||||
.map_err(super::convert::str_to_js)?;
|
||||
Ok((buffer.as_bytes().to_vec(), len))
|
||||
}
|
52
usdpl-front/src/console_logs.rs
Normal file
52
usdpl-front/src/console_logs.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
pub(crate) struct BuiltInLogger {
|
||||
min_level: log::Level,
|
||||
}
|
||||
|
||||
impl BuiltInLogger {
|
||||
pub const fn new(min: log::Level) -> Self {
|
||||
Self {
|
||||
min_level: min,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl log::Log for BuiltInLogger {
|
||||
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||
metadata.level() <= self.min_level
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
match record.level() {
|
||||
log::Level::Error => web_sys::console::error_1(&fmt_msg(record).into()),
|
||||
log::Level::Warn => web_sys::console::warn_1(&fmt_msg(record).into()),
|
||||
log::Level::Info => web_sys::console::log_1(&fmt_msg(record).into()),
|
||||
log::Level::Debug => web_sys::console::debug_1(&fmt_msg(record).into()),
|
||||
log::Level::Trace => web_sys::console::debug_1(&fmt_msg(record).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
fn fmt_msg(record: &log::Record) -> String {
|
||||
#[cfg(feature = "debug")]
|
||||
{ format!("[{}]({}) {}", record.level(), file_line_info(record), record.args()) }
|
||||
#[cfg(not(feature = "debug"))]
|
||||
{ format!("[{}]({}) {}", record.level(), module_line_info(record), record.args()) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
fn file_line_info(record: &log::Record) -> String {
|
||||
let filepath = record.file().unwrap_or("<unknown file>");
|
||||
let line = record.line().map(|l| l.to_string()).unwrap_or_else(|| "line?".to_string());
|
||||
format!("{}:{}", filepath, line)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "debug"))]
|
||||
fn module_line_info(record: &log::Record) -> String {
|
||||
let target = record.target();
|
||||
let line = record.line().map(|l| l.to_string()).unwrap_or_else(|| "line?".to_string());
|
||||
format!("{}:{}", target, line)
|
||||
}
|
|
@ -7,15 +7,10 @@
|
|||
|
||||
mod client_handler;
|
||||
pub use client_handler::WebSocketHandler;
|
||||
//mod connection;
|
||||
mod console_logs;
|
||||
mod convert;
|
||||
pub mod wasm;
|
||||
|
||||
/*#[allow(missing_docs)] // existence is pain otherwise
|
||||
pub mod _nrpc_js_interop {
|
||||
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
||||
}*/
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub mod _helpers {
|
||||
pub use js_sys;
|
||||
|
@ -26,21 +21,14 @@ pub mod _helpers {
|
|||
pub use nrpc;
|
||||
}
|
||||
|
||||
//use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
//use js_sys::Array;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
//use usdpl_core::{socket::Packet, RemoteCall};
|
||||
//const REMOTE_CALL_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
||||
//const REMOTE_PORT: std::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::new(31337);
|
||||
#[cfg(feature = "debug")]
|
||||
const DEFAULT_MIN_LEVEL: log::Level = log::Level::Trace;
|
||||
#[cfg(not(feature = "debug"))]
|
||||
const DEFAULT_MIN_LEVEL: log::Level = log::Level::Info;
|
||||
|
||||
/*static mut CTX: UsdplContext = UsdplContext {
|
||||
port: 0,
|
||||
//id: AtomicU64::new(0),
|
||||
#[cfg(feature = "encrypt")]
|
||||
key: Vec::new(),
|
||||
};*/
|
||||
const DEFAULT_LOGGER: console_logs::BuiltInLogger = console_logs::BuiltInLogger::new(DEFAULT_MIN_LEVEL);
|
||||
|
||||
static mut CACHE: Option<std::collections::HashMap<String, JsValue>> = None;
|
||||
|
||||
|
@ -51,56 +39,40 @@ fn encryption_key() -> Vec<u8> {
|
|||
hex::decode(obfstr::obfstr!(env!("USDPL_ENCRYPTION_KEY"))).unwrap()
|
||||
}
|
||||
|
||||
/*
|
||||
//#[wasm_bindgen]
|
||||
#[derive(Debug)]
|
||||
struct UsdplContext {
|
||||
port: u16,
|
||||
id: AtomicU64,
|
||||
#[cfg(feature = "encrypt")]
|
||||
key: Vec<u8>,
|
||||
}
|
||||
|
||||
fn get_port() -> u16 {
|
||||
unsafe { CTX.port }
|
||||
}
|
||||
|
||||
#[cfg(feature = "encrypt")]
|
||||
fn get_key() -> Vec<u8> {
|
||||
unsafe { CTX.key.clone() }
|
||||
}*/
|
||||
|
||||
/*fn increment_id() -> u64 {
|
||||
let atomic = unsafe { &CTX.id };
|
||||
atomic.fetch_add(1, Ordering::SeqCst)
|
||||
}*/
|
||||
static INIT_DONE: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
|
||||
|
||||
/// Initialize the front-end library
|
||||
#[wasm_bindgen]
|
||||
pub fn init_usdpl(port: u16) {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::log_1(&format!("init_usdpl(port={})", port).into());
|
||||
//#[wasm_bindgen]
|
||||
pub fn init_usdpl() {
|
||||
if !INIT_DONE.swap(true, std::sync::atomic::Ordering::SeqCst) {
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
#[cfg(feature = "console_log")]
|
||||
console_log::init_with_level(log::Level::Debug).expect("USDPL: error initializing console log");
|
||||
|
||||
/*unsafe {
|
||||
CTX = UsdplContext {
|
||||
port: port,
|
||||
//id: AtomicU64::new(0),
|
||||
#[cfg(feature = "encrypt")]
|
||||
key: encryption_key(),
|
||||
};
|
||||
}*/
|
||||
log::set_logger(&DEFAULT_LOGGER)
|
||||
.map_err(|e| web_sys::console::error_1(&format!("Failed to setup USDPL logger: {}", e).into()))
|
||||
.unwrap_or(());
|
||||
log::set_max_level(log::LevelFilter::Trace);
|
||||
log::debug!("init_usdpl() log configured");
|
||||
|
||||
unsafe {
|
||||
CACHE = Some(std::collections::HashMap::new());
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::log_1(&format!("USDPL:{} init succeeded", port).into());
|
||||
log::info!("USDPL:{} init succeeded", port);
|
||||
log::info!("USDPL init succeeded: {}", build_info());
|
||||
} else {
|
||||
log::info!("USDPL init was re-attempted");
|
||||
}
|
||||
}
|
||||
|
||||
fn build_info() -> String {
|
||||
format!("{} v{} ({}) for {} by {}, more: {}",
|
||||
env!("CARGO_PKG_NAME"),
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
env!("CARGO_PKG_LICENSE"),
|
||||
target_usdpl(),
|
||||
env!("CARGO_PKG_AUTHORS"),
|
||||
env!("CARGO_PKG_REPOSITORY"),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the targeted plugin framework, or "any" if unknown
|
||||
|
@ -140,92 +112,6 @@ pub fn get_value(key: String) -> JsValue {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// Call a function on the back-end.
|
||||
/// Returns null (None) if this fails for any reason.
|
||||
#[wasm_bindgen]
|
||||
pub async fn call_backend(name: String, parameters: Vec<JsValue>) -> JsValue {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::log_1(&format!(
|
||||
"call_backend({}, [params; {}])",
|
||||
name,
|
||||
parameters.len()
|
||||
).into());
|
||||
let next_id = increment_id();
|
||||
let mut params = Vec::with_capacity(parameters.len());
|
||||
for val in parameters {
|
||||
params.push(convert::js_to_primitive(val));
|
||||
}
|
||||
let port = get_port();
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::log_1(&format!("USDPL: Got port {}", port).into());
|
||||
let results = connection::send_call(
|
||||
next_id,
|
||||
Packet::Call(RemoteCall {
|
||||
id: next_id,
|
||||
function: name.clone(),
|
||||
parameters: params,
|
||||
}),
|
||||
port,
|
||||
#[cfg(feature = "encrypt")]
|
||||
get_key(),
|
||||
)
|
||||
.await;
|
||||
let results = match results {
|
||||
Ok(x) => x,
|
||||
#[allow(unused_variables)]
|
||||
Err(e) => {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::error_1(&format!("USDPL: Got error while calling {}: {:?}", name, e).into());
|
||||
return JsValue::NULL;
|
||||
}
|
||||
};
|
||||
let results_js = Array::new_with_length(results.len() as _);
|
||||
let mut i = 0;
|
||||
for item in results {
|
||||
results_js.set(i as _, convert::primitive_to_js(item));
|
||||
i += 1;
|
||||
}
|
||||
results_js.into()
|
||||
}
|
||||
|
||||
/// Initialize translation strings for the front-end
|
||||
#[wasm_bindgen]
|
||||
pub async fn init_tr(locale: String) {
|
||||
let next_id = increment_id();
|
||||
match connection::send_recv_packet(
|
||||
next_id,
|
||||
Packet::Language(locale.clone()),
|
||||
get_port(),
|
||||
#[cfg(feature = "encrypt")]
|
||||
get_key(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Packet::Translations(translations)) => {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::log_1(&format!("USDPL: Got translations for {}", locale).into());
|
||||
// convert translations into map
|
||||
let mut tr_map = std::collections::HashMap::with_capacity(translations.len());
|
||||
for (key, val) in translations {
|
||||
tr_map.insert(key, val);
|
||||
}
|
||||
unsafe { TRANSLATIONS = Some(tr_map) }
|
||||
}
|
||||
Ok(_) => {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::error_1(&format!("USDPL: Got wrong packet response for init_tr").into());
|
||||
unsafe { TRANSLATIONS = None }
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
Err(e) => {
|
||||
#[cfg(feature = "debug")]
|
||||
web_sys::console::error_1(&format!("USDPL: Got wrong error for init_tr: {:#?}", e).into());
|
||||
unsafe { TRANSLATIONS = None }
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
/// Translate a phrase, equivalent to tr_n(msg_id, 0)
|
||||
#[wasm_bindgen]
|
||||
pub fn tr(msg_id: String) -> String {
|
||||
|
|
Loading…
Reference in a new issue