Improve flexibility of WASM conversions in code gen
This commit is contained in:
parent
febaafe50c
commit
0b44ebc12b
33 changed files with 1028 additions and 545 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1546,7 +1546,6 @@ dependencies = [
|
||||||
"nrpc 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nrpc 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"obfstr",
|
"obfstr",
|
||||||
"prost",
|
"prost",
|
||||||
"usdpl-build",
|
|
||||||
"usdpl-core",
|
"usdpl-core",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
usdpl_build::back::build()
|
|
||||||
}
|
|
|
@ -5,14 +5,10 @@ use std::process::Command;
|
||||||
|
|
||||||
/// The home directory of the user currently running the Steam Deck UI (specifically: running gamescope).
|
/// The home directory of the user currently running the Steam Deck UI (specifically: running gamescope).
|
||||||
pub fn home() -> Option<PathBuf> {
|
pub fn home() -> Option<PathBuf> {
|
||||||
let who_out = Command::new("who")
|
let who_out = Command::new("who").output().ok()?;
|
||||||
.output().ok()?;
|
|
||||||
let who_str = String::from_utf8_lossy(who_out.stdout.as_slice());
|
let who_str = String::from_utf8_lossy(who_out.stdout.as_slice());
|
||||||
for login in who_str.split("\n") {
|
for login in who_str.split("\n") {
|
||||||
let username = login
|
let username = login.split(" ").next()?.trim();
|
||||||
.split(" ")
|
|
||||||
.next()?
|
|
||||||
.trim();
|
|
||||||
let path = Path::new("/home").join(username);
|
let path = Path::new("/home").join(username);
|
||||||
if path.is_dir() {
|
if path.is_dir() {
|
||||||
return Some(path);
|
return Some(path);
|
||||||
|
|
|
@ -7,12 +7,10 @@ pub fn home() -> Option<PathBuf> {
|
||||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
||||||
let result = crate::api_any::dirs::home();
|
let result = crate::api_any::dirs::home();
|
||||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
||||||
let result = crate::api_decky::home().ok()
|
let result = crate::api_decky::home()
|
||||||
.map(|x| PathBuf::from(x)
|
|
||||||
.join("..")
|
|
||||||
.canonicalize()
|
|
||||||
.ok()
|
.ok()
|
||||||
).flatten();
|
.map(|x| PathBuf::from(x).join("..").canonicalize().ok())
|
||||||
|
.flatten();
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
//! Common low-level file operations
|
//! Common low-level file operations
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::path::Path;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write, self};
|
use std::io::{self, Read, Write};
|
||||||
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// Write something to a file.
|
/// Write something to a file.
|
||||||
|
@ -31,14 +31,12 @@ impl<E: std::error::Error> std::fmt::Display for ReadError<E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: std::error::Error> std::error::Error for ReadError<E> {
|
impl<E: std::error::Error> std::error::Error for ReadError<E> {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read something from a file.
|
/// Read something from a file.
|
||||||
/// Useful for kernel configuration files.
|
/// Useful for kernel configuration files.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn read_single<P: AsRef<Path>, D: FromStr<Err=E>, E>(path: P) -> Result<D, ReadError<E>> {
|
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 file = File::open(path).map_err(ReadError::Io)?;
|
||||||
let mut string = String::new();
|
let mut string = String::new();
|
||||||
file.read_to_string(&mut string).map_err(ReadError::Io)?;
|
file.read_to_string(&mut string).map_err(ReadError::Io)?;
|
||||||
|
|
|
@ -5,12 +5,10 @@
|
||||||
//!
|
//!
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
#[cfg(not(any(feature = "decky")))]
|
||||||
mod api_any;
|
mod api_any;
|
||||||
mod api_common;
|
mod api_common;
|
||||||
#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))]
|
#[cfg(all(feature = "decky", not(any(feature = "any"))))]
|
||||||
mod api_crankshaft;
|
|
||||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
|
||||||
mod api_decky;
|
mod api_decky;
|
||||||
|
|
||||||
mod rpc;
|
mod rpc;
|
||||||
|
@ -27,16 +25,16 @@ pub mod api {
|
||||||
pub use super::api_common::*;
|
pub use super::api_common::*;
|
||||||
|
|
||||||
/// Standard interfaces not specific to a single plugin loader
|
/// Standard interfaces not specific to a single plugin loader
|
||||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
#[cfg(not(any(feature = "decky")))]
|
||||||
pub mod any { pub use super::super::api_any::*; }
|
pub mod any {
|
||||||
|
pub use super::super::api_any::*;
|
||||||
/// Crankshaft-specific interfaces (FIXME)
|
}
|
||||||
#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))]
|
|
||||||
pub mod crankshaft { pub use super::super::api_crankshaft::*; }
|
|
||||||
|
|
||||||
/// Decky-specific interfaces
|
/// Decky-specific interfaces
|
||||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
#[cfg(all(feature = "decky", not(any(feature = "any"))))]
|
||||||
pub mod decky { pub use super::super::api_decky::*; }
|
pub mod decky {
|
||||||
|
pub use super::super::api_decky::*;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// usdpl-core re-export
|
/// usdpl-core re-export
|
||||||
|
@ -49,9 +47,9 @@ pub mod nrpc {
|
||||||
pub use nrpc::*;
|
pub use nrpc::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// nRPC-generated exports
|
/*/// nRPC-generated exports
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub mod services {
|
pub mod services {
|
||||||
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
||||||
}
|
}*/
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
use async_lock::Mutex;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use async_lock::Mutex;
|
|
||||||
|
|
||||||
use nrpc::{ServerService, ServiceError};
|
use nrpc::{ServerService, ServiceError};
|
||||||
|
|
||||||
|
@ -19,7 +19,12 @@ impl<'a> ServiceRegistry<'a> {
|
||||||
format!("{}.{}", package, service)
|
format!("{}.{}", package, service)
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
pub async fn call_descriptor(&self, descriptor: &str, method: &str, data: bytes::Bytes) -> Result<bytes::Bytes, ServiceError> {
|
pub async fn call_descriptor(
|
||||||
|
&self,
|
||||||
|
descriptor: &str,
|
||||||
|
method: &str,
|
||||||
|
data: bytes::Bytes,
|
||||||
|
) -> Result<bytes::Bytes, ServiceError> {
|
||||||
if let Some(service) = self.entries.get(descriptor) {
|
if let Some(service) = self.entries.get(descriptor) {
|
||||||
let mut output = bytes::BytesMut::new();
|
let mut output = bytes::BytesMut::new();
|
||||||
let mut service_lock = service.lock_arc().await;
|
let mut service_lock = service.lock_arc().await;
|
||||||
|
@ -32,7 +37,8 @@ impl<'a> ServiceRegistry<'a> {
|
||||||
|
|
||||||
pub fn register<S: ServerService + Send + 'a>(&mut self, service: S) -> &mut Self {
|
pub fn register<S: ServerService + Send + 'a>(&mut self, service: S) -> &mut Self {
|
||||||
let key = service.descriptor().to_owned();
|
let key = service.descriptor().to_owned();
|
||||||
self.entries.insert(key, Arc::new(Mutex::new(Box::new(service))));
|
self.entries
|
||||||
|
.insert(key, Arc::new(Mutex::new(Box::new(service))));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,10 @@ impl WebsocketServer {
|
||||||
runner.block_on(self.run())
|
runner.block_on(self.run())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn connection_handler(services: ServiceRegistry<'static>, stream: TcpStream) -> Result<(), RatchetError> {
|
async fn connection_handler(
|
||||||
|
services: ServiceRegistry<'static>,
|
||||||
|
stream: TcpStream,
|
||||||
|
) -> Result<(), RatchetError> {
|
||||||
let upgraded = ratchet_rs::accept_with(
|
let upgraded = ratchet_rs::accept_with(
|
||||||
stream,
|
stream,
|
||||||
WebSocketConfig::default(),
|
WebSocketConfig::default(),
|
||||||
|
@ -82,19 +85,27 @@ impl WebsocketServer {
|
||||||
let mut buf = BytesMut::new();
|
let mut buf = BytesMut::new();
|
||||||
loop {
|
loop {
|
||||||
match websocket.read(&mut buf).await? {
|
match websocket.read(&mut buf).await? {
|
||||||
Message::Text => return Err(RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, "Websocket text messages are not accepted")),
|
Message::Text => {
|
||||||
|
return Err(RatchetError::with_cause(
|
||||||
|
ratchet_rs::ErrorKind::Protocol,
|
||||||
|
"Websocket text messages are not accepted",
|
||||||
|
))
|
||||||
|
}
|
||||||
Message::Binary => {
|
Message::Binary => {
|
||||||
let response = services.call_descriptor(
|
let response = services
|
||||||
|
.call_descriptor(
|
||||||
descriptor.service,
|
descriptor.service,
|
||||||
descriptor.method,
|
descriptor.method,
|
||||||
buf.clone().freeze()
|
buf.clone().freeze(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, e.to_string()))?;
|
.map_err(|e| {
|
||||||
|
RatchetError::with_cause(ratchet_rs::ErrorKind::Protocol, e.to_string())
|
||||||
|
})?;
|
||||||
websocket.write_binary(response).await?;
|
websocket.write_binary(response).await?;
|
||||||
},
|
}
|
||||||
Message::Ping(x) => websocket.write_pong(x).await?,
|
Message::Ping(x) => websocket.write_pong(x).await?,
|
||||||
Message::Pong(_) => {},
|
Message::Pong(_) => {}
|
||||||
Message::Close(_) => break,
|
Message::Close(_) => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,10 +117,7 @@ impl WebsocketServer {
|
||||||
if let Some(service) = iter.next() {
|
if let Some(service) = iter.next() {
|
||||||
if let Some(method) = iter.next() {
|
if let Some(method) = iter.next() {
|
||||||
if iter.next().is_none() {
|
if iter.next().is_none() {
|
||||||
return Ok(MethodDescriptor {
|
return Ok(MethodDescriptor { service, method });
|
||||||
service,
|
|
||||||
method
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
Err("URL path has too many separators")
|
Err("URL path has too many separators")
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,4 +22,6 @@ message LogMessage {
|
||||||
string msg = 2;
|
string msg = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Empty {}
|
message Empty {
|
||||||
|
bool ok = 1;
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
pub fn build() {
|
pub fn build(
|
||||||
|
custom_protos: impl Iterator<Item = String>,
|
||||||
|
custom_dirs: impl Iterator<Item = String>,
|
||||||
|
) {
|
||||||
crate::dump_protos_out().unwrap();
|
crate::dump_protos_out().unwrap();
|
||||||
nrpc_build::compile_servers(
|
nrpc_build::compile_servers(
|
||||||
crate::all_proto_filenames(crate::proto_builtins_out_path()),
|
crate::all_proto_filenames(crate::proto_builtins_out_path(), custom_protos),
|
||||||
crate::proto_out_paths()
|
crate::proto_out_paths(custom_dirs),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,16 +7,24 @@ pub use service_generator::WasmServiceGenerator;
|
||||||
mod shared_state;
|
mod shared_state;
|
||||||
pub(crate) use shared_state::SharedState;
|
pub(crate) use shared_state::SharedState;
|
||||||
|
|
||||||
pub fn build() {
|
pub fn build(
|
||||||
|
custom_protos: impl Iterator<Item = String>,
|
||||||
|
custom_dirs: impl Iterator<Item = String>,
|
||||||
|
) {
|
||||||
let shared_state = SharedState::new();
|
let shared_state = SharedState::new();
|
||||||
crate::dump_protos_out().unwrap();
|
crate::dump_protos_out().unwrap();
|
||||||
nrpc_build::Transpiler::new(
|
nrpc_build::Transpiler::new(
|
||||||
crate::all_proto_filenames(crate::proto_builtins_out_path()),
|
crate::all_proto_filenames(crate::proto_builtins_out_path(), custom_protos),
|
||||||
crate::proto_out_paths()
|
crate::proto_out_paths(custom_dirs),
|
||||||
).unwrap()
|
)
|
||||||
|
.unwrap()
|
||||||
.generate_client()
|
.generate_client()
|
||||||
.with_preprocessor(nrpc_build::AbstractImpl::outer(WasmProtoPreprocessor::with_state(&shared_state)))
|
.with_preprocessor(nrpc_build::AbstractImpl::outer(
|
||||||
.with_service_generator(nrpc_build::AbstractImpl::outer(WasmServiceGenerator::with_state(&shared_state)))
|
WasmProtoPreprocessor::with_state(&shared_state),
|
||||||
|
))
|
||||||
|
.with_service_generator(nrpc_build::AbstractImpl::outer(
|
||||||
|
WasmServiceGenerator::with_state(&shared_state),
|
||||||
|
))
|
||||||
.transpile()
|
.transpile()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,7 @@ impl WasmProtoPreprocessor {
|
||||||
|
|
||||||
impl IPreprocessor for WasmProtoPreprocessor {
|
impl IPreprocessor for WasmProtoPreprocessor {
|
||||||
fn process(&mut self, fds: &mut FileDescriptorSet) -> proc_macro2::TokenStream {
|
fn process(&mut self, fds: &mut FileDescriptorSet) -> proc_macro2::TokenStream {
|
||||||
self.shared.lock()
|
self.shared.lock().expect("Cannot lock shared state").fds = Some(fds.clone());
|
||||||
.expect("Cannot lock shared state")
|
quote::quote! {}
|
||||||
.fds = Some(fds.clone());
|
|
||||||
quote::quote!{}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,9 +7,7 @@ pub struct SharedState(Arc<Mutex<SharedProtoData>>);
|
||||||
|
|
||||||
impl SharedState {
|
impl SharedState {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self(Arc::new(Mutex::new(SharedProtoData {
|
Self(Arc::new(Mutex::new(SharedProtoData { fds: None })))
|
||||||
fds: None,
|
|
||||||
})))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,6 @@ pub mod back;
|
||||||
pub mod front;
|
pub mod front;
|
||||||
|
|
||||||
mod proto_files;
|
mod proto_files;
|
||||||
pub use proto_files::{dump_protos, dump_protos_out, proto_out_paths, all_proto_filenames, proto_builtins_out_path};
|
pub use proto_files::{
|
||||||
|
all_proto_filenames, dump_protos, dump_protos_out, proto_builtins_out_path, proto_out_paths,
|
||||||
|
};
|
||||||
|
|
|
@ -17,26 +17,25 @@ const TRANSLATIONS_PROTO: IncludedFileStr<'static> = IncludedFileStr {
|
||||||
contents: include_str!("../protos/translations.proto"),
|
contents: include_str!("../protos/translations.proto"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const ALL_PROTOS: [IncludedFileStr<'static>; 2] = [
|
const ALL_PROTOS: [IncludedFileStr<'static>; 2] = [DEBUG_PROTO, TRANSLATIONS_PROTO];
|
||||||
DEBUG_PROTO,
|
|
||||||
TRANSLATIONS_PROTO,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub fn proto_builtins_out_path() -> PathBuf {
|
pub fn proto_builtins_out_path() -> PathBuf {
|
||||||
PathBuf::from(std::env::var("OUT_DIR").expect("Not in a build.rs context (missing $OUT_DIR)")).join("protos")
|
PathBuf::from(std::env::var("OUT_DIR").expect("Not in a build.rs context (missing $OUT_DIR)"))
|
||||||
|
.join("protos")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn proto_out_paths() -> impl Iterator<Item = String> {
|
pub fn proto_out_paths(additionals: impl Iterator<Item = String>) -> impl Iterator<Item = String> {
|
||||||
std::iter::once(proto_builtins_out_path())
|
std::iter::once(proto_builtins_out_path())
|
||||||
.map(|x| x.to_str().unwrap().to_owned())
|
.map(|x| x.to_str().unwrap().to_owned())
|
||||||
.chain(custom_protos_dirs().into_iter())
|
.chain(custom_protos_dirs(additionals).into_iter())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn custom_protos_dirs() -> Vec<String> {
|
fn custom_protos_dirs(additionals: impl Iterator<Item = String>) -> Vec<String> {
|
||||||
let dirs = std::env::var(ADDITIONAL_PROTOBUFS_ENV_VAR).unwrap_or_else(|_| "".to_owned());
|
let dirs = std::env::var(ADDITIONAL_PROTOBUFS_ENV_VAR).unwrap_or_else(|_| "".to_owned());
|
||||||
dirs.split(':')
|
dirs.split(':')
|
||||||
.filter(|x| std::fs::read_dir(x).is_ok())
|
.filter(|x| std::fs::read_dir(x).is_ok())
|
||||||
.map(|x| x.to_owned())
|
.map(|x| x.to_owned())
|
||||||
|
.chain(additionals)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,14 +47,27 @@ fn custom_protos_filenames() -> Vec<String> {
|
||||||
.flat_map(|x| x.unwrap())
|
.flat_map(|x| x.unwrap())
|
||||||
.filter(|x| x.is_ok())
|
.filter(|x| x.is_ok())
|
||||||
.map(|x| x.unwrap().path())
|
.map(|x| x.unwrap().path())
|
||||||
.filter(|x| if let Some(ext) = x.extension() { ext.to_ascii_lowercase() == "proto" && x.is_file() } else { false })
|
.filter(|x| {
|
||||||
|
if let Some(ext) = x.extension() {
|
||||||
|
ext.to_ascii_lowercase() == "proto" && x.is_file()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
})
|
||||||
.filter_map(|x| x.to_str().map(|x| x.to_owned()))
|
.filter_map(|x| x.to_str().map(|x| x.to_owned()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_proto_filenames(p: impl AsRef<Path> + 'static) -> impl Iterator<Item = String> {
|
pub fn all_proto_filenames(
|
||||||
|
p: impl AsRef<Path> + 'static,
|
||||||
|
additionals: impl Iterator<Item = String>,
|
||||||
|
) -> impl Iterator<Item = String> {
|
||||||
//let p = p.as_ref();
|
//let p = p.as_ref();
|
||||||
ALL_PROTOS.iter().map(move |x| p.as_ref().join(x.filename).to_str().unwrap().to_owned()).chain(custom_protos_filenames())
|
ALL_PROTOS
|
||||||
|
.iter()
|
||||||
|
.map(move |x| p.as_ref().join(x.filename).to_str().unwrap().to_owned())
|
||||||
|
.chain(custom_protos_filenames())
|
||||||
|
.chain(additionals)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump_protos(p: impl AsRef<Path>) -> std::io::Result<()> {
|
pub fn dump_protos(p: impl AsRef<Path>) -> std::io::Result<()> {
|
||||||
|
|
|
@ -4,12 +4,10 @@
|
||||||
|
|
||||||
mod remote_call;
|
mod remote_call;
|
||||||
|
|
||||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
#[cfg(not(any(feature = "decky")))]
|
||||||
mod api_any;
|
mod api_any;
|
||||||
mod api_common;
|
mod api_common;
|
||||||
#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))]
|
#[cfg(all(feature = "decky", not(any(feature = "any"))))]
|
||||||
mod api_crankshaft;
|
|
||||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
|
||||||
mod api_decky;
|
mod api_decky;
|
||||||
|
|
||||||
pub mod serdes;
|
pub mod serdes;
|
||||||
|
@ -20,11 +18,9 @@ pub use remote_call::{RemoteCall, RemoteCallResponse};
|
||||||
/// USDPL core API.
|
/// USDPL core API.
|
||||||
/// This contains functionality used in both the back-end and front-end.
|
/// This contains functionality used in both the back-end and front-end.
|
||||||
pub mod api {
|
pub mod api {
|
||||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
#[cfg(not(any(feature = "decky")))]
|
||||||
pub use super::api_any::*;
|
pub use super::api_any::*;
|
||||||
pub use super::api_common::*;
|
pub use super::api_common::*;
|
||||||
#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))]
|
#[cfg(all(feature = "decky", not(any(feature = "any"))))]
|
||||||
pub use super::api_crankshaft::*;
|
|
||||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
|
||||||
pub use super::api_decky::*;
|
pub use super::api_decky::*;
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn remote_call_idempotence_test() {
|
fn remote_call_idempotence_test() {
|
||||||
let call = RemoteCall{
|
let call = RemoteCall {
|
||||||
id: 42,
|
id: 42,
|
||||||
function: "something very long just in case this causes unexpected issues".into(),
|
function: "something very long just in case this causes unexpected issues".into(),
|
||||||
parameters: vec!["param1".into(), 42f64.into()],
|
parameters: vec!["param1".into(), 42f64.into()],
|
||||||
|
@ -88,7 +88,10 @@ mod tests {
|
||||||
assert_eq!(len, loaded_len, "Expected load and dump lengths to match");
|
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.id, call.id, "RemoteCall.id does not match");
|
||||||
assert_eq!(loaded_call.function, call.function, "RemoteCall.function 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(loaded) = &loaded_call.parameters[0] {
|
||||||
if let Primitive::String(original) = &call.parameters[0] {
|
if let Primitive::String(original) = &call.parameters[0] {
|
||||||
assert_eq!(loaded, original, "RemoteCall.parameters[0] does not match");
|
assert_eq!(loaded, original, "RemoteCall.parameters[0] does not match");
|
||||||
|
|
|
@ -26,43 +26,34 @@ impl<T: Dumpable> Dumpable for Vec<T> {
|
||||||
|
|
||||||
impl<T0: Dumpable, T1: Dumpable> Dumpable for (T0, T1) {
|
impl<T0: Dumpable, T1: Dumpable> Dumpable for (T0, T1) {
|
||||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||||
Ok(
|
Ok(self.0.dump(buffer)? + self.1.dump(buffer)?)
|
||||||
self.0.dump(buffer)?
|
|
||||||
+ self.1.dump(buffer)?
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T0: Dumpable, T1: Dumpable, T2: Dumpable> Dumpable for (T0, T1, T2) {
|
impl<T0: Dumpable, T1: Dumpable, T2: Dumpable> Dumpable for (T0, T1, T2) {
|
||||||
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||||
Ok(
|
Ok(self.0.dump(buffer)? + self.1.dump(buffer)? + self.2.dump(buffer)?)
|
||||||
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) {
|
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> {
|
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||||
Ok(
|
Ok(self.0.dump(buffer)?
|
||||||
self.0.dump(buffer)?
|
|
||||||
+ self.1.dump(buffer)?
|
+ self.1.dump(buffer)?
|
||||||
+ self.2.dump(buffer)?
|
+ self.2.dump(buffer)?
|
||||||
+ self.3.dump(buffer)?
|
+ self.3.dump(buffer)?)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T0: Dumpable, T1: Dumpable, T2: Dumpable, T3: Dumpable, T4: Dumpable> Dumpable for (T0, T1, T2, T3, T4) {
|
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> {
|
fn dump(&self, buffer: &mut dyn Write) -> Result<usize, DumpError> {
|
||||||
Ok(
|
Ok(self.0.dump(buffer)?
|
||||||
self.0.dump(buffer)?
|
|
||||||
+ self.1.dump(buffer)?
|
+ self.1.dump(buffer)?
|
||||||
+ self.2.dump(buffer)?
|
+ self.2.dump(buffer)?
|
||||||
+ self.3.dump(buffer)?
|
+ self.3.dump(buffer)?
|
||||||
+ self.4.dump(buffer)?
|
+ self.4.dump(buffer)?)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,10 +42,7 @@ impl<T0: Loadable, T1: Loadable> Loadable for (T0, T1) {
|
||||||
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||||
let (t0, len0) = T0::load(buffer)?;
|
let (t0, len0) = T0::load(buffer)?;
|
||||||
let (t1, len1) = T1::load(buffer)?;
|
let (t1, len1) = T1::load(buffer)?;
|
||||||
Ok((
|
Ok(((t0, t1), len0 + len1))
|
||||||
(t0, t1),
|
|
||||||
len0 + len1
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,10 +51,7 @@ impl<T0: Loadable, T1: Loadable, T2: Loadable> Loadable for (T0, T1, T2) {
|
||||||
let (t0, len0) = T0::load(buffer)?;
|
let (t0, len0) = T0::load(buffer)?;
|
||||||
let (t1, len1) = T1::load(buffer)?;
|
let (t1, len1) = T1::load(buffer)?;
|
||||||
let (t2, len2) = T2::load(buffer)?;
|
let (t2, len2) = T2::load(buffer)?;
|
||||||
Ok((
|
Ok(((t0, t1, t2), len0 + len1 + len2))
|
||||||
(t0, t1, t2),
|
|
||||||
len0 + len1 + len2
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,24 +61,20 @@ impl<T0: Loadable, T1: Loadable, T2: Loadable, T3: Loadable> Loadable for (T0, T
|
||||||
let (t1, len1) = T1::load(buffer)?;
|
let (t1, len1) = T1::load(buffer)?;
|
||||||
let (t2, len2) = T2::load(buffer)?;
|
let (t2, len2) = T2::load(buffer)?;
|
||||||
let (t3, len3) = T3::load(buffer)?;
|
let (t3, len3) = T3::load(buffer)?;
|
||||||
Ok((
|
Ok(((t0, t1, t2, t3), len0 + len1 + len2 + len3))
|
||||||
(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) {
|
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> {
|
fn load(buffer: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||||
let (t0, len0) = T0::load(buffer)?;
|
let (t0, len0) = T0::load(buffer)?;
|
||||||
let (t1, len1) = T1::load(buffer)?;
|
let (t1, len1) = T1::load(buffer)?;
|
||||||
let (t2, len2) = T2::load(buffer)?;
|
let (t2, len2) = T2::load(buffer)?;
|
||||||
let (t3, len3) = T3::load(buffer)?;
|
let (t3, len3) = T3::load(buffer)?;
|
||||||
let (t4, len4) = T4::load(buffer)?;
|
let (t4, len4) = T4::load(buffer)?;
|
||||||
Ok((
|
Ok(((t0, t1, t2, t3, t4), len0 + len1 + len2 + len3 + len4))
|
||||||
(t0, t1, t2, t3, t4),
|
|
||||||
len0 + len1 + len2 + len3 + len4
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::io::{Read, Write};
|
|
||||||
use super::{DumpError, Dumpable, LoadError, Loadable};
|
use super::{DumpError, Dumpable, LoadError, Loadable};
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
/// Primitive types supported for communication between the USDPL back- and front-end.
|
/// Primitive types supported for communication between the USDPL back- and front-end.
|
||||||
/// These are used for sending over the TCP connection.
|
/// These are used for sending over the TCP connection.
|
||||||
|
@ -47,7 +47,8 @@ impl Primitive {
|
||||||
impl Loadable for Primitive {
|
impl Loadable for Primitive {
|
||||||
fn load(buf: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
fn load(buf: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||||
let mut discriminant_buf = [u8::MAX; 1];
|
let mut discriminant_buf = [u8::MAX; 1];
|
||||||
buf.read_exact(&mut discriminant_buf).map_err(LoadError::Io)?;
|
buf.read_exact(&mut discriminant_buf)
|
||||||
|
.map_err(LoadError::Io)?;
|
||||||
let mut result: (Self, usize) = match discriminant_buf[0] {
|
let mut result: (Self, usize) = match discriminant_buf[0] {
|
||||||
//0 => (None, 0),
|
//0 => (None, 0),
|
||||||
1 => (Self::Empty, 0),
|
1 => (Self::Empty, 0),
|
||||||
|
@ -105,7 +106,7 @@ macro_rules! into_impl {
|
||||||
Primitive::$variant(self)
|
Primitive::$variant(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
into_impl! {String, String}
|
into_impl! {String, String}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::io::{Read, Write, Cursor};
|
|
||||||
use base64::{decode_config_buf, encode_config_buf, Config};
|
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);
|
const B64_CONF: Config = Config::new(base64::CharacterSet::Standard, true);
|
||||||
|
|
||||||
|
@ -49,8 +49,7 @@ pub trait Loadable: Sized {
|
||||||
/// Load data from a base64-encoded buffer
|
/// Load data from a base64-encoded buffer
|
||||||
fn load_base64(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
fn load_base64(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||||
let mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
let mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
||||||
decode_config_buf(buffer, B64_CONF, &mut buffer2)
|
decode_config_buf(buffer, B64_CONF, &mut buffer2).map_err(|_| LoadError::InvalidData)?;
|
||||||
.map_err(|_| LoadError::InvalidData)?;
|
|
||||||
let mut cursor = Cursor::new(buffer2);
|
let mut cursor = Cursor::new(buffer2);
|
||||||
Self::load(&mut cursor)
|
Self::load(&mut cursor)
|
||||||
}
|
}
|
||||||
|
@ -66,7 +65,9 @@ pub trait Loadable: Sized {
|
||||||
base64::decode_config_buf(buffer, B64_CONF, &mut decoded_buf)
|
base64::decode_config_buf(buffer, B64_CONF, &mut decoded_buf)
|
||||||
.map_err(|_| LoadError::InvalidData)?;
|
.map_err(|_| LoadError::InvalidData)?;
|
||||||
//println!("Decoded buf: {:?}", decoded_buf);
|
//println!("Decoded buf: {:?}", decoded_buf);
|
||||||
cipher.decrypt_in_place(nonce, ASSOCIATED_DATA, &mut decoded_buf).map_err(|_| LoadError::DecryptionError)?;
|
cipher
|
||||||
|
.decrypt_in_place(nonce, ASSOCIATED_DATA, &mut decoded_buf)
|
||||||
|
.map_err(|_| LoadError::DecryptionError)?;
|
||||||
//println!("Decrypted buf: {:?}", decoded_buf);
|
//println!("Decrypted buf: {:?}", decoded_buf);
|
||||||
let mut cursor = Cursor::new(decoded_buf);
|
let mut cursor = Cursor::new(decoded_buf);
|
||||||
Self::load(&mut cursor)
|
Self::load(&mut cursor)
|
||||||
|
@ -121,7 +122,12 @@ pub trait Dumpable {
|
||||||
|
|
||||||
/// Dump data as an encrypted base64-encoded buffer
|
/// Dump data as an encrypted base64-encoded buffer
|
||||||
#[cfg(feature = "encrypt")]
|
#[cfg(feature = "encrypt")]
|
||||||
fn dump_encrypted(&self, buffer: &mut Vec<u8>, key: &[u8], nonce: &[u8]) -> Result<usize, DumpError> {
|
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 mut buffer2 = Vec::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
||||||
let size = self.dump(&mut buffer2)?;
|
let size = self.dump(&mut buffer2)?;
|
||||||
buffer2.truncate(size);
|
buffer2.truncate(size);
|
||||||
|
@ -129,7 +135,9 @@ pub trait Dumpable {
|
||||||
let key = aes_gcm_siv::Key::from_slice(key);
|
let key = aes_gcm_siv::Key::from_slice(key);
|
||||||
let cipher = aes_gcm_siv::Aes256GcmSiv::new(key);
|
let cipher = aes_gcm_siv::Aes256GcmSiv::new(key);
|
||||||
let nonce = aes_gcm_siv::Nonce::from_slice(nonce);
|
let nonce = aes_gcm_siv::Nonce::from_slice(nonce);
|
||||||
cipher.encrypt_in_place(nonce, ASSOCIATED_DATA, &mut buffer2).map_err(|_| DumpError::EncryptionError)?;
|
cipher
|
||||||
|
.encrypt_in_place(nonce, ASSOCIATED_DATA, &mut buffer2)
|
||||||
|
.map_err(|_| DumpError::EncryptionError)?;
|
||||||
//println!("Encrypted slice: {:?}", &buffer2);
|
//println!("Encrypted slice: {:?}", &buffer2);
|
||||||
let mut base64_buf = String::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
let mut base64_buf = String::with_capacity(crate::socket::PACKET_BUFFER_SIZE);
|
||||||
encode_config_buf(buffer2.as_slice(), B64_CONF, &mut base64_buf);
|
encode_config_buf(buffer2.as_slice(), B64_CONF, &mut base64_buf);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Web messaging
|
//! Web messaging
|
||||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||||
|
|
||||||
use crate::serdes::{DumpError, Dumpable, LoadError, Loadable};
|
use crate::serdes::{DumpError, Dumpable, LoadError, Loadable};
|
||||||
use crate::{RemoteCall, RemoteCallResponse};
|
use crate::{RemoteCall, RemoteCallResponse};
|
||||||
|
@ -66,7 +66,8 @@ impl Packet {
|
||||||
impl Loadable for Packet {
|
impl Loadable for Packet {
|
||||||
fn load(buf: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
fn load(buf: &mut dyn Read) -> Result<(Self, usize), LoadError> {
|
||||||
let mut discriminant_buf = [u8::MAX; 1];
|
let mut discriminant_buf = [u8::MAX; 1];
|
||||||
buf.read_exact(&mut discriminant_buf).map_err(LoadError::Io)?;
|
buf.read_exact(&mut discriminant_buf)
|
||||||
|
.map_err(LoadError::Io)?;
|
||||||
let mut result: (Self, usize) = match discriminant_buf[0] {
|
let mut result: (Self, usize) = match discriminant_buf[0] {
|
||||||
//0 => (None, 0),
|
//0 => (None, 0),
|
||||||
1 => {
|
1 => {
|
||||||
|
@ -88,15 +89,15 @@ impl Loadable for Packet {
|
||||||
8 => {
|
8 => {
|
||||||
let (obj, len) = <_>::load(buf)?;
|
let (obj, len) = <_>::load(buf)?;
|
||||||
(Self::Many(obj), len)
|
(Self::Many(obj), len)
|
||||||
},
|
}
|
||||||
9 => {
|
9 => {
|
||||||
let (obj, len) = <_>::load(buf)?;
|
let (obj, len) = <_>::load(buf)?;
|
||||||
(Self::Translations(obj), len)
|
(Self::Translations(obj), len)
|
||||||
},
|
}
|
||||||
10 => {
|
10 => {
|
||||||
let (obj, len) = <_>::load(buf)?;
|
let (obj, len) = <_>::load(buf)?;
|
||||||
(Self::Language(obj), len)
|
(Self::Language(obj), len)
|
||||||
},
|
}
|
||||||
_ => return Err(LoadError::InvalidData),
|
_ => return Err(LoadError::InvalidData),
|
||||||
};
|
};
|
||||||
result.1 += 1;
|
result.1 += 1;
|
||||||
|
@ -130,24 +131,39 @@ mod tests {
|
||||||
#[cfg(feature = "encrypt")]
|
#[cfg(feature = "encrypt")]
|
||||||
#[test]
|
#[test]
|
||||||
fn encryption_integration_test() {
|
fn encryption_integration_test() {
|
||||||
let key = hex_literal::hex!("59C4E408F27250B3147E7724511824F1D28ED7BEF43CF7103ACE747F77A2B265");
|
let key =
|
||||||
|
hex_literal::hex!("59C4E408F27250B3147E7724511824F1D28ED7BEF43CF7103ACE747F77A2B265");
|
||||||
let nonce = [0u8; NONCE_SIZE];
|
let nonce = [0u8; NONCE_SIZE];
|
||||||
let packet = Packet::Call(RemoteCall{
|
let packet = Packet::Call(RemoteCall {
|
||||||
id: 42,
|
id: 42,
|
||||||
function: "test".into(),
|
function: "test".into(),
|
||||||
parameters: Vec::new(),
|
parameters: Vec::new(),
|
||||||
});
|
});
|
||||||
let mut buffer = Vec::with_capacity(PACKET_BUFFER_SIZE);
|
let mut buffer = Vec::with_capacity(PACKET_BUFFER_SIZE);
|
||||||
let len = packet.dump_encrypted(&mut buffer, &key, &nonce).unwrap();
|
let len = packet.dump_encrypted(&mut buffer, &key, &nonce).unwrap();
|
||||||
println!("buffer: {}", String::from_utf8(buffer.as_slice()[..len].to_vec()).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();
|
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_out) = packet_out {
|
||||||
if let Packet::Call(call_in) = packet {
|
if let Packet::Call(call_in) = packet {
|
||||||
assert_eq!(call_in.id, call_out.id, "Input and output packets do not match");
|
assert_eq!(
|
||||||
assert_eq!(call_in.function, call_out.function, "Input and output packets do not match");
|
call_in.id, call_out.id,
|
||||||
assert_eq!(call_in.parameters.len(), call_out.parameters.len(), "Input and output packets do not match");
|
"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 {
|
} else {
|
||||||
panic!("Packet in not a Call");
|
panic!("Packet in not a Call");
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,3 @@ prost = "0.11"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasm-bindgen-test = { version = "0.3.13" }
|
wasm-bindgen-test = { version = "0.3.13" }
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
usdpl-build = { version = "0.11", path = "../usdpl-build" }
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
fn main() {
|
|
||||||
usdpl_build::front::build()
|
|
||||||
}
|
|
|
@ -1,20 +1,23 @@
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
use nrpc::{ClientHandler, ServiceError, _helpers::bytes, _helpers::async_trait};
|
|
||||||
use gloo_net::websocket::{Message, futures::WebSocket};
|
|
||||||
use wasm_bindgen_futures::spawn_local;
|
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
|
use gloo_net::websocket::{futures::WebSocket, Message};
|
||||||
|
use nrpc::{ClientHandler, ServiceError, _helpers::async_trait, _helpers::bytes};
|
||||||
|
use wasm_bindgen_futures::spawn_local;
|
||||||
|
|
||||||
static LAST_ID: AtomicU64 = AtomicU64::new(0);
|
static LAST_ID: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
/// Websocket client.
|
||||||
|
/// In most cases, this shouldn't be used directly, but generated code will use this.
|
||||||
pub struct WebSocketHandler {
|
pub struct WebSocketHandler {
|
||||||
// TODO
|
|
||||||
port: u16,
|
port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_recv_ws(url: String, input: bytes::Bytes) -> Result<Vec<u8>, String> {
|
async fn send_recv_ws(url: String, input: bytes::Bytes) -> Result<Vec<u8>, String> {
|
||||||
let mut ws = WebSocket::open(&url).map_err(|e| e.to_string())?;
|
let mut ws = WebSocket::open(&url).map_err(|e| e.to_string())?;
|
||||||
ws.send(Message::Bytes(input.into())).await.map_err(|e| e.to_string())?;
|
ws.send(Message::Bytes(input.into()))
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
read_next_incoming(ws).await
|
read_next_incoming(ws).await
|
||||||
}
|
}
|
||||||
|
@ -42,7 +45,7 @@ impl std::fmt::Display for ErrorStr {
|
||||||
impl std::error::Error for ErrorStr {}
|
impl std::error::Error for ErrorStr {}
|
||||||
|
|
||||||
impl WebSocketHandler {
|
impl WebSocketHandler {
|
||||||
#[allow(dead_code)]
|
/// Instantiate the web socket client for connecting on the specified port
|
||||||
pub fn new(port: u16) -> Self {
|
pub fn new(port: u16) -> Self {
|
||||||
Self { port }
|
Self { port }
|
||||||
}
|
}
|
||||||
|
@ -50,33 +53,29 @@ impl WebSocketHandler {
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl ClientHandler for WebSocketHandler {
|
impl ClientHandler for WebSocketHandler {
|
||||||
async fn call(&mut self,
|
async fn call(
|
||||||
|
&mut self,
|
||||||
package: &str,
|
package: &str,
|
||||||
service: &str,
|
service: &str,
|
||||||
method: &str,
|
method: &str,
|
||||||
input: bytes::Bytes,
|
input: bytes::Bytes,
|
||||||
output: &mut bytes::BytesMut) -> Result<(), ServiceError> {
|
output: &mut bytes::BytesMut,
|
||||||
|
) -> Result<(), ServiceError> {
|
||||||
let id = LAST_ID.fetch_add(1, Ordering::SeqCst);
|
let id = LAST_ID.fetch_add(1, Ordering::SeqCst);
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"ws://usdpl-ws-{}.localhost:{}/{}.{}/{}",
|
"ws://usdpl-ws-{}.localhost:{}/{}.{}/{}",
|
||||||
id,
|
id, self.port, package, service, method,
|
||||||
self.port,
|
|
||||||
package,
|
|
||||||
service,
|
|
||||||
method,
|
|
||||||
);
|
);
|
||||||
let (tx, rx) = async_channel::bounded(1);
|
let (tx, rx) = async_channel::bounded(1);
|
||||||
spawn_local(async move {
|
spawn_local(async move {
|
||||||
tx.send(send_recv_ws(
|
tx.send(send_recv_ws(url, input).await).await.unwrap_or(());
|
||||||
url,
|
|
||||||
input
|
|
||||||
).await).await.unwrap_or(());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
output.extend_from_slice(
|
output.extend_from_slice(
|
||||||
&rx.recv().await
|
&rx.recv()
|
||||||
|
.await
|
||||||
.map_err(|e| ServiceError::Method(Box::new(e)))?
|
.map_err(|e| ServiceError::Method(Box::new(e)))?
|
||||||
.map_err(|e| ServiceError::Method(Box::new(ErrorStr(e))))?
|
.map_err(|e| ServiceError::Method(Box::new(ErrorStr(e))))?,
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,26 +14,33 @@ use usdpl_core::serdes::{Dumpable, Loadable, Primitive};
|
||||||
use usdpl_core::socket;
|
use usdpl_core::socket;
|
||||||
|
|
||||||
#[cfg(feature = "encrypt")]
|
#[cfg(feature = "encrypt")]
|
||||||
const NONCE: [u8; socket::NONCE_SIZE]= [0u8; socket::NONCE_SIZE];
|
const NONCE: [u8; socket::NONCE_SIZE] = [0u8; socket::NONCE_SIZE];
|
||||||
|
|
||||||
pub async fn send_recv_packet(
|
pub async fn send_recv_packet(
|
||||||
id: u64,
|
id: u64,
|
||||||
packet: socket::Packet,
|
packet: socket::Packet,
|
||||||
port: u16,
|
port: u16,
|
||||||
#[cfg(feature = "encrypt")]
|
#[cfg(feature = "encrypt")] key: Vec<u8>,
|
||||||
key: Vec<u8>,
|
|
||||||
) -> Result<socket::Packet, JsValue> {
|
) -> Result<socket::Packet, JsValue> {
|
||||||
|
|
||||||
let mut opts = RequestInit::new();
|
let mut opts = RequestInit::new();
|
||||||
opts.method("POST");
|
opts.method("POST");
|
||||||
opts.mode(RequestMode::Cors);
|
opts.mode(RequestMode::Cors);
|
||||||
|
|
||||||
let url = format!("http://usdpl{}.{}:{}/usdpl/call", id, socket::HOST_STR, port);
|
let url = format!(
|
||||||
|
"http://usdpl{}.{}:{}/usdpl/call",
|
||||||
|
id,
|
||||||
|
socket::HOST_STR,
|
||||||
|
port
|
||||||
|
);
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
let (buffer, len) = dump_to_buffer(packet, #[cfg(feature = "encrypt")] key.as_slice())?;
|
let (buffer, len) = dump_to_buffer(
|
||||||
|
packet,
|
||||||
|
#[cfg(feature = "encrypt")]
|
||||||
|
key.as_slice(),
|
||||||
|
)?;
|
||||||
let string: String = String::from_utf8_lossy(buffer.as_slice()).into();
|
let string: String = String::from_utf8_lossy(buffer.as_slice()).into();
|
||||||
#[cfg(feature="debug")]
|
#[cfg(feature = "debug")]
|
||||||
crate::imports::console_log(&format!("Dumped base64 `{}` len:{}", string, len));
|
crate::imports::console_log(&format!("Dumped base64 `{}` len:{}", string, len));
|
||||||
opts.body(Some(&string.into()));
|
opts.body(Some(&string.into()));
|
||||||
|
|
||||||
|
@ -50,31 +57,46 @@ pub async fn send_recv_packet(
|
||||||
let string: JsString = text.dyn_into()?;
|
let string: JsString = text.dyn_into()?;
|
||||||
|
|
||||||
let rust_str = string.as_string().unwrap();
|
let rust_str = string.as_string().unwrap();
|
||||||
#[cfg(feature="debug")]
|
#[cfg(feature = "debug")]
|
||||||
crate::imports::console_log(&format!("Received base64 `{}` len:{}", rust_str, rust_str.len()));
|
crate::imports::console_log(&format!(
|
||||||
|
"Received base64 `{}` len:{}",
|
||||||
|
rust_str,
|
||||||
|
rust_str.len()
|
||||||
|
));
|
||||||
|
|
||||||
#[cfg(not(feature = "encrypt"))]
|
#[cfg(not(feature = "encrypt"))]
|
||||||
{Ok(socket::Packet::load_base64(rust_str.as_bytes())
|
{
|
||||||
|
Ok(socket::Packet::load_base64(rust_str.as_bytes())
|
||||||
.map_err(super::convert::str_to_js)?
|
.map_err(super::convert::str_to_js)?
|
||||||
.0)}
|
.0)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "encrypt")]
|
#[cfg(feature = "encrypt")]
|
||||||
{Ok(socket::Packet::load_encrypted(rust_str.as_bytes(), key.as_slice(), &NONCE)
|
{
|
||||||
|
Ok(
|
||||||
|
socket::Packet::load_encrypted(rust_str.as_bytes(), key.as_slice(), &NONCE)
|
||||||
.map_err(super::convert::str_to_js)?
|
.map_err(super::convert::str_to_js)?
|
||||||
.0)}
|
.0,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send_call(
|
pub async fn send_call(
|
||||||
id: u64,
|
id: u64,
|
||||||
packet: socket::Packet,
|
packet: socket::Packet,
|
||||||
port: u16,
|
port: u16,
|
||||||
#[cfg(feature = "encrypt")]
|
#[cfg(feature = "encrypt")] key: Vec<u8>,
|
||||||
key: Vec<u8>,
|
|
||||||
) -> Result<Vec<Primitive>, JsValue> {
|
) -> Result<Vec<Primitive>, JsValue> {
|
||||||
let packet = send_recv_packet(id, packet, port, #[cfg(feature = "encrypt")] key).await?;
|
let packet = send_recv_packet(
|
||||||
|
id,
|
||||||
|
packet,
|
||||||
|
port,
|
||||||
|
#[cfg(feature = "encrypt")]
|
||||||
|
key,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
match packet
|
match packet {
|
||||||
{
|
|
||||||
socket::Packet::CallResponse(resp) => Ok(resp.response),
|
socket::Packet::CallResponse(resp) => Ok(resp.response),
|
||||||
_ => {
|
_ => {
|
||||||
//imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url()));
|
//imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url()));
|
||||||
|
|
|
@ -6,13 +6,22 @@
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
mod client_handler;
|
mod client_handler;
|
||||||
|
pub use client_handler::WebSocketHandler;
|
||||||
mod connection;
|
mod connection;
|
||||||
mod convert;
|
mod convert;
|
||||||
mod imports;
|
mod imports;
|
||||||
|
pub mod wasm;
|
||||||
|
|
||||||
#[allow(missing_docs)] // existence is pain otherwise
|
/*#[allow(missing_docs)] // existence is pain otherwise
|
||||||
pub mod _nrpc_js_interop {
|
pub mod _nrpc_js_interop {
|
||||||
include!(concat!(env!("OUT_DIR"), "/usdpl.rs"));
|
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub mod _helpers {
|
||||||
|
pub use js_sys;
|
||||||
|
pub use wasm_bindgen;
|
||||||
|
pub use wasm_bindgen_futures;
|
||||||
}
|
}
|
||||||
|
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
@ -99,7 +108,11 @@ pub fn version_usdpl() -> String {
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn set_value(key: String, value: JsValue) -> JsValue {
|
pub fn set_value(key: String, value: JsValue) -> JsValue {
|
||||||
unsafe {
|
unsafe {
|
||||||
CACHE.as_mut().unwrap().insert(key, value).unwrap_or(JsValue::NULL)
|
CACHE
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.insert(key, value)
|
||||||
|
.unwrap_or(JsValue::NULL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +120,12 @@ pub fn set_value(key: String, value: JsValue) -> JsValue {
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn get_value(key: String) -> JsValue {
|
pub fn get_value(key: String) -> JsValue {
|
||||||
unsafe {
|
unsafe {
|
||||||
CACHE.as_ref().unwrap().get(&key).map(|x| x.to_owned()).unwrap_or(JsValue::UNDEFINED)
|
CACHE
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.get(&key)
|
||||||
|
.map(|x| x.to_owned())
|
||||||
|
.unwrap_or(JsValue::UNDEFINED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +156,7 @@ pub async fn call_backend(name: String, parameters: Vec<JsValue>) -> JsValue {
|
||||||
}),
|
}),
|
||||||
port,
|
port,
|
||||||
#[cfg(feature = "encrypt")]
|
#[cfg(feature = "encrypt")]
|
||||||
get_key()
|
get_key(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let results = match results {
|
let results = match results {
|
||||||
|
@ -168,8 +186,10 @@ pub async fn init_tr(locale: String) {
|
||||||
Packet::Language(locale.clone()),
|
Packet::Language(locale.clone()),
|
||||||
get_port(),
|
get_port(),
|
||||||
#[cfg(feature = "encrypt")]
|
#[cfg(feature = "encrypt")]
|
||||||
get_key()
|
get_key(),
|
||||||
).await {
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(Packet::Translations(translations)) => {
|
Ok(Packet::Translations(translations)) => {
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
imports::console_log(&format!("USDPL: Got translations for {}", locale));
|
imports::console_log(&format!("USDPL: Got translations for {}", locale));
|
||||||
|
@ -179,12 +199,12 @@ pub async fn init_tr(locale: String) {
|
||||||
tr_map.insert(key, val);
|
tr_map.insert(key, val);
|
||||||
}
|
}
|
||||||
unsafe { TRANSLATIONS = Some(tr_map) }
|
unsafe { TRANSLATIONS = Some(tr_map) }
|
||||||
},
|
}
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
imports::console_error(&format!("USDPL: Got wrong packet response for init_tr"));
|
imports::console_error(&format!("USDPL: Got wrong packet response for init_tr"));
|
||||||
unsafe { TRANSLATIONS = None }
|
unsafe { TRANSLATIONS = None }
|
||||||
},
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
|
|
94
usdpl-front/src/wasm/arrays.rs
Normal file
94
usdpl-front/src/wasm/arrays.rs
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
use js_sys::Array;
|
||||||
|
|
||||||
|
use super::{FromWasmable, IntoWasmable};
|
||||||
|
|
||||||
|
macro_rules! numbers_array {
|
||||||
|
($num_ty: ident) => {
|
||||||
|
impl FromWasmable<Array> for Vec<$num_ty> {
|
||||||
|
fn from_wasm(js: Array) -> Self {
|
||||||
|
let mut result = Vec::with_capacity(js.length() as usize);
|
||||||
|
js.for_each(&mut |val, _index, _arr| {
|
||||||
|
// according to MDN, this is guaranteed to be in order so index can be ignored
|
||||||
|
if let Some(val) = val.as_f64() {
|
||||||
|
result.push(val as $num_ty);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoWasmable<Array> for Vec<$num_ty> {
|
||||||
|
fn into_wasm(self) -> Array {
|
||||||
|
let result = Array::new();
|
||||||
|
for val in self {
|
||||||
|
result.push(&val.into());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
numbers_array! { f64 }
|
||||||
|
numbers_array! { f32 }
|
||||||
|
|
||||||
|
numbers_array! { isize }
|
||||||
|
numbers_array! { usize }
|
||||||
|
|
||||||
|
numbers_array! { i8 }
|
||||||
|
numbers_array! { i16 }
|
||||||
|
numbers_array! { i32 }
|
||||||
|
numbers_array! { i64 }
|
||||||
|
numbers_array! { i128 }
|
||||||
|
|
||||||
|
numbers_array! { u8 }
|
||||||
|
numbers_array! { u16 }
|
||||||
|
numbers_array! { u32 }
|
||||||
|
numbers_array! { u64 }
|
||||||
|
numbers_array! { u128 }
|
||||||
|
|
||||||
|
impl FromWasmable<Array> for Vec<String> {
|
||||||
|
fn from_wasm(js: Array) -> Self {
|
||||||
|
let mut result = Vec::with_capacity(js.length() as usize);
|
||||||
|
js.for_each(&mut |val, _index, _arr| {
|
||||||
|
// according to MDN, this is guaranteed to be in order so index can be ignored
|
||||||
|
if let Some(val) = val.as_string() {
|
||||||
|
result.push(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoWasmable<Array> for Vec<String> {
|
||||||
|
fn into_wasm(self) -> Array {
|
||||||
|
let result = Array::new();
|
||||||
|
for val in self {
|
||||||
|
result.push(&val.into());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmable<Array> for Vec<bool> {
|
||||||
|
fn from_wasm(js: Array) -> Self {
|
||||||
|
let mut result = Vec::with_capacity(js.length() as usize);
|
||||||
|
js.for_each(&mut |val, _index, _arr| {
|
||||||
|
// according to MDN, this is guaranteed to be in order so index can be ignored
|
||||||
|
if let Some(val) = val.as_bool() {
|
||||||
|
result.push(val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoWasmable<Array> for Vec<bool> {
|
||||||
|
fn into_wasm(self) -> Array {
|
||||||
|
let result = Array::new();
|
||||||
|
for val in self {
|
||||||
|
result.push(&val.into());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
99
usdpl-front/src/wasm/maps.rs
Normal file
99
usdpl-front/src/wasm/maps.rs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use js_sys::Map;
|
||||||
|
|
||||||
|
use super::{FromWasmable, IntoWasmable};
|
||||||
|
|
||||||
|
macro_rules! numbers_map {
|
||||||
|
($num_ty: ident) => {
|
||||||
|
impl FromWasmable<Map> for HashMap<String, $num_ty> {
|
||||||
|
fn from_wasm(js: Map) -> Self {
|
||||||
|
let mut result = HashMap::with_capacity(js.size() as usize);
|
||||||
|
js.for_each(&mut |key, val| {
|
||||||
|
if let Some(key) = key.as_string() {
|
||||||
|
if let Some(val) = val.as_f64() {
|
||||||
|
result.insert(key, val as $num_ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoWasmable<Map> for HashMap<String, $num_ty> {
|
||||||
|
fn into_wasm(self) -> Map {
|
||||||
|
let result = Map::new();
|
||||||
|
for (key, val) in self {
|
||||||
|
result.set(&key.into(), &val.into());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
numbers_map! { f64 }
|
||||||
|
numbers_map! { f32 }
|
||||||
|
|
||||||
|
numbers_map! { isize }
|
||||||
|
numbers_map! { usize }
|
||||||
|
|
||||||
|
numbers_map! { i8 }
|
||||||
|
numbers_map! { i16 }
|
||||||
|
numbers_map! { i32 }
|
||||||
|
numbers_map! { i64 }
|
||||||
|
numbers_map! { i128 }
|
||||||
|
|
||||||
|
numbers_map! { u8 }
|
||||||
|
numbers_map! { u16 }
|
||||||
|
numbers_map! { u32 }
|
||||||
|
numbers_map! { u64 }
|
||||||
|
numbers_map! { u128 }
|
||||||
|
|
||||||
|
impl FromWasmable<Map> for HashMap<String, String> {
|
||||||
|
fn from_wasm(js: Map) -> Self {
|
||||||
|
let mut result = HashMap::with_capacity(js.size() as usize);
|
||||||
|
js.for_each(&mut |key, val| {
|
||||||
|
if let Some(key) = key.as_string() {
|
||||||
|
if let Some(val) = val.as_string() {
|
||||||
|
result.insert(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoWasmable<Map> for HashMap<String, String> {
|
||||||
|
fn into_wasm(self) -> Map {
|
||||||
|
let result = Map::new();
|
||||||
|
for (key, val) in self {
|
||||||
|
result.set(&key.into(), &val.into());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWasmable<Map> for HashMap<String, bool> {
|
||||||
|
fn from_wasm(js: Map) -> Self {
|
||||||
|
let mut result = HashMap::with_capacity(js.size() as usize);
|
||||||
|
js.for_each(&mut |key, val| {
|
||||||
|
if let Some(key) = key.as_string() {
|
||||||
|
if let Some(val) = val.as_bool() {
|
||||||
|
result.insert(key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoWasmable<Map> for HashMap<String, bool> {
|
||||||
|
fn into_wasm(self) -> Map {
|
||||||
|
let result = Map::new();
|
||||||
|
for (key, val) in self {
|
||||||
|
result.set(&key.into(), &val.into());
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
7
usdpl-front/src/wasm/mod.rs
Normal file
7
usdpl-front/src/wasm/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
//! WASM <-> Rust interop utilities
|
||||||
|
mod arrays;
|
||||||
|
mod maps;
|
||||||
|
mod trivials;
|
||||||
|
mod wasm_traits;
|
||||||
|
|
||||||
|
pub use wasm_traits::*;
|
40
usdpl-front/src/wasm/trivials.rs
Normal file
40
usdpl-front/src/wasm/trivials.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use super::{FromWasmable, IntoWasmable};
|
||||||
|
|
||||||
|
macro_rules! trivial_convert {
|
||||||
|
($ty: ty) => {
|
||||||
|
impl FromWasmable<$ty> for $ty {
|
||||||
|
fn from_wasm(js: $ty) -> Self {
|
||||||
|
js
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoWasmable<$ty> for $ty {
|
||||||
|
fn into_wasm(self) -> $ty {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
trivial_convert! { f64 }
|
||||||
|
trivial_convert! { f32 }
|
||||||
|
|
||||||
|
trivial_convert! { isize }
|
||||||
|
trivial_convert! { usize }
|
||||||
|
|
||||||
|
trivial_convert! { i8 }
|
||||||
|
trivial_convert! { i16 }
|
||||||
|
trivial_convert! { i32 }
|
||||||
|
trivial_convert! { i64 }
|
||||||
|
trivial_convert! { i128 }
|
||||||
|
|
||||||
|
trivial_convert! { u8 }
|
||||||
|
trivial_convert! { u16 }
|
||||||
|
trivial_convert! { u32 }
|
||||||
|
trivial_convert! { u64 }
|
||||||
|
trivial_convert! { u128 }
|
||||||
|
|
||||||
|
trivial_convert! { bool }
|
||||||
|
trivial_convert! { String }
|
||||||
|
|
||||||
|
trivial_convert! { () }
|
40
usdpl-front/src/wasm/wasm_traits.rs
Normal file
40
usdpl-front/src/wasm/wasm_traits.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/// A Rust type which supports Into/FromWasmAbi or WasmDescribe
|
||||||
|
pub trait KnownWasmCompatible {}
|
||||||
|
|
||||||
|
/// Convert Rust type to WASM-compatible type
|
||||||
|
pub trait IntoWasmable<T: KnownWasmCompatible> {
|
||||||
|
/// Required method
|
||||||
|
fn into_wasm(self) -> T;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert WASM-compatible type to Rust-centric type
|
||||||
|
pub trait FromWasmable<T: KnownWasmCompatible> {
|
||||||
|
/// Required method
|
||||||
|
fn from_wasm(js: T) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KnownWasmCompatible for f64 {}
|
||||||
|
impl KnownWasmCompatible for f32 {}
|
||||||
|
|
||||||
|
impl KnownWasmCompatible for isize {}
|
||||||
|
impl KnownWasmCompatible for usize {}
|
||||||
|
|
||||||
|
impl KnownWasmCompatible for i8 {}
|
||||||
|
impl KnownWasmCompatible for i16 {}
|
||||||
|
impl KnownWasmCompatible for i32 {}
|
||||||
|
impl KnownWasmCompatible for i64 {}
|
||||||
|
impl KnownWasmCompatible for i128 {}
|
||||||
|
|
||||||
|
impl KnownWasmCompatible for u8 {}
|
||||||
|
impl KnownWasmCompatible for u16 {}
|
||||||
|
impl KnownWasmCompatible for u32 {}
|
||||||
|
impl KnownWasmCompatible for u64 {}
|
||||||
|
impl KnownWasmCompatible for u128 {}
|
||||||
|
|
||||||
|
impl KnownWasmCompatible for bool {}
|
||||||
|
impl KnownWasmCompatible for String {}
|
||||||
|
|
||||||
|
impl KnownWasmCompatible for () {}
|
||||||
|
|
||||||
|
impl KnownWasmCompatible for js_sys::Map {}
|
||||||
|
impl KnownWasmCompatible for js_sys::Array {}
|
Loading…
Reference in a new issue