Improve flexibility of WASM conversions in code gen
This commit is contained in:
parent
febaafe50c
commit
0b44ebc12b
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)",
|
||||
"obfstr",
|
||||
"prost",
|
||||
"usdpl-build",
|
||||
"usdpl-core",
|
||||
"wasm-bindgen",
|
||||
"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).
|
||||
pub fn home() -> Option<PathBuf> {
|
||||
let who_out = Command::new("who")
|
||||
.output().ok()?;
|
||||
let who_out = Command::new("who").output().ok()?;
|
||||
let who_str = String::from_utf8_lossy(who_out.stdout.as_slice());
|
||||
for login in who_str.split("\n") {
|
||||
let username = login
|
||||
.split(" ")
|
||||
.next()?
|
||||
.trim();
|
||||
let username = login.split(" ").next()?.trim();
|
||||
let path = Path::new("/home").join(username);
|
||||
if path.is_dir() {
|
||||
return Some(path);
|
||||
|
|
|
@ -7,12 +7,10 @@ pub fn home() -> Option<PathBuf> {
|
|||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
||||
let result = crate::api_any::dirs::home();
|
||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
||||
let result = crate::api_decky::home().ok()
|
||||
.map(|x| PathBuf::from(x)
|
||||
.join("..")
|
||||
.canonicalize()
|
||||
let result = crate::api_decky::home()
|
||||
.ok()
|
||||
).flatten();
|
||||
.map(|x| PathBuf::from(x).join("..").canonicalize().ok())
|
||||
.flatten();
|
||||
|
||||
result
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! Common low-level file operations
|
||||
use std::fmt::Display;
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write, self};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Write something to a file.
|
||||
|
@ -31,9 +31,7 @@ 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.
|
||||
/// Useful for kernel configuration files.
|
||||
|
|
|
@ -5,12 +5,10 @@
|
|||
//!
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
||||
#[cfg(not(any(feature = "decky")))]
|
||||
mod api_any;
|
||||
mod api_common;
|
||||
#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))]
|
||||
mod api_crankshaft;
|
||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
||||
#[cfg(all(feature = "decky", not(any(feature = "any"))))]
|
||||
mod api_decky;
|
||||
|
||||
mod rpc;
|
||||
|
@ -27,16 +25,16 @@ pub mod api {
|
|||
pub use super::api_common::*;
|
||||
|
||||
/// Standard interfaces not specific to a single plugin loader
|
||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
||||
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::*; }
|
||||
#[cfg(not(any(feature = "decky")))]
|
||||
pub mod any {
|
||||
pub use super::super::api_any::*;
|
||||
}
|
||||
|
||||
/// Decky-specific interfaces
|
||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
||||
pub mod decky { pub use super::super::api_decky::*; }
|
||||
#[cfg(all(feature = "decky", not(any(feature = "any"))))]
|
||||
pub mod decky {
|
||||
pub use super::super::api_decky::*;
|
||||
}
|
||||
}
|
||||
|
||||
/// usdpl-core re-export
|
||||
|
@ -49,9 +47,9 @@ pub mod nrpc {
|
|||
pub use nrpc::*;
|
||||
}
|
||||
|
||||
/// nRPC-generated exports
|
||||
/*/// nRPC-generated exports
|
||||
#[allow(missing_docs)]
|
||||
#[allow(dead_code)]
|
||||
pub mod services {
|
||||
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
||||
}
|
||||
}*/
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use async_lock::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use async_lock::Mutex;
|
||||
|
||||
use nrpc::{ServerService, ServiceError};
|
||||
|
||||
|
@ -19,7 +19,12 @@ impl<'a> ServiceRegistry<'a> {
|
|||
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) {
|
||||
let mut output = bytes::BytesMut::new();
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,10 @@ impl WebsocketServer {
|
|||
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(
|
||||
stream,
|
||||
WebSocketConfig::default(),
|
||||
|
@ -82,19 +85,27 @@ impl WebsocketServer {
|
|||
let mut buf = BytesMut::new();
|
||||
loop {
|
||||
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 => {
|
||||
let response = services.call_descriptor(
|
||||
let response = services
|
||||
.call_descriptor(
|
||||
descriptor.service,
|
||||
descriptor.method,
|
||||
buf.clone().freeze()
|
||||
buf.clone().freeze(),
|
||||
)
|
||||
.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?;
|
||||
},
|
||||
}
|
||||
Message::Ping(x) => websocket.write_pong(x).await?,
|
||||
Message::Pong(_) => {},
|
||||
Message::Pong(_) => {}
|
||||
Message::Close(_) => break,
|
||||
}
|
||||
}
|
||||
|
@ -106,10 +117,7 @@ impl WebsocketServer {
|
|||
if let Some(service) = iter.next() {
|
||||
if let Some(method) = iter.next() {
|
||||
if iter.next().is_none() {
|
||||
return Ok(MethodDescriptor {
|
||||
service,
|
||||
method
|
||||
});
|
||||
return Ok(MethodDescriptor { service, method });
|
||||
} else {
|
||||
Err("URL path has too many separators")
|
||||
}
|
||||
|
|
|
@ -22,4 +22,6 @@ message LogMessage {
|
|||
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();
|
||||
nrpc_build::compile_servers(
|
||||
crate::all_proto_filenames(crate::proto_builtins_out_path()),
|
||||
crate::proto_out_paths()
|
||||
crate::all_proto_filenames(crate::proto_builtins_out_path(), custom_protos),
|
||||
crate::proto_out_paths(custom_dirs),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,16 +7,24 @@ pub use service_generator::WasmServiceGenerator;
|
|||
mod shared_state;
|
||||
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();
|
||||
crate::dump_protos_out().unwrap();
|
||||
nrpc_build::Transpiler::new(
|
||||
crate::all_proto_filenames(crate::proto_builtins_out_path()),
|
||||
crate::proto_out_paths()
|
||||
).unwrap()
|
||||
crate::all_proto_filenames(crate::proto_builtins_out_path(), custom_protos),
|
||||
crate::proto_out_paths(custom_dirs),
|
||||
)
|
||||
.unwrap()
|
||||
.generate_client()
|
||||
.with_preprocessor(nrpc_build::AbstractImpl::outer(WasmProtoPreprocessor::with_state(&shared_state)))
|
||||
.with_service_generator(nrpc_build::AbstractImpl::outer(WasmServiceGenerator::with_state(&shared_state)))
|
||||
.with_preprocessor(nrpc_build::AbstractImpl::outer(
|
||||
WasmProtoPreprocessor::with_state(&shared_state),
|
||||
))
|
||||
.with_service_generator(nrpc_build::AbstractImpl::outer(
|
||||
WasmServiceGenerator::with_state(&shared_state),
|
||||
))
|
||||
.transpile()
|
||||
.unwrap()
|
||||
}
|
||||
|
|
|
@ -18,10 +18,7 @@ impl WasmProtoPreprocessor {
|
|||
|
||||
impl IPreprocessor for WasmProtoPreprocessor {
|
||||
fn process(&mut self, fds: &mut FileDescriptorSet) -> proc_macro2::TokenStream {
|
||||
self.shared.lock()
|
||||
.expect("Cannot lock shared state")
|
||||
.fds = Some(fds.clone());
|
||||
self.shared.lock().expect("Cannot lock shared state").fds = Some(fds.clone());
|
||||
quote::quote! {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use prost_build::Service;
|
||||
use prost_types::{FileDescriptorSet, DescriptorProto, EnumDescriptorProto, FieldDescriptorProto};
|
||||
use nrpc_build::IServiceGenerator;
|
||||
use prost_build::Service;
|
||||
use prost_types::{DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, FileDescriptorSet};
|
||||
|
||||
use super::SharedState;
|
||||
|
||||
|
@ -18,32 +18,52 @@ impl WasmServiceGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
fn generate_service_methods(service: &Service, fds: &FileDescriptorSet) -> proc_macro2::TokenStream {
|
||||
fn generate_service_methods(
|
||||
service: &Service,
|
||||
fds: &FileDescriptorSet,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let mut gen_methods = Vec::with_capacity(service.methods.len());
|
||||
for method in &service.methods {
|
||||
let method_name = quote::format_ident!("{}", method.name);
|
||||
let method_input = quote::format_ident!("{}{}", &service.name, method.input_type);
|
||||
let method_output = quote::format_ident!("{}{}", &service.name, method.output_type);
|
||||
let method_output = quote::format_ident!("{}{}Wasm", &service.name, method.output_type);
|
||||
let method_output_as_in = quote::format_ident!("{}{}", &service.name, method.output_type);
|
||||
|
||||
let input_type = find_message_type(&method.input_type, &service.package, fds).expect("Protobuf message is used but not found");
|
||||
let input_type = find_message_type(&method.input_type, &service.package, fds)
|
||||
.expect("Protobuf message is used but not found");
|
||||
|
||||
let mut input_params = Vec::with_capacity(input_type.field.len());
|
||||
let mut params_to_fields = Vec::with_capacity(input_type.field.len());
|
||||
for field in &input_type.field {
|
||||
//let param_name = quote::format_ident!("val{}", i.to_string());
|
||||
let type_name = ProtobufType::from_field(field, &service.name).to_tokens();
|
||||
let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name"));
|
||||
let type_enum = ProtobufType::from_field(field, &service.name, false);
|
||||
//let rs_type_name = type_enum.to_tokens();
|
||||
let js_type_name = type_enum.to_wasm_tokens();
|
||||
let rs_type_name = type_enum.to_tokens();
|
||||
let field_name = quote::format_ident!(
|
||||
"{}",
|
||||
field
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf message field needs a name")
|
||||
);
|
||||
input_params.push(quote::quote! {
|
||||
#field_name: #type_name,
|
||||
#field_name: #js_type_name,
|
||||
});
|
||||
params_to_fields.push(quote::quote! {
|
||||
#field_name,//: #field_name,
|
||||
#field_name: #rs_type_name::from_wasm(#field_name.into()),//: #field_name,
|
||||
});
|
||||
}
|
||||
let params_to_fields_transformer = if input_type.field.len() == 1 {
|
||||
let field_name = quote::format_ident!("{}", input_type.field[0].name.as_ref().expect("Protobuf message field needs a name"));
|
||||
let field_name = quote::format_ident!(
|
||||
"{}",
|
||||
input_type.field[0]
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf message field needs a name")
|
||||
);
|
||||
quote::quote! {
|
||||
let val = #field_name;
|
||||
let val = #method_input::from_wasm(#field_name.into());
|
||||
}
|
||||
} else if input_type.field.is_empty() {
|
||||
quote::quote! {
|
||||
|
@ -57,49 +77,56 @@ fn generate_service_methods(service: &Service, fds: &FileDescriptorSet) -> proc_
|
|||
}
|
||||
};
|
||||
|
||||
let special_fn_into_input = quote::format_ident!("{}_convert_into", method.input_type.split('.').last().unwrap().to_lowercase());
|
||||
|
||||
let special_fn_from_output = quote::format_ident!("{}_convert_from", method.output_type.split('.').last().unwrap().to_lowercase());
|
||||
|
||||
gen_methods.push(
|
||||
quote::quote!{
|
||||
gen_methods.push(quote::quote! {
|
||||
#[wasm_bindgen]
|
||||
pub async fn #method_name(&mut self, #(#input_params)*) -> Option<#method_output> {
|
||||
|
||||
#params_to_fields_transformer
|
||||
|
||||
match self.service.#method_name(#special_fn_into_input(val)).await {
|
||||
Ok(x) => Some(#special_fn_from_output(x)),
|
||||
match self.service.#method_name(val.into()).await {
|
||||
Ok(x) => {
|
||||
let x2: #method_output_as_in = x.into();
|
||||
Some(x2.into_wasm())
|
||||
},
|
||||
Err(_e) => {
|
||||
// TODO log error
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
quote::quote! {
|
||||
#(#gen_methods)*
|
||||
}
|
||||
}
|
||||
|
||||
fn find_message_type<'a>(want_type: &str, want_package: &str, fds: &'a FileDescriptorSet) -> Option<&'a DescriptorProto> {
|
||||
fn find_message_type<'a>(
|
||||
want_type: &str,
|
||||
want_package: &str,
|
||||
fds: &'a FileDescriptorSet,
|
||||
) -> Option<&'a DescriptorProto> {
|
||||
for file in &fds.file {
|
||||
if let Some(pkg) = &file.package {
|
||||
if pkg == want_package {
|
||||
for message_type in &file.message_type {
|
||||
if let Some(name) = &message_type.name {
|
||||
if let Some(pkg) = &file.package {
|
||||
if name == want_type && pkg == want_package {
|
||||
if name == want_type {
|
||||
return Some(message_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn find_enum_type<'a>(want_type: &str, want_package: &str, fds: &'a FileDescriptorSet) -> Option<&'a EnumDescriptorProto> {
|
||||
fn find_enum_type<'a>(
|
||||
want_type: &str,
|
||||
want_package: &str,
|
||||
fds: &'a FileDescriptorSet,
|
||||
) -> Option<&'a EnumDescriptorProto> {
|
||||
for file in &fds.file {
|
||||
for enum_type in &file.enum_type {
|
||||
if let Some(name) = &enum_type.name {
|
||||
|
@ -114,7 +141,10 @@ fn find_enum_type<'a>(want_type: &str, want_package: &str, fds: &'a FileDescript
|
|||
None
|
||||
}
|
||||
|
||||
fn find_field<'a>(want_field: &str, descriptor: &'a DescriptorProto) -> Option<&'a FieldDescriptorProto> {
|
||||
fn find_field<'a>(
|
||||
want_field: &str,
|
||||
descriptor: &'a DescriptorProto,
|
||||
) -> Option<&'a FieldDescriptorProto> {
|
||||
for field in &descriptor.field {
|
||||
if let Some(name) = &field.name {
|
||||
if name == want_field {
|
||||
|
@ -125,9 +155,47 @@ fn find_field<'a>(want_field: &str, descriptor: &'a DescriptorProto) -> Option<&
|
|||
None
|
||||
}
|
||||
|
||||
fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mut HashSet<String>, handled_types: &mut HashSet<String>, is_response_msg: bool, service: &str) -> proc_macro2::TokenStream {
|
||||
let msg_name = quote::format_ident!("{}{}", service, descriptor.name.as_ref().expect("Protobuf message needs a name"));
|
||||
let super_msg_name = quote::format_ident!("{}", descriptor.name.as_ref().expect("Protobuf message needs a name"));
|
||||
fn is_known_map(field: &FieldDescriptorProto, known_maps: &HashSet<String>) -> bool {
|
||||
if let Some(type_name) = &field.type_name {
|
||||
let name = type_name.split('.').last().unwrap();
|
||||
known_maps.contains(name)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_wasm_struct_interop(
|
||||
descriptor: &DescriptorProto,
|
||||
handled_enums: &mut HashSet<String>,
|
||||
handled_types: &mut HashSet<String>,
|
||||
known_maps: &mut HashSet<String>,
|
||||
seen_super_enums: &mut HashSet<String>,
|
||||
is_response_msg: bool,
|
||||
service: &str,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let msg_name = quote::format_ident!(
|
||||
"{}{}",
|
||||
service,
|
||||
descriptor
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf message needs a name")
|
||||
);
|
||||
let msg_name_wasm = quote::format_ident!(
|
||||
"{}{}Wasm",
|
||||
service,
|
||||
descriptor
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf message needs a name")
|
||||
);
|
||||
let super_msg_name = quote::format_ident!(
|
||||
"{}",
|
||||
descriptor
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf message needs a name")
|
||||
);
|
||||
let mut gen_fields = Vec::with_capacity(descriptor.field.len());
|
||||
let mut gen_into_fields = Vec::with_capacity(descriptor.field.len());
|
||||
let mut gen_from_fields = Vec::with_capacity(descriptor.field.len());
|
||||
|
@ -137,126 +205,60 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu
|
|||
let mut gen_enums = Vec::with_capacity(descriptor.enum_type.len());
|
||||
|
||||
if let Some(options) = &descriptor.options {
|
||||
//dbg!(options);
|
||||
if let Some(map_entry) = options.map_entry {
|
||||
// TODO deal with options when necessary
|
||||
if map_entry {
|
||||
let name = descriptor.name.clone().expect("Protobuf message needs a name");
|
||||
let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase());
|
||||
let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase());
|
||||
let key_field = find_field("key", descriptor).expect("Protobuf map entry has no key field");
|
||||
let key_type = ProtobufType::from_field(&key_field, service);
|
||||
let value_field = find_field("value", descriptor).expect("Protobuf map entry has no value field");
|
||||
let value_type = ProtobufType::from_field(&value_field, service);
|
||||
//dbg!(descriptor);
|
||||
let name = descriptor
|
||||
.name
|
||||
.clone()
|
||||
.expect("Protobuf message needs a name");
|
||||
known_maps.insert(name.clone());
|
||||
let key_field =
|
||||
find_field("key", descriptor).expect("Protobuf map entry has no key field");
|
||||
let key_type = ProtobufType::from_field(&key_field, service, false);
|
||||
let value_field =
|
||||
find_field("value", descriptor).expect("Protobuf map entry has no value field");
|
||||
let value_type = ProtobufType::from_field(&value_field, service, false);
|
||||
|
||||
let key_type_tokens = key_type.to_tokens();
|
||||
let value_type_tokens = value_type.to_tokens();
|
||||
|
||||
let (fn_from, fn_into) = match (key_type, value_type) {
|
||||
(ProtobufType::String, ProtobufType::String) => (
|
||||
quote::quote!{
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_from(other: ::std::collections::HashMap<#key_type_tokens, #value_type_tokens>) -> #msg_name {
|
||||
let map = #msg_name::new();
|
||||
for (key, val) in other.iter() {
|
||||
map.set(&key.into(), &val.into());
|
||||
}
|
||||
map
|
||||
}
|
||||
},
|
||||
quote::quote!{
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type_tokens, #value_type_tokens> {
|
||||
let mut output = ::std::collections::HashMap::<#key_type_tokens, #value_type_tokens>::new();
|
||||
this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| {
|
||||
if let Some(key) = key.as_string() {
|
||||
if let Some(val) = val.as_string() {
|
||||
output.insert(key, val);
|
||||
}
|
||||
}
|
||||
});
|
||||
output
|
||||
}
|
||||
}
|
||||
),
|
||||
(ProtobufType::String, ProtobufType::Double | ProtobufType::Float | ProtobufType::Int32| ProtobufType::Int64| ProtobufType::Uint32| ProtobufType::Uint64| ProtobufType::Sint32| ProtobufType::Sint64| ProtobufType::Fixed32| ProtobufType::Fixed64| ProtobufType::Sfixed32| ProtobufType::Sfixed64) => (
|
||||
quote::quote!{
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_from(other: ::std::collections::HashMap<#key_type_tokens, #value_type_tokens>) -> #msg_name {
|
||||
let map = #msg_name::new();
|
||||
for (key, val) in other.iter() {
|
||||
map.set(&key.into(), &(val as f64).into());
|
||||
}
|
||||
map
|
||||
}
|
||||
},
|
||||
quote::quote!{
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type_tokens, #value_type_tokens> {
|
||||
let mut output = ::std::collections::HashMap::<#key_type_tokens, #value_type_tokens>::new();
|
||||
this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| {
|
||||
if let Some(key) = key.as_string() {
|
||||
if let Some(val) = val.as_f64() {
|
||||
output.insert(key, val as _);
|
||||
}
|
||||
}
|
||||
});
|
||||
output
|
||||
}
|
||||
}
|
||||
),
|
||||
(ProtobufType::String, ProtobufType::Bool) => (
|
||||
quote::quote!{
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_from(other: ::std::collections::HashMap<#key_type_tokens, #value_type_tokens>) -> #msg_name {
|
||||
let map = #msg_name::new();
|
||||
for (key, val) in other.iter() {
|
||||
map.set(&key.into(), &(val as f64).into());
|
||||
}
|
||||
map
|
||||
}
|
||||
},
|
||||
quote::quote!{
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_into(this: #msg_name) -> ::std::collections::HashMap<#key_type_tokens, #value_type_tokens> {
|
||||
let mut output = ::std::collections::HashMap::<#key_type_tokens, #value_type_tokens>::new();
|
||||
this.for_each(&mut |key: ::wasm_bindgen::JsValue, val: ::wasm_bindgen::JsValue| {
|
||||
if let Some(key) = key.as_string() {
|
||||
if let Some(val) = val.as_bool() {
|
||||
output.insert(key, val);
|
||||
}
|
||||
}
|
||||
});
|
||||
output
|
||||
}
|
||||
}
|
||||
),
|
||||
(key_type, value_type) => panic!("Unsupported map type map<{:?}, {:?}>", key_type, value_type),
|
||||
let map_type = ProtobufType::Map {
|
||||
key: Box::new(key_type),
|
||||
value: Box::new(value_type),
|
||||
};
|
||||
|
||||
//dbg!("Generated map type", name);
|
||||
|
||||
let map_tokens = map_type.to_tokens();
|
||||
let wasm_tokens = map_type.to_wasm_tokens();
|
||||
|
||||
return quote::quote! {
|
||||
pub type #msg_name = ::js_sys::Map;
|
||||
pub type #msg_name = #map_tokens;
|
||||
pub type #msg_name_wasm = #wasm_tokens;
|
||||
};
|
||||
}
|
||||
}
|
||||
// TODO Deal with other message options when necessary
|
||||
}
|
||||
|
||||
#fn_from
|
||||
|
||||
#fn_into
|
||||
}
|
||||
}
|
||||
} else {
|
||||
todo!("Deal with message options when necessary");
|
||||
}
|
||||
}
|
||||
//dbg!(&descriptor.options);
|
||||
|
||||
for n_type in &descriptor.nested_type {
|
||||
let type_name = n_type.name.clone().expect("Protobuf nested message needs a name");
|
||||
let type_name = n_type
|
||||
.name
|
||||
.clone()
|
||||
.expect("Protobuf nested message needs a name");
|
||||
if !handled_types.contains(&type_name) {
|
||||
handled_types.insert(type_name);
|
||||
gen_nested_types.push(generate_wasm_struct_interop(n_type, handled_enums, handled_types, is_response_msg, service));
|
||||
gen_nested_types.push(generate_wasm_struct_interop(
|
||||
n_type,
|
||||
handled_enums,
|
||||
handled_types,
|
||||
known_maps,
|
||||
seen_super_enums,
|
||||
is_response_msg,
|
||||
service,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,62 +266,55 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu
|
|||
let type_name = e_type.name.clone().expect("Protobuf enum needs a name");
|
||||
if !handled_enums.contains(&type_name) {
|
||||
handled_enums.insert(type_name);
|
||||
gen_enums.push(generate_wasm_enum_interop(e_type, service));
|
||||
gen_enums.push(generate_wasm_enum_interop(
|
||||
e_type,
|
||||
service,
|
||||
seen_super_enums,
|
||||
));
|
||||
}
|
||||
}
|
||||
if descriptor.field.len() == 1 {
|
||||
|
||||
if descriptor.field.len() == 0 {
|
||||
quote::quote! {
|
||||
pub type #msg_name = ();
|
||||
pub type #msg_name_wasm = #msg_name;
|
||||
|
||||
#(#gen_nested_types)*
|
||||
|
||||
#(#gen_enums)*
|
||||
}
|
||||
} else if descriptor.field.len() == 1 {
|
||||
let field = &descriptor.field[0];
|
||||
let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name"));
|
||||
let type_name = ProtobufType::from_field(field, service).to_tokens();
|
||||
gen_fields.push(quote::quote!{
|
||||
pub #field_name: #type_name,
|
||||
});
|
||||
if let Some(name) = &field.type_name {
|
||||
let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase());
|
||||
let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase());
|
||||
gen_into_fields.push(
|
||||
quote::quote!{
|
||||
#field_name: #special_fn_into(this)
|
||||
}
|
||||
//dbg!(descriptor, field);
|
||||
let field_name = quote::format_ident!(
|
||||
"{}",
|
||||
field
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf message field needs a name")
|
||||
);
|
||||
|
||||
gen_from_fields.push(
|
||||
quote::quote!{
|
||||
#special_fn_from(other.#field_name)
|
||||
}
|
||||
);
|
||||
} else {
|
||||
gen_into_fields.push(
|
||||
quote::quote!{
|
||||
#field_name: this
|
||||
}
|
||||
);
|
||||
|
||||
gen_from_fields.push(
|
||||
quote::quote!{
|
||||
other.#field_name
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let name = descriptor.name.clone().expect("Protobuf message needs a name");
|
||||
let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase());
|
||||
let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase());
|
||||
let type_enum = ProtobufType::from_field(field, service, is_known_map(field, known_maps));
|
||||
let type_name = type_enum.to_tokens();
|
||||
let wasm_type_name = type_enum.to_wasm_tokens();
|
||||
|
||||
quote::quote! {
|
||||
pub type #msg_name = #type_name;
|
||||
pub type #msg_name_wasm = #wasm_type_name;
|
||||
|
||||
impl std::convert::Into<super::#super_msg_name> for #msg_name {
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_from(other: super::#super_msg_name) -> #msg_name {
|
||||
#(#gen_from_fields)*
|
||||
fn into(self) -> super::#super_msg_name {
|
||||
super::#super_msg_name {
|
||||
#field_name: self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<super::#super_msg_name> for #msg_name {
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_into(this: #msg_name) -> super::#super_msg_name {
|
||||
super::#super_msg_name {
|
||||
#(#gen_into_fields)*
|
||||
#[allow(unused_variables)]
|
||||
fn from(other: super::#super_msg_name) -> Self {
|
||||
other.#field_name
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -329,45 +324,31 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu
|
|||
}
|
||||
} else {
|
||||
for field in &descriptor.field {
|
||||
let field_name = quote::format_ident!("{}", field.name.as_ref().expect("Protobuf message field needs a name"));
|
||||
let type_name = ProtobufType::from_field(field, service).to_tokens();
|
||||
let field_name = quote::format_ident!(
|
||||
"{}",
|
||||
field
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf message field needs a name")
|
||||
);
|
||||
let type_enum =
|
||||
ProtobufType::from_field(field, service, is_known_map(field, known_maps));
|
||||
let type_name = type_enum.to_tokens();
|
||||
//let wasm_type_name = type_enum.to_wasm_tokens();
|
||||
gen_fields.push(quote::quote! {
|
||||
pub #field_name: #type_name,
|
||||
});
|
||||
if let Some(name) = &field.type_name {
|
||||
let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase());
|
||||
let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase());
|
||||
gen_into_fields.push(
|
||||
quote::quote!{
|
||||
#field_name: #special_fn_into(self.#field_name),
|
||||
}
|
||||
);
|
||||
gen_into_fields.push(quote::quote! {
|
||||
#field_name: self.#field_name.into(),
|
||||
});
|
||||
|
||||
gen_from_fields.push(
|
||||
quote::quote!{
|
||||
#field_name: #special_fn_from(other.#field_name),
|
||||
}
|
||||
);
|
||||
} else {
|
||||
gen_into_fields.push(
|
||||
quote::quote!{
|
||||
#field_name: self.#field_name,
|
||||
}
|
||||
);
|
||||
|
||||
gen_from_fields.push(
|
||||
quote::quote!{
|
||||
#field_name: other.#field_name,
|
||||
}
|
||||
);
|
||||
}
|
||||
gen_from_fields.push(quote::quote! {
|
||||
#field_name: <_>::from(other.#field_name),
|
||||
});
|
||||
}
|
||||
|
||||
let name = descriptor.name.clone().expect("Protobuf message needs a name");
|
||||
let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase());
|
||||
let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase());
|
||||
|
||||
let wasm_attribute_maybe = if descriptor.field.len() == 1 || !is_response_msg {
|
||||
let wasm_attribute_maybe =
|
||||
if (descriptor.field.len() == 1 || !is_response_msg) && !descriptor.field.is_empty() {
|
||||
quote::quote! {}
|
||||
} else {
|
||||
quote::quote! {
|
||||
|
@ -381,6 +362,22 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu
|
|||
#(#gen_fields)*
|
||||
}
|
||||
|
||||
impl KnownWasmCompatible for #msg_name {}
|
||||
|
||||
impl IntoWasmable<#msg_name> for #msg_name {
|
||||
fn into_wasm(self) -> Self {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FromWasmable<#msg_name> for #msg_name {
|
||||
fn from_wasm(x: Self) -> Self {
|
||||
x
|
||||
}
|
||||
}
|
||||
|
||||
type #msg_name_wasm = #msg_name;
|
||||
|
||||
impl std::convert::Into<super::#super_msg_name> for #msg_name {
|
||||
#[inline]
|
||||
fn into(self) -> super::#super_msg_name {
|
||||
|
@ -400,24 +397,11 @@ fn generate_wasm_struct_interop(descriptor: &DescriptorProto, handled_enums: &mu
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_from(other: super::#super_msg_name) -> #msg_name {
|
||||
#msg_name::from(other)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_into(this: #msg_name) -> super::#super_msg_name {
|
||||
this.into()
|
||||
}
|
||||
|
||||
#(#gen_nested_types)*
|
||||
|
||||
#(#gen_enums)*
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -437,6 +421,11 @@ enum ProtobufType {
|
|||
Bool,
|
||||
String,
|
||||
Bytes,
|
||||
Repeated(Box<ProtobufType>),
|
||||
Map {
|
||||
key: Box<ProtobufType>,
|
||||
value: Box<ProtobufType>,
|
||||
},
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
|
@ -464,31 +453,40 @@ impl ProtobufType {
|
|||
|
||||
fn from_id(id: i32) -> Self {
|
||||
match id {
|
||||
//"double" => quote::quote!{f64},
|
||||
1 => Self::Double,
|
||||
//"float" => quote::quote!{f32},
|
||||
//"int32" => quote::quote!{i32},
|
||||
//"int64" => quote::quote!{i64},
|
||||
//"uint32" => quote::quote!{u32},
|
||||
//"uint64" => quote::quote!{u64},
|
||||
4 => Self::Uint64,
|
||||
5 => Self::Int32,
|
||||
//"sint32" => quote::quote!{i32},
|
||||
//"sint64" => quote::quote!{i64},
|
||||
//"fixed32" => quote::quote!{u32},
|
||||
//"fixed64" => quote::quote!{u64},
|
||||
//"sfixed32" => quote::quote!{i32},
|
||||
//"sfixed64" => quote::quote!{i64},
|
||||
//"bool" => quote::quote!{bool},
|
||||
8 => Self::Bool,
|
||||
9 => Self::String,
|
||||
//"bytes" => quote::quote!{Vec<u8>},
|
||||
t => Self::Custom(format!("UnknownType{}", t)),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_field(field: &FieldDescriptorProto, service: &str) -> Self {
|
||||
if let Some(type_name) = &field.type_name {
|
||||
fn from_field(field: &FieldDescriptorProto, service: &str, is_map: bool) -> Self {
|
||||
let inner = if let Some(type_name) = &field.type_name {
|
||||
Self::from_str(type_name, service)
|
||||
} else {
|
||||
let number = field.r#type.unwrap();
|
||||
Self::from_id(number)
|
||||
};
|
||||
if let Some(label) = field.label {
|
||||
match label {
|
||||
3 if !is_map => Self::Repeated(Box::new(inner)), // is also the label for maps for some reason...
|
||||
_ => inner,
|
||||
}
|
||||
} else {
|
||||
inner
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -509,17 +507,77 @@ impl ProtobufType {
|
|||
Self::Bool => quote::quote! {bool},
|
||||
Self::String => quote::quote! {String},
|
||||
Self::Bytes => quote::quote! {Vec<u8>},
|
||||
Self::Repeated(t) => {
|
||||
let inner = t.to_tokens();
|
||||
quote::quote! {Vec::<#inner>}
|
||||
}
|
||||
Self::Map { key, value } => {
|
||||
let key = key.to_tokens();
|
||||
let value = value.to_tokens();
|
||||
quote::quote! {std::collections::HashMap<#key, #value>}
|
||||
}
|
||||
Self::Custom(t) => {
|
||||
let ident = quote::format_ident!("{}", t);
|
||||
quote::quote! {#ident}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_wasm_enum_interop(descriptor: &EnumDescriptorProto, service: &str) -> proc_macro2::TokenStream {
|
||||
let enum_name = quote::format_ident!("{}{}", service, descriptor.name.as_ref().expect("Protobuf enum needs a name"));
|
||||
let super_enum_name = quote::format_ident!("{}", descriptor.name.as_ref().expect("Protobuf enum needs a name"));
|
||||
fn to_wasm_tokens(&self) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
Self::Double => quote::quote! {f64},
|
||||
Self::Float => quote::quote! {f32},
|
||||
Self::Int32 => quote::quote! {i32},
|
||||
Self::Int64 => quote::quote! {i64},
|
||||
Self::Uint32 => quote::quote! {u32},
|
||||
Self::Uint64 => quote::quote! {u64},
|
||||
Self::Sint32 => quote::quote! {i32},
|
||||
Self::Sint64 => quote::quote! {i64},
|
||||
Self::Fixed32 => quote::quote! {u32},
|
||||
Self::Fixed64 => quote::quote! {u64},
|
||||
Self::Sfixed32 => quote::quote! {i32},
|
||||
Self::Sfixed64 => quote::quote! {i64},
|
||||
Self::Bool => quote::quote! {bool},
|
||||
Self::String => quote::quote! {String},
|
||||
Self::Bytes => quote::quote! {Vec<u8>},
|
||||
Self::Repeated(_) => quote::quote! {js_sys::Array},
|
||||
Self::Map { .. } => quote::quote! {js_sys::Map},
|
||||
Self::Custom(t) => {
|
||||
let ident = quote::format_ident!("{}Wasm", t);
|
||||
quote::quote! {#ident}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_wasm_enum_interop(
|
||||
descriptor: &EnumDescriptorProto,
|
||||
service: &str,
|
||||
seen_super_enums: &mut HashSet<String>,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let enum_name = quote::format_ident!(
|
||||
"{}{}",
|
||||
service,
|
||||
descriptor
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf enum needs a name")
|
||||
);
|
||||
let enum_name_wasm = quote::format_ident!(
|
||||
"{}{}Wasm",
|
||||
service,
|
||||
descriptor
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf enum needs a name")
|
||||
);
|
||||
let super_enum_name = quote::format_ident!(
|
||||
"{}",
|
||||
descriptor
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf enum needs a name")
|
||||
);
|
||||
let mut gen_values = Vec::with_capacity(descriptor.value.len());
|
||||
let mut gen_into_values = Vec::with_capacity(descriptor.value.len());
|
||||
let mut gen_from_values = Vec::with_capacity(descriptor.value.len());
|
||||
|
@ -528,40 +586,44 @@ fn generate_wasm_enum_interop(descriptor: &EnumDescriptorProto, service: &str) -
|
|||
todo!("Deal with enum options when necessary");
|
||||
}
|
||||
for value in &descriptor.value {
|
||||
let val_name = quote::format_ident!("{}", value.name.as_ref().expect("Protobuf enum value needs a name"));
|
||||
let val_name = quote::format_ident!(
|
||||
"{}",
|
||||
value
|
||||
.name
|
||||
.as_ref()
|
||||
.expect("Protobuf enum value needs a name")
|
||||
);
|
||||
if let Some(_val_options) = &value.options {
|
||||
// TODO deal with options when necessary
|
||||
todo!("Deal with enum value options when necessary");
|
||||
} else {
|
||||
if let Some(number) = &value.number {
|
||||
gen_values.push(
|
||||
quote::quote!{
|
||||
gen_values.push(quote::quote! {
|
||||
#val_name = #number,
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
gen_values.push(
|
||||
quote::quote!{
|
||||
gen_values.push(quote::quote! {
|
||||
#val_name,
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
gen_into_values.push(
|
||||
quote::quote!{
|
||||
gen_into_values.push(quote::quote! {
|
||||
Self::#val_name => super::#super_enum_name::#val_name,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
gen_from_values.push(
|
||||
quote::quote!{
|
||||
gen_from_values.push(quote::quote! {
|
||||
super::#super_enum_name::#val_name => Self::#val_name,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
let name = descriptor.name.clone().expect("Protobuf message needs a name");
|
||||
let special_fn_from = quote::format_ident!("{}_convert_from", name.split('.').last().unwrap().to_lowercase());
|
||||
let special_fn_into = quote::format_ident!("{}_convert_into", name.split('.').last().unwrap().to_lowercase());
|
||||
|
||||
let impl_wasm_compat = if seen_super_enums.contains(descriptor.name.as_ref().unwrap()) {
|
||||
quote::quote! {}
|
||||
} else {
|
||||
seen_super_enums.insert(descriptor.name.clone().unwrap());
|
||||
quote::quote! {
|
||||
//impl KnownWasmCompatible for super::#super_enum_name {}
|
||||
}
|
||||
};
|
||||
|
||||
quote::quote! {
|
||||
#[wasm_bindgen]
|
||||
|
@ -571,6 +633,8 @@ fn generate_wasm_enum_interop(descriptor: &EnumDescriptorProto, service: &str) -
|
|||
#(#gen_values)*
|
||||
}
|
||||
|
||||
type #enum_name_wasm = #enum_name;
|
||||
|
||||
impl std::convert::Into<super::#super_enum_name> for #enum_name {
|
||||
fn into(self) -> super::#super_enum_name {
|
||||
match self {
|
||||
|
@ -587,66 +651,137 @@ fn generate_wasm_enum_interop(descriptor: &EnumDescriptorProto, service: &str) -
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_from(other: i32) -> #enum_name {
|
||||
#impl_wasm_compat
|
||||
|
||||
impl FromWasmable<i32> for #enum_name {
|
||||
fn from_wasm(js: i32) -> Self {
|
||||
#enum_name::from(super::#super_enum_name::from_i32(js).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoWasmable<i32> for #enum_name {
|
||||
fn into_wasm(self) -> i32 {
|
||||
self as i32
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for #enum_name {
|
||||
fn from(other: i32) -> Self {
|
||||
#enum_name::from(super::#super_enum_name::from_i32(other).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
fn #special_fn_into(this: #enum_name) -> i32 {
|
||||
this as i32
|
||||
impl Into<i32> for #enum_name {
|
||||
fn into(self) -> i32 {
|
||||
self as i32
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_service_io_types(service: &Service, fds: &FileDescriptorSet) -> proc_macro2::TokenStream {
|
||||
fn generate_service_io_types(
|
||||
service: &Service,
|
||||
fds: &FileDescriptorSet,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let mut gen_types = Vec::with_capacity(service.methods.len() * 2);
|
||||
let mut gen_enums = Vec::new();
|
||||
let mut handled_enums = HashSet::new();
|
||||
let mut handled_types = HashSet::new();
|
||||
let mut known_maps = HashSet::new();
|
||||
let mut seen_super_enums = HashSet::new();
|
||||
for method in &service.methods {
|
||||
if let Some(input_message) = find_message_type(&method.input_type, &service.package, fds) {
|
||||
let msg_name = input_message.name.clone().expect("Protobuf message name required");
|
||||
let msg_name = input_message
|
||||
.name
|
||||
.clone()
|
||||
.expect("Protobuf message name required");
|
||||
if !handled_types.contains(&msg_name) {
|
||||
handled_types.insert(msg_name);
|
||||
gen_types.push(generate_wasm_struct_interop(input_message, &mut handled_enums, &mut handled_types, false, &service.name));
|
||||
gen_types.push(generate_wasm_struct_interop(
|
||||
input_message,
|
||||
&mut handled_enums,
|
||||
&mut handled_types,
|
||||
&mut known_maps,
|
||||
&mut seen_super_enums,
|
||||
false,
|
||||
&service.name,
|
||||
));
|
||||
}
|
||||
} else if let Some(input_enum) = find_enum_type(&method.input_type, &service.package, fds) {
|
||||
let enum_name = input_enum.name.clone().expect("Protobuf enum name required");
|
||||
let enum_name = input_enum
|
||||
.name
|
||||
.clone()
|
||||
.expect("Protobuf enum name required");
|
||||
if !handled_enums.contains(&enum_name) {
|
||||
handled_enums.insert(enum_name);
|
||||
gen_types.push(generate_wasm_enum_interop(input_enum, &service.name));
|
||||
gen_enums.push(generate_wasm_enum_interop(
|
||||
input_enum,
|
||||
&service.name,
|
||||
&mut seen_super_enums,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
panic!("Cannot find proto type {}/{}", service.package, method.input_type);
|
||||
panic!(
|
||||
"Cannot find input proto type/message {}/{} for method {}",
|
||||
service.package, method.input_type, method.name
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(output_message) = find_message_type(&method.output_type, &service.package, fds) {
|
||||
let msg_name = output_message.name.clone().expect("Protobuf message name required");
|
||||
if let Some(output_message) = find_message_type(&method.output_type, &service.package, fds)
|
||||
{
|
||||
let msg_name = output_message
|
||||
.name
|
||||
.clone()
|
||||
.expect("Protobuf message name required");
|
||||
if !handled_types.contains(&msg_name) {
|
||||
handled_types.insert(msg_name);
|
||||
gen_types.push(generate_wasm_struct_interop(output_message, &mut handled_enums, &mut handled_types, true, &service.name));
|
||||
gen_types.push(generate_wasm_struct_interop(
|
||||
output_message,
|
||||
&mut handled_enums,
|
||||
&mut handled_types,
|
||||
&mut known_maps,
|
||||
&mut seen_super_enums,
|
||||
true,
|
||||
&service.name,
|
||||
));
|
||||
}
|
||||
} else if let Some(output_enum) = find_enum_type(&method.output_type, &service.package, fds) {
|
||||
let enum_name = output_enum.name.clone().expect("Protobuf enum name required");
|
||||
} else if let Some(output_enum) = find_enum_type(&method.output_type, &service.package, fds)
|
||||
{
|
||||
let enum_name = output_enum
|
||||
.name
|
||||
.clone()
|
||||
.expect("Protobuf enum name required");
|
||||
if !handled_enums.contains(&enum_name) {
|
||||
handled_enums.insert(enum_name);
|
||||
gen_types.push(generate_wasm_enum_interop(output_enum, &service.name));
|
||||
gen_enums.push(generate_wasm_enum_interop(
|
||||
output_enum,
|
||||
&service.name,
|
||||
&mut seen_super_enums,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
panic!("Cannot find proto type {}/{}", service.package, method.input_type);
|
||||
panic!(
|
||||
"Cannot find output proto type/message {}/{} for method {}",
|
||||
service.package, method.output_type, method.name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// always generate all enums, since they aren't encountered (ever, afaik) when generating message structs
|
||||
for file in &fds.file {
|
||||
if let Some(pkg) = &file.package {
|
||||
if pkg == &service.package {
|
||||
for enum_type in &file.enum_type {
|
||||
let enum_name = enum_type.name.clone().expect("Protobuf enum name required");
|
||||
if !handled_enums.contains(&enum_name) {
|
||||
handled_enums.insert(enum_name);
|
||||
gen_enums.push(generate_wasm_enum_interop(enum_type, &service.name));
|
||||
gen_enums.push(generate_wasm_enum_interop(
|
||||
enum_type,
|
||||
&service.name,
|
||||
&mut seen_super_enums,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -659,9 +794,9 @@ fn generate_service_io_types(service: &Service, fds: &FileDescriptorSet) -> proc
|
|||
|
||||
impl IServiceGenerator for WasmServiceGenerator {
|
||||
fn generate(&mut self, service: Service) -> proc_macro2::TokenStream {
|
||||
let lock = self.shared.lock()
|
||||
.expect("Cannot lock shared state");
|
||||
let fds = lock.fds
|
||||
let lock = self.shared.lock().expect("Cannot lock shared state");
|
||||
let fds = lock
|
||||
.fds
|
||||
.as_ref()
|
||||
.expect("FileDescriptorSet required for WASM service generator");
|
||||
let service_struct_name = quote::format_ident!("{}Client", service.name);
|
||||
|
@ -671,9 +806,15 @@ impl IServiceGenerator for WasmServiceGenerator {
|
|||
let mod_name = quote::format_ident!("js_{}", service.name.to_lowercase());
|
||||
quote::quote! {
|
||||
mod #mod_name {
|
||||
use wasm_bindgen::prelude::*;
|
||||
#![allow(dead_code, unused_imports)]
|
||||
use usdpl_front::_helpers::wasm_bindgen::prelude::*;
|
||||
use usdpl_front::_helpers::wasm_bindgen;
|
||||
use usdpl_front::_helpers::wasm_bindgen_futures;
|
||||
use usdpl_front::_helpers::js_sys;
|
||||
|
||||
use crate::client_handler::WebSocketHandler;
|
||||
use usdpl_front::wasm::*;
|
||||
|
||||
use usdpl_front::WebSocketHandler;
|
||||
|
||||
#service_types
|
||||
|
||||
|
|
|
@ -7,9 +7,7 @@ pub struct SharedState(Arc<Mutex<SharedProtoData>>);
|
|||
|
||||
impl SharedState {
|
||||
pub fn new() -> Self {
|
||||
Self(Arc::new(Mutex::new(SharedProtoData {
|
||||
fds: None,
|
||||
})))
|
||||
Self(Arc::new(Mutex::new(SharedProtoData { fds: None })))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,4 +2,6 @@ pub mod back;
|
|||
pub mod front;
|
||||
|
||||
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"),
|
||||
};
|
||||
|
||||
const ALL_PROTOS: [IncludedFileStr<'static>; 2] = [
|
||||
DEBUG_PROTO,
|
||||
TRANSLATIONS_PROTO,
|
||||
];
|
||||
const ALL_PROTOS: [IncludedFileStr<'static>; 2] = [DEBUG_PROTO, TRANSLATIONS_PROTO];
|
||||
|
||||
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())
|
||||
.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());
|
||||
dirs.split(':')
|
||||
.filter(|x| std::fs::read_dir(x).is_ok())
|
||||
.map(|x| x.to_owned())
|
||||
.chain(additionals)
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
@ -48,14 +47,27 @@ fn custom_protos_filenames() -> Vec<String> {
|
|||
.flat_map(|x| x.unwrap())
|
||||
.filter(|x| x.is_ok())
|
||||
.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()))
|
||||
.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();
|
||||
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<()> {
|
||||
|
|
|
@ -4,12 +4,10 @@
|
|||
|
||||
mod remote_call;
|
||||
|
||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
||||
#[cfg(not(any(feature = "decky")))]
|
||||
mod api_any;
|
||||
mod api_common;
|
||||
#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))]
|
||||
mod api_crankshaft;
|
||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
||||
#[cfg(all(feature = "decky", not(any(feature = "any"))))]
|
||||
mod api_decky;
|
||||
|
||||
pub mod serdes;
|
||||
|
@ -20,11 +18,9 @@ pub use remote_call::{RemoteCall, RemoteCallResponse};
|
|||
/// USDPL core API.
|
||||
/// This contains functionality used in both the back-end and front-end.
|
||||
pub mod api {
|
||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
||||
#[cfg(not(any(feature = "decky")))]
|
||||
pub use super::api_any::*;
|
||||
pub use super::api_common::*;
|
||||
#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))]
|
||||
pub use super::api_crankshaft::*;
|
||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
||||
#[cfg(all(feature = "decky", not(any(feature = "any"))))]
|
||||
pub use super::api_decky::*;
|
||||
}
|
||||
|
|
|
@ -88,7 +88,10 @@ mod tests {
|
|||
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");
|
||||
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");
|
||||
|
|
|
@ -26,43 +26,34 @@ impl<T: Dumpable> Dumpable for Vec<T> {
|
|||
|
||||
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)?
|
||||
)
|
||||
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)?
|
||||
)
|
||||
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)?
|
||||
Ok(self.0.dump(buffer)?
|
||||
+ self.1.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> {
|
||||
Ok(
|
||||
self.0.dump(buffer)?
|
||||
Ok(self.0.dump(buffer)?
|
||||
+ self.1.dump(buffer)?
|
||||
+ self.2.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> {
|
||||
let (t0, len0) = T0::load(buffer)?;
|
||||
let (t1, len1) = T1::load(buffer)?;
|
||||
Ok((
|
||||
(t0, t1),
|
||||
len0 + len1
|
||||
))
|
||||
Ok(((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 (t1, len1) = T1::load(buffer)?;
|
||||
let (t2, len2) = T2::load(buffer)?;
|
||||
Ok((
|
||||
(t0, t1, t2),
|
||||
len0 + len1 + len2
|
||||
))
|
||||
Ok(((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 (t2, len2) = T2::load(buffer)?;
|
||||
let (t3, len3) = T3::load(buffer)?;
|
||||
Ok((
|
||||
(t0, t1, t2, t3),
|
||||
len0 + len1 + len2 + len3
|
||||
))
|
||||
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) {
|
||||
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
|
||||
))
|
||||
Ok(((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 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.
|
||||
|
@ -47,7 +47,8 @@ impl Primitive {
|
|||
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)?;
|
||||
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),
|
||||
|
@ -105,7 +106,7 @@ macro_rules! into_impl {
|
|||
Primitive::$variant(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
into_impl! {String, String}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::io::{Read, Write, Cursor};
|
||||
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);
|
||||
|
||||
|
@ -49,8 +49,7 @@ pub trait Loadable: Sized {
|
|||
/// 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)?;
|
||||
decode_config_buf(buffer, B64_CONF, &mut buffer2).map_err(|_| LoadError::InvalidData)?;
|
||||
let mut cursor = Cursor::new(buffer2);
|
||||
Self::load(&mut cursor)
|
||||
}
|
||||
|
@ -66,7 +65,9 @@ pub trait Loadable: Sized {
|
|||
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)?;
|
||||
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)
|
||||
|
@ -121,7 +122,12 @@ pub trait Dumpable {
|
|||
|
||||
/// 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> {
|
||||
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);
|
||||
|
@ -129,7 +135,9 @@ pub trait Dumpable {
|
|||
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)?;
|
||||
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);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Web messaging
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
|
||||
use crate::serdes::{DumpError, Dumpable, LoadError, Loadable};
|
||||
use crate::{RemoteCall, RemoteCallResponse};
|
||||
|
@ -66,7 +66,8 @@ impl Packet {
|
|||
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)?;
|
||||
buf.read_exact(&mut discriminant_buf)
|
||||
.map_err(LoadError::Io)?;
|
||||
let mut result: (Self, usize) = match discriminant_buf[0] {
|
||||
//0 => (None, 0),
|
||||
1 => {
|
||||
|
@ -88,15 +89,15 @@ impl Loadable for Packet {
|
|||
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;
|
||||
|
@ -130,7 +131,8 @@ mod tests {
|
|||
#[cfg(feature = "encrypt")]
|
||||
#[test]
|
||||
fn encryption_integration_test() {
|
||||
let key = hex_literal::hex!("59C4E408F27250B3147E7724511824F1D28ED7BEF43CF7103ACE747F77A2B265");
|
||||
let key =
|
||||
hex_literal::hex!("59C4E408F27250B3147E7724511824F1D28ED7BEF43CF7103ACE747F77A2B265");
|
||||
let nonce = [0u8; NONCE_SIZE];
|
||||
let packet = Packet::Call(RemoteCall {
|
||||
id: 42,
|
||||
|
@ -139,15 +141,29 @@ mod tests {
|
|||
});
|
||||
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());
|
||||
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_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");
|
||||
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");
|
||||
}
|
||||
|
|
|
@ -50,6 +50,3 @@ prost = "0.11"
|
|||
|
||||
[dev-dependencies]
|
||||
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 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 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);
|
||||
|
||||
/// Websocket client.
|
||||
/// In most cases, this shouldn't be used directly, but generated code will use this.
|
||||
pub struct WebSocketHandler {
|
||||
// TODO
|
||||
port: u16,
|
||||
}
|
||||
|
||||
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())?;
|
||||
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
|
||||
}
|
||||
|
@ -42,7 +45,7 @@ impl std::fmt::Display for ErrorStr {
|
|||
impl std::error::Error for ErrorStr {}
|
||||
|
||||
impl WebSocketHandler {
|
||||
#[allow(dead_code)]
|
||||
/// Instantiate the web socket client for connecting on the specified port
|
||||
pub fn new(port: u16) -> Self {
|
||||
Self { port }
|
||||
}
|
||||
|
@ -50,33 +53,29 @@ impl WebSocketHandler {
|
|||
|
||||
#[async_trait::async_trait]
|
||||
impl ClientHandler for WebSocketHandler {
|
||||
async fn call(&mut self,
|
||||
async fn call(
|
||||
&mut self,
|
||||
package: &str,
|
||||
service: &str,
|
||||
method: &str,
|
||||
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 url = format!(
|
||||
"ws://usdpl-ws-{}.localhost:{}/{}.{}/{}",
|
||||
id,
|
||||
self.port,
|
||||
package,
|
||||
service,
|
||||
method,
|
||||
id, self.port, package, service, method,
|
||||
);
|
||||
let (tx, rx) = async_channel::bounded(1);
|
||||
spawn_local(async move {
|
||||
tx.send(send_recv_ws(
|
||||
url,
|
||||
input
|
||||
).await).await.unwrap_or(());
|
||||
tx.send(send_recv_ws(url, input).await).await.unwrap_or(());
|
||||
});
|
||||
|
||||
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(ErrorStr(e))))?
|
||||
.map_err(|e| ServiceError::Method(Box::new(ErrorStr(e))))?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -20,18 +20,25 @@ pub async fn send_recv_packet(
|
|||
id: u64,
|
||||
packet: socket::Packet,
|
||||
port: u16,
|
||||
#[cfg(feature = "encrypt")]
|
||||
key: Vec<u8>,
|
||||
#[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);
|
||||
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 (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));
|
||||
|
@ -51,30 +58,45 @@ pub async fn send_recv_packet(
|
|||
|
||||
let rust_str = string.as_string().unwrap();
|
||||
#[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"))]
|
||||
{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)?
|
||||
.0)}
|
||||
.0)
|
||||
}
|
||||
|
||||
#[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)?
|
||||
.0)}
|
||||
.0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_call(
|
||||
id: u64,
|
||||
packet: socket::Packet,
|
||||
port: u16,
|
||||
#[cfg(feature = "encrypt")]
|
||||
key: Vec<u8>,
|
||||
#[cfg(feature = "encrypt")] key: Vec<u8>,
|
||||
) -> 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),
|
||||
_ => {
|
||||
//imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url()));
|
||||
|
|
|
@ -6,13 +6,22 @@
|
|||
#![warn(missing_docs)]
|
||||
|
||||
mod client_handler;
|
||||
pub use client_handler::WebSocketHandler;
|
||||
mod connection;
|
||||
mod convert;
|
||||
mod imports;
|
||||
pub mod wasm;
|
||||
|
||||
#[allow(missing_docs)] // existence is pain otherwise
|
||||
/*#[allow(missing_docs)] // existence is pain otherwise
|
||||
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};
|
||||
|
@ -99,7 +108,11 @@ pub fn version_usdpl() -> String {
|
|||
#[wasm_bindgen]
|
||||
pub fn set_value(key: String, value: JsValue) -> JsValue {
|
||||
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]
|
||||
pub fn get_value(key: String) -> JsValue {
|
||||
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,
|
||||
#[cfg(feature = "encrypt")]
|
||||
get_key()
|
||||
get_key(),
|
||||
)
|
||||
.await;
|
||||
let results = match results {
|
||||
|
@ -168,8 +186,10 @@ pub async fn init_tr(locale: String) {
|
|||
Packet::Language(locale.clone()),
|
||||
get_port(),
|
||||
#[cfg(feature = "encrypt")]
|
||||
get_key()
|
||||
).await {
|
||||
get_key(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Packet::Translations(translations)) => {
|
||||
#[cfg(feature = "debug")]
|
||||
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);
|
||||
}
|
||||
unsafe { TRANSLATIONS = Some(tr_map) }
|
||||
},
|
||||
}
|
||||
Ok(_) => {
|
||||
#[cfg(feature = "debug")]
|
||||
imports::console_error(&format!("USDPL: Got wrong packet response for init_tr"));
|
||||
unsafe { TRANSLATIONS = None }
|
||||
},
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
Err(e) => {
|
||||
#[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