Minor refactor and code cleanup to improve ergonomics
This commit is contained in:
parent
63ed7fa5d1
commit
0a5323c927
25 changed files with 467 additions and 713 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -926,7 +926,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "usdpl"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"js-sys",
|
||||
|
@ -940,10 +940,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "usdpl-back"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"lazy_static",
|
||||
"tokio",
|
||||
"usdpl-core",
|
||||
"warp",
|
||||
|
@ -951,7 +950,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "usdpl-core"
|
||||
version = "0.1.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
|
6
build.sh
6
build.sh
|
@ -7,9 +7,11 @@ $0 [decky|crankshaft|<nothing>]"
|
|||
elif [ "$1" == "decky" ]; then
|
||||
echo "Building back & front for decky framework"
|
||||
# usdpl-back
|
||||
echo "...Running usdpl-back build..."
|
||||
cd ./usdpl-back
|
||||
./build.sh decky
|
||||
# usdpl-front
|
||||
echo "...Running usdpl-front build..."
|
||||
cd ../usdpl-front
|
||||
./build.sh decky
|
||||
cd ..
|
||||
|
@ -35,11 +37,11 @@ else
|
|||
# usdpl-back
|
||||
echo "...Running usdpl-back build..."
|
||||
cd ./usdpl-back
|
||||
cargo build --release
|
||||
./build.sh
|
||||
# usdpl-front
|
||||
echo "...Running usdpl-front build..."
|
||||
cd ../usdpl-front
|
||||
./build.sh crankshaft
|
||||
./build.sh
|
||||
cd ..
|
||||
echo "Built usdpl back & front for any"
|
||||
fi
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "usdpl-back"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://github.com/NGnius/usdpl-rs"
|
||||
|
@ -13,10 +13,9 @@ decky = []
|
|||
crankshaft = []
|
||||
|
||||
[dependencies]
|
||||
usdpl-core = { version = "0.3.0", path = "../usdpl-core" }
|
||||
usdpl-core = { version = "0.4.0", path = "../usdpl-core" }
|
||||
|
||||
# HTTP web framework
|
||||
warp = { version = "0.3" }
|
||||
bytes = { version = "1.1" }
|
||||
tokio = { version = "1.19", features = ["rt", "rt-multi-thread"] }
|
||||
lazy_static = { version = "1.4" }
|
||||
|
|
5
usdpl-back/src/callable.rs
Normal file
5
usdpl-back/src/callable.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
use usdpl_core::serdes::Primitive;
|
||||
|
||||
pub trait Callable: Send + Sync {
|
||||
fn call(&mut self, params: Vec<Primitive>) -> Vec<Primitive>;
|
||||
}
|
|
@ -1,24 +1,19 @@
|
|||
//use std::net::{TcpListener, TcpStream, SocketAddr};
|
||||
use std::collections::HashMap;
|
||||
//use std::sync::Arc;
|
||||
//use std::io::{Read, Write};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use warp::Filter;
|
||||
|
||||
use usdpl_core::serdes::{Dumpable, Loadable, Primitive};
|
||||
use usdpl_core::{RemoteCallResponse, socket};
|
||||
use usdpl_core::serdes::{Dumpable, Loadable};
|
||||
use usdpl_core::{socket, RemoteCallResponse};
|
||||
|
||||
type Callable = Box<(dyn (Fn(Vec<Primitive>) -> Vec<Primitive>) + Send + Sync)>;
|
||||
use super::Callable;
|
||||
|
||||
lazy_static! {
|
||||
static ref CALLS: std::sync::Mutex<HashMap<String, Callable>> = std::sync::Mutex::new(HashMap::new());
|
||||
}
|
||||
type WrappedCallable = Arc<Mutex<Box<dyn Callable>>>; // thread-safe, cloneable Callable
|
||||
|
||||
/// Back-end instance for interacting with the front-end
|
||||
pub struct Instance {
|
||||
//calls: HashMap<String, Callable>,
|
||||
calls: HashMap<String, WrappedCallable>,
|
||||
port: u16,
|
||||
}
|
||||
|
||||
|
@ -27,86 +22,25 @@ impl Instance {
|
|||
#[inline]
|
||||
pub fn new(port_usdpl: u16) -> Self {
|
||||
Instance {
|
||||
//calls: HashMap::new(),
|
||||
calls: HashMap::new(),
|
||||
port: port_usdpl,
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a function which can be invoked by the front-end
|
||||
pub fn register<S: std::convert::Into<String>, F: (Fn(Vec<Primitive>) -> Vec<Primitive>) + Send + Sync + 'static>(&mut self, name: S, f: F) -> &mut Self {
|
||||
CALLS.lock().unwrap().insert(name.into(), Box::new(f));
|
||||
//self.calls.insert(name.into(), Box::new(f));
|
||||
pub fn register<S: std::convert::Into<String>, F: Callable + 'static>(
|
||||
&mut self,
|
||||
name: S,
|
||||
f: F,
|
||||
) -> &mut Self {
|
||||
//CALLS.lock().unwrap().insert(name.into(), Mutex::new(Box::new(f)));
|
||||
self.calls
|
||||
.insert(name.into(), Arc::new(Mutex::new(Box::new(f))));
|
||||
self
|
||||
}
|
||||
|
||||
/*fn handle_packet<const ERROR: bool>(&mut self, packet: socket::Packet, buffer: &mut [u8], peer_addr: &SocketAddr) -> Result<String, super::ServerError> {
|
||||
match packet {
|
||||
socket::Packet::Call(obj) => {
|
||||
if let Some(target_func) = self.calls.get_mut(&obj.function) {
|
||||
// TODO: multithread this
|
||||
let result = target_func(obj.parameters);
|
||||
let response = socket::Packet::CallResponse(RemoteCallResponse {
|
||||
id: obj.id,
|
||||
response: result,
|
||||
});
|
||||
let (ok, len) = response.dump(buffer);
|
||||
if !ok && ERROR {
|
||||
return Err(super::ServerError::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Cannot dump return value of function `{}`", &obj.function))));
|
||||
}
|
||||
if ERROR {
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.extend_from_slice(&buffer[..len]);
|
||||
incoming.write_message(Message::Binary(vec)).map_err(super::ServerError::Tungstenite)?;
|
||||
} else {
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.extend_from_slice(&buffer[..len]);
|
||||
incoming.write_message(Message::Binary(vec)).unwrap_or_default();
|
||||
}
|
||||
} else {
|
||||
if ERROR {
|
||||
return Err(super::ServerError::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Invalid remote call `{}` received from {}", obj.function, peer_addr))));
|
||||
} else {
|
||||
eprintln!("Invalid remote call `{}` received from {}", obj.function, peer_addr);
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
socket::Packet::Many(many) => {
|
||||
for packet in many {
|
||||
if let socket::Packet::Many(_) = packet {
|
||||
// drop nested socket packets (prevents DoS and bad practices)
|
||||
if ERROR {
|
||||
return Err(super::ServerError::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Invalid nested Many packet received from {}", peer_addr))));
|
||||
} else {
|
||||
eprintln!("Invalid nested Many packet received from {}", peer_addr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
self.handle_packet::<ERROR>(packet, buffer, incoming, peer_addr)?;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
let (ok, len) = socket::Packet::Unsupported.dump(buffer);
|
||||
if !ok && ERROR {
|
||||
return Err(super::ServerError::Io(std::io::Error::new(std::io::ErrorKind::Unsupported, format!("Cannot dump unsupported packet"))));
|
||||
}
|
||||
if ERROR {
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.extend_from_slice(&buffer[..len]);
|
||||
incoming.write_message(Message::Binary(vec)).map_err(super::ServerError::Tungstenite)?;
|
||||
} else {
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
vec.extend_from_slice(&buffer[..len]);
|
||||
incoming.write_message(Message::Binary(vec)).unwrap_or_default();
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}*/
|
||||
|
||||
pub fn serve(self) -> super::ServerResult {
|
||||
pub fn serve(&self) -> Result<(), ()> {
|
||||
let result = self.serve_internal();
|
||||
//println!("Stopping server due to serve_internal returning a result");
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
|
@ -114,13 +48,18 @@ impl Instance {
|
|||
.block_on(result)
|
||||
}
|
||||
|
||||
fn handle_call(packet: socket::Packet) -> socket::Packet {
|
||||
println!("Got packet");
|
||||
fn handle_call(
|
||||
packet: socket::Packet,
|
||||
handlers: &HashMap<String, WrappedCallable>,
|
||||
) -> socket::Packet {
|
||||
match packet {
|
||||
socket::Packet::Call(call) => {
|
||||
let handlers = CALLS.lock().expect("Failed to acquite CALLS lock");
|
||||
//let handlers = CALLS.lock().expect("Failed to acquire CALLS lock");
|
||||
if let Some(target) = handlers.get(&call.function) {
|
||||
let result = target(call.parameters);
|
||||
let result = target
|
||||
.lock()
|
||||
.expect("Failed to acquire CALLS.function lock")
|
||||
.call(call.parameters);
|
||||
socket::Packet::CallResponse(RemoteCallResponse {
|
||||
id: call.id,
|
||||
response: result,
|
||||
|
@ -128,51 +67,61 @@ impl Instance {
|
|||
} else {
|
||||
socket::Packet::Invalid
|
||||
}
|
||||
},
|
||||
}
|
||||
socket::Packet::Many(packets) => {
|
||||
let mut result = Vec::with_capacity(packets.len());
|
||||
for packet in packets {
|
||||
result.push(Self::handle_call(packet));
|
||||
result.push(Self::handle_call(packet, handlers));
|
||||
}
|
||||
socket::Packet::Many(result)
|
||||
},
|
||||
}
|
||||
_ => socket::Packet::Invalid,
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive and execute callbacks forever
|
||||
pub async fn serve_internal(self) -> super::ServerResult {
|
||||
//let handlers = self.calls;
|
||||
pub async fn serve_internal(&self) -> Result<(), ()> {
|
||||
let handlers = self.calls.clone();
|
||||
//self.calls = HashMap::new();
|
||||
let calls = warp::post()
|
||||
.and(warp::path("usdpl/call"))
|
||||
.and(warp::body::content_length_limit((socket::PACKET_BUFFER_SIZE * 2) as _))
|
||||
.and(warp::path!("usdpl" / "call"))
|
||||
.and(warp::body::content_length_limit(
|
||||
(socket::PACKET_BUFFER_SIZE * 2) as _,
|
||||
))
|
||||
.and(warp::body::bytes())
|
||||
.map(|data: bytes::Bytes| {
|
||||
let (obj_maybe, _) = socket::Packet::load_base64(&data);
|
||||
.map(move |data: bytes::Bytes| {
|
||||
let (packet, _) = match socket::Packet::load_base64(&data) {
|
||||
Ok(x) => x,
|
||||
Err(_) => {
|
||||
return warp::reply::with_status(
|
||||
warp::http::Response::builder()
|
||||
.body("Failed to load packet".to_string()),
|
||||
warp::http::StatusCode::from_u16(400).unwrap(),
|
||||
)
|
||||
}
|
||||
};
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
if let Some(packet) = obj_maybe {
|
||||
let response = Self::handle_call(packet);
|
||||
let (ok, len) = response.dump_base64(&mut buffer);
|
||||
if !ok {
|
||||
eprintln!("Failed to dump response packet");
|
||||
warp::reply::with_status(warp::http::Response::builder()
|
||||
.body("Failed to dump response packet".to_string()), warp::http::StatusCode::from_u16(400).unwrap())
|
||||
} else {
|
||||
let response = Self::handle_call(packet, &handlers);
|
||||
let len = match response.dump_base64(&mut buffer) {
|
||||
Ok(x) => x,
|
||||
Err(_) => {
|
||||
return warp::reply::with_status(
|
||||
warp::http::Response::builder()
|
||||
.body("Failed to dump response packet".to_string()),
|
||||
warp::http::StatusCode::from_u16(500).unwrap(),
|
||||
)
|
||||
}
|
||||
};
|
||||
let string: String = String::from_utf8_lossy(&buffer[..len]).into();
|
||||
warp::reply::with_status(warp::http::Response::builder()
|
||||
.body(string), warp::http::StatusCode::from_u16(200).unwrap())
|
||||
}
|
||||
} else {
|
||||
eprintln!("Failed to load packet");
|
||||
warp::reply::with_status(warp::http::Response::builder()
|
||||
.body("Failed to load packet".to_string()), warp::http::StatusCode::from_u16(400).unwrap())
|
||||
}
|
||||
warp::reply::with_status(
|
||||
warp::http::Response::builder().body(string),
|
||||
warp::http::StatusCode::from_u16(200).unwrap(),
|
||||
)
|
||||
})
|
||||
.map(|reply| {
|
||||
warp::reply::with_header(reply, "Access-Control-Allow-Origin", "*")
|
||||
});
|
||||
|
||||
.map(|reply| warp::reply::with_header(reply, "Access-Control-Allow-Origin", "*"));
|
||||
#[cfg(debug_assertions)]
|
||||
warp::serve(calls).run(([0, 0, 0, 0], self.port)).await;
|
||||
#[cfg(not(debug_assertions))]
|
||||
warp::serve(calls).run(([127, 0, 0, 1], self.port)).await;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -180,110 +129,6 @@ impl Instance {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//use std::net::TcpStream;
|
||||
//use super::*;
|
||||
|
||||
//const PORT: u16 = 31337;
|
||||
|
||||
/*#[test]
|
||||
fn serve_full_test() -> std::io::Result<()> {
|
||||
let _server = std::thread::spawn(|| {
|
||||
Instance::new(PORT)
|
||||
.register("echo".to_string(), &mut |params| params)
|
||||
.register("hello".to_string(), &mut |params| {
|
||||
if let Some(Primitive::String(name)) = params.get(0) {
|
||||
vec![Primitive::String(format!("Hello {}", name))]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
})
|
||||
.serve::<true>()
|
||||
});
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
let mut front = TcpStream::connect(socket::socket_addr(PORT)).unwrap();
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let call = socket::Packet::Call(usdpl_core::RemoteCall {
|
||||
id: 42,
|
||||
function: "hello".to_string(),
|
||||
parameters: vec![Primitive::String("USDPL".to_string())]
|
||||
});
|
||||
let (ok, len) = call.dump(&mut buffer);
|
||||
assert!(ok, "Packet dump failed");
|
||||
assert_eq!(len, 32, "Packet dumped wrong amount of data");
|
||||
front.write(&buffer[..len])?;
|
||||
let len = front.read(&mut buffer)?;
|
||||
let (response, len) = socket::Packet::load(&buffer[..len]);
|
||||
assert!(response.is_some(), "Response load failed");
|
||||
assert_eq!(len, 29, "Response loaded wrong amount of data");
|
||||
let response = response.unwrap();
|
||||
if let socket::Packet::CallResponse(resp) = response {
|
||||
assert_eq!(resp.id, 42);
|
||||
if let Some(Primitive::String(s)) = resp.response.get(0) {
|
||||
assert_eq!(s, "Hello USDPL");
|
||||
} else {
|
||||
panic!("Wrong response data");
|
||||
}
|
||||
} else {
|
||||
panic!("Wrong response packet type");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn serve_err_test() {
|
||||
let _client = std::thread::spawn(|| {
|
||||
std::thread::sleep(std::time::Duration::from_millis(100));
|
||||
let mut front = TcpStream::connect(socket::socket_addr(PORT+1)).unwrap();
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let (_, len) = socket::Packet::Bad.dump(&mut buffer);
|
||||
front.write(&buffer[..len]).unwrap();
|
||||
let _ = front.read(&mut buffer).unwrap();
|
||||
});
|
||||
Instance::new(PORT+1)
|
||||
.register("echo".to_string(), &mut |params| params)
|
||||
.register("hello".to_string(), &mut |params| {
|
||||
if let Some(Primitive::String(name)) = params.get(0) {
|
||||
vec![Primitive::String(format!("Hello {}", name))]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
})
|
||||
.serve::<true>()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn serve_unsupported_test() {
|
||||
let _server = std::thread::spawn(|| {
|
||||
Instance::new(PORT+2)
|
||||
.register("echo".to_string(), &mut |params| params)
|
||||
.register("hello".to_string(), &mut |params| {
|
||||
if let Some(Primitive::String(name)) = params.get(0) {
|
||||
vec![Primitive::String(format!("Hello {}", name))]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
})
|
||||
.serve::<true>()
|
||||
});
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
let mut front = TcpStream::connect(socket::socket_addr(PORT+2)).unwrap();
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let (ok, len) = socket::Packet::Unsupported.dump(&mut buffer);
|
||||
assert!(ok, "Packet dump failed");
|
||||
assert_eq!(len, 32, "Packet dumped wrong amount of data");
|
||||
front.write(&buffer[..len]).unwrap();
|
||||
let len = front.read(&mut buffer).unwrap();
|
||||
let (response, len) = socket::Packet::load(&buffer[..len]);
|
||||
assert!(response.is_some(), "Response load failed");
|
||||
assert_eq!(len, 29, "Response loaded wrong amount of data");
|
||||
let response = response.unwrap();
|
||||
if let socket::Packet::Unsupported = response {
|
||||
} else {
|
||||
panic!("Wrong response packet type");
|
||||
}
|
||||
}*/
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,13 @@
|
|||
//! This is a minimalist TCP server for handling events from the front-end.
|
||||
//!
|
||||
|
||||
mod errors;
|
||||
mod callable;
|
||||
//mod errors;
|
||||
mod instance;
|
||||
|
||||
pub use callable::Callable;
|
||||
pub use instance::Instance;
|
||||
pub use errors::{ServerError, ServerResult};
|
||||
//pub use errors::{ServerError, ServerResult};
|
||||
|
||||
pub mod core {
|
||||
pub use usdpl_core::*;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "usdpl-core"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
repository = "https://github.com/NGnius/usdpl-rs"
|
||||
|
|
1
usdpl-core/src/api_any/mod.rs
Normal file
1
usdpl-core/src/api_any/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
3
usdpl-core/src/api_common/mod.rs
Normal file
3
usdpl-core/src/api_common/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
mod target;
|
||||
|
||||
pub use target::Platform;
|
32
usdpl-core/src/api_common/target.rs
Normal file
32
usdpl-core/src/api_common/target.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
pub enum Platform {
|
||||
Any,
|
||||
Decky,
|
||||
Crankshaft,
|
||||
}
|
||||
|
||||
impl Platform {
|
||||
pub fn current() -> Self {
|
||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
||||
{
|
||||
Self::Decky
|
||||
}
|
||||
#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))]
|
||||
{
|
||||
Self::Crankshaft
|
||||
}
|
||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
||||
{
|
||||
Self::Any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Platform {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Any => write!(f, "any"),
|
||||
Self::Decky => write!(f, "decky"),
|
||||
Self::Crankshaft => write!(f, "crankshaft"),
|
||||
}
|
||||
}
|
||||
}
|
1
usdpl-core/src/api_crankshaft/mod.rs
Normal file
1
usdpl-core/src/api_crankshaft/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
1
usdpl-core/src/api_decky/mod.rs
Normal file
1
usdpl-core/src/api_decky/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
|
@ -2,7 +2,25 @@
|
|||
//! This contains serialization functionality and networking datatypes.
|
||||
mod remote_call;
|
||||
|
||||
pub mod socket;
|
||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
||||
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"))))]
|
||||
mod api_decky;
|
||||
|
||||
pub mod serdes;
|
||||
pub mod socket;
|
||||
|
||||
pub use remote_call::{RemoteCall, RemoteCallResponse};
|
||||
|
||||
pub mod api {
|
||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
||||
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"))))]
|
||||
pub use super::api_decky::*;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::serdes::{Primitive, Loadable, Dumpable};
|
||||
use crate::serdes::{DumpError, Dumpable, LoadError, Loadable, Primitive};
|
||||
|
||||
/// Remote call packet representing a function to call on the back-end, sent from the front-end
|
||||
pub struct RemoteCall {
|
||||
|
@ -8,42 +8,27 @@ pub struct RemoteCall {
|
|||
}
|
||||
|
||||
impl Loadable for RemoteCall {
|
||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
||||
let (id_num, len0) = u64::load(buffer);
|
||||
if id_num.is_none() {
|
||||
return (None, len0);
|
||||
}
|
||||
let (function_name, len1) = String::load(&buffer[len0..]);
|
||||
if function_name.is_none() {
|
||||
return (None, len1);
|
||||
}
|
||||
let (params, len2) = Vec::<Primitive>::load(&buffer[len0+len1..]);
|
||||
if params.is_none() {
|
||||
return (None, len1 + len2);
|
||||
}
|
||||
(
|
||||
Some(Self {
|
||||
id: id_num.unwrap(),
|
||||
function: function_name.unwrap(),
|
||||
parameters: params.unwrap(),
|
||||
}),
|
||||
len0 + len1 + len2
|
||||
)
|
||||
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
let (id_num, len0) = u64::load(buffer)?;
|
||||
let (function_name, len1) = String::load(&buffer[len0..])?;
|
||||
let (params, len2) = Vec::<Primitive>::load(&buffer[len0 + len1..])?;
|
||||
Ok((
|
||||
Self {
|
||||
id: id_num,
|
||||
function: function_name,
|
||||
parameters: params,
|
||||
},
|
||||
len0 + len1 + len2,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for RemoteCall {
|
||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize) {
|
||||
let (ok0, len0) = self.id.dump(buffer);
|
||||
if !ok0 {
|
||||
return (ok0, len0);
|
||||
}
|
||||
let (ok1, len1) = self.function.dump(&mut buffer[len0..]);
|
||||
if !ok1 {
|
||||
return (ok1, len1);
|
||||
}
|
||||
let (ok2, len2) = self.parameters.dump(&mut buffer[len0+len1..]);
|
||||
(ok2, len0 + len1 + len2)
|
||||
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||
let len0 = self.id.dump(buffer)?;
|
||||
let len1 = self.function.dump(&mut buffer[len0..])?;
|
||||
let len2 = self.parameters.dump(&mut buffer[len0 + len1..])?;
|
||||
Ok(len0 + len1 + len2)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,33 +39,23 @@ pub struct RemoteCallResponse {
|
|||
}
|
||||
|
||||
impl Loadable for RemoteCallResponse {
|
||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
||||
let (id_num, len0) = u64::load(buffer);
|
||||
if id_num.is_none() {
|
||||
return (None, len0);
|
||||
}
|
||||
let (response_var, len1) = Vec::<Primitive>::load(&buffer[len0..]);
|
||||
if response_var.is_none() {
|
||||
return (None, len1);
|
||||
}
|
||||
(
|
||||
Some(Self {
|
||||
id: id_num.unwrap(),
|
||||
response: response_var.unwrap(),
|
||||
}),
|
||||
len0 + len1
|
||||
)
|
||||
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
let (id_num, len0) = u64::load(buffer)?;
|
||||
let (response_var, len1) = Vec::<Primitive>::load(&buffer[len0..])?;
|
||||
Ok((
|
||||
Self {
|
||||
id: id_num,
|
||||
response: response_var,
|
||||
},
|
||||
len0 + len1,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for RemoteCallResponse {
|
||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize) {
|
||||
let (ok0, len0) = self.id.dump(buffer);
|
||||
if !ok0 {
|
||||
return (ok0, len0);
|
||||
}
|
||||
let (ok1, len1) = self.response.dump(&mut buffer[len0..]);
|
||||
(ok1, len0 + len1)
|
||||
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||
let len0 = self.id.dump(buffer)?;
|
||||
let len1 = self.response.dump(&mut buffer[len0..])?;
|
||||
Ok(len0 + len1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,77 +1,74 @@
|
|||
use super::Dumpable;
|
||||
use super::{DumpError, Dumpable};
|
||||
|
||||
impl Dumpable for String {
|
||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize) {
|
||||
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||
let str_bytes = self.as_bytes();
|
||||
let len_bytes = (str_bytes.len() as u32).to_le_bytes();
|
||||
let total_len = str_bytes.len() + 4;
|
||||
if buffer.len() < total_len {
|
||||
return (false, 0);
|
||||
return Err(DumpError::TooSmallBuffer);
|
||||
}
|
||||
(&mut buffer[..4]).copy_from_slice(&len_bytes);
|
||||
(&mut buffer[4..total_len]).copy_from_slice(str_bytes);
|
||||
(true, total_len)
|
||||
Ok(total_len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Dumpable> Dumpable for Vec<T> {
|
||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize) {
|
||||
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||
let len_bytes = (self.len() as u32).to_le_bytes();
|
||||
(&mut buffer[..4]).copy_from_slice(&len_bytes);
|
||||
let mut cursor = 4;
|
||||
for obj in self.iter() {
|
||||
let (ok, len) = obj.dump(&mut buffer[cursor..]);
|
||||
let len = obj.dump(&mut buffer[cursor..])?;
|
||||
cursor += len;
|
||||
if !ok {
|
||||
return (false, cursor);
|
||||
}
|
||||
}
|
||||
(true, cursor)
|
||||
Ok(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for bool {
|
||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize) {
|
||||
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||
if buffer.len() < 1 {
|
||||
return (false, 0);
|
||||
return Err(DumpError::TooSmallBuffer);
|
||||
}
|
||||
buffer[0] = *self as u8;
|
||||
(true, 1)
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for u8 {
|
||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize) {
|
||||
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||
if buffer.len() < 1 {
|
||||
return (false, 0);
|
||||
return Err(DumpError::TooSmallBuffer);
|
||||
}
|
||||
buffer[0] = *self;
|
||||
(true, 1)
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for i8 {
|
||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize) {
|
||||
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||
if buffer.len() < 1 {
|
||||
return (false, 0);
|
||||
return Err(DumpError::TooSmallBuffer);
|
||||
}
|
||||
buffer[0] = self.to_le_bytes()[0];
|
||||
(true, 1)
|
||||
Ok(1)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! int_impl {
|
||||
($type:ty, $size:literal) => {
|
||||
impl Dumpable for $type {
|
||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize) {
|
||||
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||
if buffer.len() < $size {
|
||||
return (false, 0);
|
||||
return Err(DumpError::TooSmallBuffer);
|
||||
}
|
||||
(&mut buffer[..$size]).copy_from_slice(&self.to_le_bytes());
|
||||
(true, $size)
|
||||
}
|
||||
Ok($size)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int_impl! {u16, 2}
|
||||
|
@ -97,12 +94,11 @@ mod tests {
|
|||
fn $fn_name() {
|
||||
let data = $data;
|
||||
let mut buffer = [0u8; 128];
|
||||
let (ok, write_len) = data.dump(&mut buffer);
|
||||
assert!(ok, "Dump not ok");
|
||||
let write_len = data.dump(&mut buffer).expect("Dump not ok");
|
||||
assert_eq!(write_len, $expected_len, "Wrong amount written");
|
||||
assert_eq!(&buffer[..write_len], $expected_dump);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_impl! {string_dump_test, "test".to_string(), 8, &[4, 0, 0, 0, 116, 101, 115, 116]}
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
use super::Loadable;
|
||||
use super::{LoadError, Loadable};
|
||||
|
||||
impl Loadable for String {
|
||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
||||
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
if buffer.len() < 4 {
|
||||
return (None, 0);
|
||||
return Err(LoadError::TooSmallBuffer);
|
||||
}
|
||||
let mut u32_bytes: [u8; 4] = [u8::MAX; 4];
|
||||
u32_bytes.copy_from_slice(&buffer[..4]);
|
||||
let str_size = u32::from_le_bytes(u32_bytes) as usize;
|
||||
(Some(Self::from_utf8_lossy(&buffer[4..str_size + 4]).into_owned()), str_size + 4)
|
||||
Ok((
|
||||
Self::from_utf8_lossy(&buffer[4..str_size + 4]).into_owned(),
|
||||
str_size + 4,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Loadable> Loadable for Vec<T> {
|
||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
||||
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
if buffer.len() < 4 {
|
||||
return (None, 0);
|
||||
return Err(LoadError::TooSmallBuffer);
|
||||
}
|
||||
let mut u32_bytes: [u8; 4] = [u8::MAX; 4];
|
||||
u32_bytes.copy_from_slice(&buffer[..4]);
|
||||
|
@ -23,59 +26,55 @@ impl<T: Loadable> Loadable for Vec<T> {
|
|||
let mut cursor = 4;
|
||||
let mut items = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
let (obj, len) = T::load(&buffer[cursor..]);
|
||||
let (obj, len) = T::load(&buffer[cursor..])?;
|
||||
cursor += len;
|
||||
if let Some(obj) = obj {
|
||||
items.push(obj);
|
||||
} else {
|
||||
return (None, cursor);
|
||||
}
|
||||
}
|
||||
(Some(items), cursor)
|
||||
Ok((items, cursor))
|
||||
}
|
||||
}
|
||||
|
||||
impl Loadable for bool {
|
||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
||||
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
if buffer.len() < 1 {
|
||||
return (None, 0);
|
||||
return Err(LoadError::TooSmallBuffer);
|
||||
}
|
||||
(Some(buffer[0] != 0), 1)
|
||||
Ok((buffer[0] != 0, 1))
|
||||
}
|
||||
}
|
||||
|
||||
impl Loadable for u8 {
|
||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
||||
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
if buffer.len() < 1 {
|
||||
return (None, 0);
|
||||
return Err(LoadError::TooSmallBuffer);
|
||||
}
|
||||
(Some(buffer[0]), 1)
|
||||
Ok((buffer[0], 1))
|
||||
}
|
||||
}
|
||||
|
||||
impl Loadable for i8 {
|
||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
||||
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
if buffer.len() < 1 {
|
||||
return (None, 0);
|
||||
return Err(LoadError::TooSmallBuffer);
|
||||
}
|
||||
(Some(i8::from_le_bytes([buffer[0]])), 1)
|
||||
Ok((i8::from_le_bytes([buffer[0]]), 1))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! int_impl {
|
||||
($type:ty, $size:literal) => {
|
||||
impl Loadable for $type {
|
||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
||||
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
if buffer.len() < $size {
|
||||
return (None, 0);
|
||||
return Err(LoadError::TooSmallBuffer);
|
||||
}
|
||||
let mut bytes: [u8; $size] = [u8::MAX; $size];
|
||||
bytes.copy_from_slice(&buffer[..$size]);
|
||||
let i = <$type>::from_le_bytes(bytes);
|
||||
(Some(i), $size)
|
||||
}
|
||||
Ok((i, $size))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int_impl! {u16, 2}
|
||||
|
@ -100,12 +99,11 @@ mod tests {
|
|||
#[test]
|
||||
fn $fn_name() {
|
||||
let buffer = $data;
|
||||
let (obj, read_len) = <$type>::load(&buffer);
|
||||
assert!(obj.is_some(), "Load not ok");
|
||||
let (obj, read_len) = <$type>::load(&buffer).expect("Load not ok");
|
||||
assert_eq!(read_len, $expected_len, "Wrong amount read");
|
||||
assert_eq!(obj.unwrap(), $expected_load, "Loaded value not as expected");
|
||||
}
|
||||
assert_eq!(obj, $expected_load, "Loaded value not as expected");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test_impl! {string_load_test, [4u8, 0, 0, 0, 116, 101, 115, 116, 0, 128], String, 8, "test"}
|
||||
|
|
|
@ -6,5 +6,5 @@ mod load_impl;
|
|||
mod primitive;
|
||||
mod traits;
|
||||
|
||||
pub use traits::{Dumpable, Loadable};
|
||||
pub use primitive::Primitive;
|
||||
pub use traits::{DumpError, Dumpable, LoadError, Loadable};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::{Loadable, Dumpable};
|
||||
use super::{DumpError, Dumpable, LoadError, Loadable};
|
||||
|
||||
/// Primitive types supported for communication between the USDPL back- and front-end.
|
||||
/// These are used for sending over the TCP connection.
|
||||
|
@ -33,65 +33,37 @@ impl Primitive {
|
|||
}
|
||||
|
||||
impl Loadable for Primitive {
|
||||
fn load(buf: &[u8]) -> (Option<Self>, usize) {
|
||||
fn load(buf: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
if buf.len() == 0 {
|
||||
return (None, 1);
|
||||
return Err(LoadError::TooSmallBuffer);
|
||||
}
|
||||
let mut result: (Option<Self>, usize) = match buf[0] {
|
||||
let mut result: (Self, usize) = match buf[0] {
|
||||
//0 => (None, 0),
|
||||
1 => (Some(Self::Empty), 0),
|
||||
2 => {
|
||||
let (obj, len) = String::load(&buf[1..]);
|
||||
(obj.map(Self::String), len)
|
||||
},
|
||||
3 => {
|
||||
let (obj, len) = f32::load(&buf[1..]);
|
||||
(obj.map(Self::F32), len)
|
||||
},
|
||||
4 => {
|
||||
let (obj, len) = f64::load(&buf[1..]);
|
||||
(obj.map(Self::F64), len)
|
||||
},
|
||||
5 => {
|
||||
let (obj, len) = u32::load(&buf[1..]);
|
||||
(obj.map(Self::U32), len)
|
||||
},
|
||||
6 => {
|
||||
let (obj, len) = u64::load(&buf[1..]);
|
||||
(obj.map(Self::U64), len)
|
||||
},
|
||||
7 => {
|
||||
let (obj, len) = i32::load(&buf[1..]);
|
||||
(obj.map(Self::I32), len)
|
||||
},
|
||||
8 => {
|
||||
let (obj, len) = i64::load(&buf[1..]);
|
||||
(obj.map(Self::I64), len)
|
||||
},
|
||||
9 => {
|
||||
let (obj, len) = bool::load(&buf[1..]);
|
||||
(obj.map(Self::Bool), len)
|
||||
},
|
||||
10 => {
|
||||
let (obj, len) = String::load(&buf[1..]);
|
||||
(obj.map(Self::Json), len)
|
||||
}
|
||||
_ => (None, 0)
|
||||
1 => (Self::Empty, 0),
|
||||
2 => String::load(&buf[1..]).map(|(obj, len)| (Self::String(obj), len))?,
|
||||
3 => f32::load(&buf[1..]).map(|(obj, len)| (Self::F32(obj), len))?,
|
||||
4 => f64::load(&buf[1..]).map(|(obj, len)| (Self::F64(obj), len))?,
|
||||
5 => u32::load(&buf[1..]).map(|(obj, len)| (Self::U32(obj), len))?,
|
||||
6 => u64::load(&buf[1..]).map(|(obj, len)| (Self::U64(obj), len))?,
|
||||
7 => i32::load(&buf[1..]).map(|(obj, len)| (Self::I32(obj), len))?,
|
||||
8 => i64::load(&buf[1..]).map(|(obj, len)| (Self::I64(obj), len))?,
|
||||
9 => bool::load(&buf[1..]).map(|(obj, len)| (Self::Bool(obj), len))?,
|
||||
10 => String::load(&buf[1..]).map(|(obj, len)| (Self::Json(obj), len))?,
|
||||
_ => return Err(LoadError::InvalidData),
|
||||
};
|
||||
result.1 += 1;
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Dumpable for Primitive {
|
||||
fn dump(&self, buf: &mut [u8]) -> (bool, usize) {
|
||||
fn dump(&self, buf: &mut [u8]) -> Result<usize, DumpError> {
|
||||
if buf.len() == 0 {
|
||||
return (false, 0);
|
||||
return Err(DumpError::TooSmallBuffer);
|
||||
}
|
||||
buf[0] = self.discriminant();
|
||||
let mut result = match self {
|
||||
Self::Empty => (true, 0),
|
||||
Self::Empty => Ok(0),
|
||||
Self::String(s) => s.dump(&mut buf[1..]),
|
||||
Self::F32(x) => x.dump(&mut buf[1..]),
|
||||
Self::F64(x) => x.dump(&mut buf[1..]),
|
||||
|
@ -101,9 +73,9 @@ impl Dumpable for Primitive {
|
|||
Self::I64(x) => x.dump(&mut buf[1..]),
|
||||
Self::Bool(x) => x.dump(&mut buf[1..]),
|
||||
Self::Json(x) => x.dump(&mut buf[1..]),
|
||||
};
|
||||
result.1 += 1;
|
||||
result
|
||||
}?;
|
||||
result += 1;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,12 +106,13 @@ mod tests {
|
|||
let data = "Test";
|
||||
let primitive = Primitive::String(data.to_string());
|
||||
let mut buffer = [0u8; 128];
|
||||
let (ok, write_len) = primitive.dump(&mut buffer);
|
||||
assert!(ok, "Dump not ok");
|
||||
let (obj, read_len) = Primitive::load(&buffer);
|
||||
assert_eq!(write_len, read_len, "Amount written and amount read do not match");
|
||||
assert!(obj.is_some(), "Load not ok");
|
||||
if let Some(Primitive::String(result)) = obj {
|
||||
let write_len = primitive.dump(&mut buffer).expect("Dump not ok");
|
||||
let (obj, read_len) = Primitive::load(&buffer).expect("Load not ok");
|
||||
assert_eq!(
|
||||
write_len, read_len,
|
||||
"Amount written and amount read do not match"
|
||||
);
|
||||
if let Primitive::String(result) = obj {
|
||||
assert_eq!(data, result, "Data written and read does not match");
|
||||
} else {
|
||||
panic!("Read non-string primitive");
|
||||
|
@ -150,12 +123,13 @@ mod tests {
|
|||
fn empty_idempotence_test() {
|
||||
let primitive = Primitive::Empty;
|
||||
let mut buffer = [0u8; 128];
|
||||
let (ok, write_len) = primitive.dump(&mut buffer);
|
||||
assert!(ok, "Dump not ok");
|
||||
let (obj, read_len) = Primitive::load(&buffer);
|
||||
assert_eq!(write_len, read_len, "Amount written and amount read do not match");
|
||||
assert!(obj.is_some(), "Load not ok");
|
||||
if let Some(Primitive::Empty) = obj {
|
||||
let write_len = primitive.dump(&mut buffer).expect("Dump not ok");
|
||||
let (obj, read_len) = Primitive::load(&buffer).expect("Load not ok");
|
||||
assert_eq!(
|
||||
write_len, read_len,
|
||||
"Amount written and amount read do not match"
|
||||
);
|
||||
if let Primitive::Empty = obj {
|
||||
//assert_eq!(data, result, "Data written and read does not match");
|
||||
} else {
|
||||
panic!("Read non-string primitive");
|
||||
|
|
|
@ -1,36 +1,71 @@
|
|||
use base64::{encode_config_slice, decode_config_slice, Config};
|
||||
use base64::{decode_config_slice, encode_config_slice, Config};
|
||||
|
||||
const B64_CONF: Config = Config::new(base64::CharacterSet::Standard, true);
|
||||
|
||||
/// Errors from Loadable::load
|
||||
#[derive(Debug)]
|
||||
pub enum LoadError {
|
||||
TooSmallBuffer,
|
||||
InvalidData,
|
||||
#[cfg(debug_assertions)]
|
||||
Todo,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LoadError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::TooSmallBuffer => write!(f, "LoadError: TooSmallBuffer"),
|
||||
Self::InvalidData => write!(f, "LoadError: InvalidData"),
|
||||
#[cfg(debug_assertions)]
|
||||
Self::Todo => write!(f, "LoadError: TODO!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Load an object from the buffer
|
||||
pub trait Loadable: Sized {
|
||||
/// Read the buffer, building the object and returning the amount of bytes read.
|
||||
/// If anything is wrong with the buffer, None should be returned.
|
||||
fn load(buffer: &[u8]) -> (Option<Self>, usize);
|
||||
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError>;
|
||||
|
||||
fn load_base64(buffer: &[u8]) -> (Option<Self>, usize) {
|
||||
fn load_base64(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
let mut buffer2 = [0u8; crate::socket::PACKET_BUFFER_SIZE];
|
||||
let len = match decode_config_slice(buffer, B64_CONF, &mut buffer2) {
|
||||
Ok(len) => len,
|
||||
Err(_) => return (None, 0),
|
||||
};
|
||||
let len = decode_config_slice(buffer, B64_CONF, &mut buffer2)
|
||||
.map_err(|_| LoadError::InvalidData)?;
|
||||
Self::load(&buffer2[..len])
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors from Dumpable::dump
|
||||
#[derive(Debug)]
|
||||
pub enum DumpError {
|
||||
TooSmallBuffer,
|
||||
Unsupported,
|
||||
#[cfg(debug_assertions)]
|
||||
Todo,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for DumpError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::TooSmallBuffer => write!(f, "DumpError: TooSmallBuffer"),
|
||||
Self::Unsupported => write!(f, "DumpError: Unsupported"),
|
||||
#[cfg(debug_assertions)]
|
||||
Self::Todo => write!(f, "DumpError: TODO!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dump an object into the buffer
|
||||
pub trait Dumpable {
|
||||
/// Write the object to the buffer, returning the amount of bytes written.
|
||||
/// If anything is wrong, false should be returned.
|
||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize);
|
||||
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError>;
|
||||
|
||||
fn dump_base64(&self, buffer: &mut [u8]) -> (bool, usize) {
|
||||
fn dump_base64(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||
let mut buffer2 = [0u8; crate::socket::PACKET_BUFFER_SIZE];
|
||||
let (ok, len) = self.dump(&mut buffer2);
|
||||
if !ok {
|
||||
return (false, len)
|
||||
}
|
||||
let len = self.dump(&mut buffer2)?;
|
||||
let len = encode_config_slice(&buffer2[..len], B64_CONF, buffer);
|
||||
(true, len)
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::net::{SocketAddrV4, SocketAddr, Ipv4Addr};
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
|
||||
use crate::serdes::{Loadable, Dumpable};
|
||||
use crate::serdes::{DumpError, Dumpable, LoadError, Loadable};
|
||||
use crate::{RemoteCall, RemoteCallResponse};
|
||||
|
||||
pub const HOST_STR: &str = "127.0.0.1";
|
||||
|
@ -42,56 +42,56 @@ impl Packet {
|
|||
}
|
||||
|
||||
impl Loadable for Packet {
|
||||
fn load(buf: &[u8]) -> (Option<Self>, usize) {
|
||||
fn load(buf: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||
if buf.len() == 0 {
|
||||
return (None, 1);
|
||||
return Err(LoadError::TooSmallBuffer);
|
||||
}
|
||||
let mut result: (Option<Self>, usize) = match buf[0] {
|
||||
let mut result: (Self, usize) = match buf[0] {
|
||||
//0 => (None, 0),
|
||||
1 => {
|
||||
let (obj, len) = RemoteCall::load(&buf[1..]);
|
||||
(obj.map(Self::Call), len)
|
||||
},
|
||||
2 => {
|
||||
let (obj, len) = RemoteCallResponse::load(&buf[1..]);
|
||||
(obj.map(Self::CallResponse), len)
|
||||
},
|
||||
3 => (Some(Self::KeepAlive), 0),
|
||||
4 => (Some(Self::Invalid), 0),
|
||||
5 => {
|
||||
let (obj, len) = String::load(&buf[1..]);
|
||||
(obj.map(Self::Message), len)
|
||||
},
|
||||
6 => (Some(Self::Unsupported), 0),
|
||||
7 => (None, 0),
|
||||
8 => {
|
||||
let (obj, len) = <_>::load(&buf[1..]);
|
||||
(obj.map(Self::Many), len)
|
||||
let (obj, len) = RemoteCall::load(&buf[1..])?;
|
||||
(Self::Call(obj), len)
|
||||
}
|
||||
_ => (None, 0)
|
||||
2 => {
|
||||
let (obj, len) = RemoteCallResponse::load(&buf[1..])?;
|
||||
(Self::CallResponse(obj), len)
|
||||
}
|
||||
3 => (Self::KeepAlive, 0),
|
||||
4 => (Self::Invalid, 0),
|
||||
5 => {
|
||||
let (obj, len) = String::load(&buf[1..])?;
|
||||
(Self::Message(obj), len)
|
||||
}
|
||||
6 => (Self::Unsupported, 0),
|
||||
7 => return Err(LoadError::InvalidData),
|
||||
8 => {
|
||||
let (obj, len) = <_>::load(&buf[1..])?;
|
||||
(Self::Many(obj), len)
|
||||
}
|
||||
_ => return Err(LoadError::InvalidData),
|
||||
};
|
||||
result.1 += 1;
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dumpable for Packet {
|
||||
fn dump(&self, buf: &mut [u8]) -> (bool, usize) {
|
||||
fn dump(&self, buf: &mut [u8]) -> Result<usize, DumpError> {
|
||||
if buf.len() == 0 {
|
||||
return (false, 0);
|
||||
return Err(DumpError::TooSmallBuffer);
|
||||
}
|
||||
buf[0] = self.discriminant();
|
||||
let mut result = match self {
|
||||
Self::Call(c) => c.dump(&mut buf[1..]),
|
||||
Self::CallResponse(c) => c.dump(&mut buf[1..]),
|
||||
Self::KeepAlive => (true, 0),
|
||||
Self::Invalid => (true, 0),
|
||||
Self::KeepAlive => Ok(0),
|
||||
Self::Invalid => Ok(0),
|
||||
Self::Message(s) => s.dump(&mut buf[1..]),
|
||||
Self::Unsupported => (true, 0),
|
||||
Self::Bad => (false, 0),
|
||||
Self::Unsupported => Ok(0),
|
||||
Self::Bad => return Err(DumpError::Unsupported),
|
||||
Self::Many(v) => v.dump(&mut buf[1..]),
|
||||
};
|
||||
result.1 += 1;
|
||||
result
|
||||
}?;
|
||||
result += 1;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "usdpl"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
||||
edition = "2021"
|
||||
license = "GPL-3.0-only"
|
||||
|
@ -43,7 +43,7 @@ web-sys = { version = "0.3", features = [
|
|||
]}#["WebSocket", "MessageEvent", "ErrorEvent", "BinaryType"] }
|
||||
js-sys = { version = "0.3" }
|
||||
|
||||
usdpl-core = { version = "0.3.0", path = "../usdpl-core" }
|
||||
usdpl-core = { version = "0.4.0", path = "../usdpl-core" }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.13"
|
||||
|
|
|
@ -6,14 +6,12 @@ use wasm_bindgen::JsCast;
|
|||
use wasm_bindgen_futures::JsFuture;
|
||||
|
||||
//use web_sys::{WebSocket, MessageEvent, ErrorEvent};
|
||||
use js_sys::JsString;
|
||||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||
use js_sys::{ArrayBuffer, DataView, Uint8Array, JsString};
|
||||
//use wasm_rs_shared_channel::{Expects, spsc::{Receiver, Sender}};
|
||||
|
||||
use usdpl_core::socket;
|
||||
use usdpl_core::serdes::{Dumpable, Loadable, Primitive};
|
||||
|
||||
use super::imports;
|
||||
use usdpl_core::socket;
|
||||
|
||||
pub async fn send_js(packet: socket::Packet, port: u16) -> Result<Vec<Primitive>, JsValue> {
|
||||
let mut opts = RequestInit::new();
|
||||
|
@ -23,170 +21,36 @@ pub async fn send_js(packet: socket::Packet, port: u16) -> Result<Vec<Primitive>
|
|||
let url = format!("http://localhost:{}/usdpl/call", port);
|
||||
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let (ok, len) = packet.dump_base64(&mut buffer);
|
||||
if !ok {
|
||||
imports::console_error("USDPL error: packet dump failed");
|
||||
return Err("Packet dump failed".into());
|
||||
}
|
||||
let len = packet
|
||||
.dump_base64(&mut buffer)
|
||||
.map_err(super::convert::str_to_js)?;
|
||||
let string: String = String::from_utf8_lossy(&buffer[..len]).into();
|
||||
opts.body(Some(&string.into()));
|
||||
|
||||
let request = Request::new_with_str_and_init(&url, &opts)?;
|
||||
|
||||
request
|
||||
.headers()
|
||||
.set("Accept", "application/bytes")?;
|
||||
request.headers().set("Accept", "application/bytes")?;
|
||||
//.set("Authorization", "wasm TODO_KEY")?;
|
||||
|
||||
let window = web_sys::window().unwrap();
|
||||
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
|
||||
|
||||
let resp: Response = resp_value.dyn_into()?;
|
||||
|
||||
// Convert this other `Promise` into a rust `Future`.
|
||||
let text = JsFuture::from(resp.text()?).await?;
|
||||
|
||||
let string: JsString = text.dyn_into()?;
|
||||
|
||||
match socket::Packet::load_base64(&string.as_string().unwrap().as_bytes()).0 {
|
||||
Some(socket::Packet::CallResponse(resp)) => {
|
||||
Ok(resp.response)
|
||||
},
|
||||
match socket::Packet::load_base64(string.as_string().unwrap().as_bytes())
|
||||
.map_err(super::convert::str_to_js)?
|
||||
.0
|
||||
{
|
||||
socket::Packet::CallResponse(resp) => Ok(resp.response),
|
||||
_ => {
|
||||
imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url()));
|
||||
Err("".into())
|
||||
//imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url()));
|
||||
Err(format!(
|
||||
"Expected call response message from {}, got something else",
|
||||
resp.url()
|
||||
)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*#[allow(dead_code)]
|
||||
/// Send packet over a Javascript socket
|
||||
pub(crate) fn send_js(packet: socket::Packet, port: u16, callback: js_sys::Function) -> bool {
|
||||
let addr = format!("wss://{}:{}",
|
||||
"192.168.0.128",//socket::HOST_STR,
|
||||
port);
|
||||
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let (ok, len) = packet.dump(&mut buffer);
|
||||
if !ok {
|
||||
imports::console_error("USDPL error: packet dump failed");
|
||||
return false;
|
||||
}
|
||||
// copy to JS buffer
|
||||
let array_buffer = ArrayBuffer::new(len as _);
|
||||
let dataview = DataView::new(&array_buffer, 0, len);
|
||||
for i in 0..len {
|
||||
dataview.set_uint8(i, buffer[i]);
|
||||
}
|
||||
|
||||
imports::console_log("USDPL: creating WebSocket");
|
||||
|
||||
let socket = match WebSocket::new("wss://demo.piesocket.com/v3/channel_1?api_key=VCXCEuvhGcBDP7XhiJJUDvR1e1D3eiVjgZ9VRiaV¬ify_self"/*addr*/) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
imports::console_error(
|
||||
&format!("USDPL error: TcpSocket::new(...) failed with error {}",
|
||||
js_sys::JSON::stringify(&e)
|
||||
.map(|x| x.as_string().unwrap_or("WTF".into()))
|
||||
.unwrap_or("unknown error".into())));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
socket.set_binary_type(web_sys::BinaryType::Arraybuffer);
|
||||
//let (tx, rx) : (Sender<_>, Receiver<_>) = wasm_rs_shared_channel::spsc::channel(socket::PACKET_BUFFER_SIZE as u32 + 4).split();
|
||||
|
||||
let onmessage_callback = Closure::wrap(Box::new(onmessage_factory(callback)) as Box<dyn FnMut(MessageEvent)>);
|
||||
//socket.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref()));
|
||||
onmessage_callback.forget();
|
||||
|
||||
let onerror_callback = Closure::wrap(Box::new(onerror_factory()) as Box<dyn FnMut(ErrorEvent)>);
|
||||
socket.set_onerror(Some(onerror_callback.as_ref().unchecked_ref()));
|
||||
onerror_callback.forget();
|
||||
|
||||
let onopen_callback = Closure::wrap(Box::new(onopen_factory(array_buffer, socket.clone())) as Box<dyn FnMut(JsValue)>);
|
||||
socket.set_onopen(Some(onopen_callback.as_ref().unchecked_ref()));
|
||||
onopen_callback.forget();
|
||||
|
||||
imports::console_log("USDPL: socket initialized");
|
||||
true
|
||||
}
|
||||
|
||||
fn onmessage_factory(callback: js_sys::Function) -> impl FnMut(MessageEvent) {
|
||||
move |e: MessageEvent| {
|
||||
super::imports::console_log("USDPL: Got message");
|
||||
if let Ok(buf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
|
||||
//let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let array = Uint8Array::new(&buf);
|
||||
let len = array.byte_length() as usize;
|
||||
super::imports::console_log(&format!("USDPL: Received websocket message with length {}", len));
|
||||
match socket::Packet::load(array.to_vec().as_slice()).0 {
|
||||
Some(socket::Packet::CallResponse(resp)) => {
|
||||
let mut vec: Vec<JsValue> = Vec::with_capacity(resp.response.len());
|
||||
for item in resp.response {
|
||||
vec.push(super::convert::primitive_to_js(item));
|
||||
}
|
||||
let array: js_sys::Array = vec.iter().collect();
|
||||
if let Err(e) = callback.call1(&JsValue::NULL, &array) {
|
||||
imports::console_warn(&format!("USDPL warning: Callback error -- {:?}", e));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", e.origin()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
imports::console_warn(&format!("USDPL warning: Got non-data message from {}", e.origin()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn onerror_factory() -> impl FnMut(ErrorEvent) {
|
||||
move |e: ErrorEvent| {
|
||||
imports::console_error(&format!("USDPL error: got socket error {}", e.message()))
|
||||
}
|
||||
}
|
||||
|
||||
fn onopen_factory(buffer: ArrayBuffer, socket: WebSocket) -> impl FnMut(JsValue) {
|
||||
move |_| {
|
||||
imports::console_log("USDPL: connection opened");
|
||||
match socket.send_with_array_buffer(&buffer) {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
imports::console_error(&format!("USDPL error: socket send_with_array_buffer(...) failed -- {:?}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Send packet over a WASM-native TCP socket
|
||||
pub(crate) fn send_native(packet: socket::Packet, port: u16) -> Option<socket::Packet> {
|
||||
let mut socket = match TcpStream::connect(socket::socket_addr(port)) {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
imports::console_error(&format!("USDPL error: TcpStream failed to connect with error {}", e));
|
||||
return None;
|
||||
},
|
||||
};
|
||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||
let (ok, len) = packet.dump(&mut buffer);
|
||||
if !ok {
|
||||
imports::console_error("USDPL error: packet dump failed");
|
||||
return None;
|
||||
}
|
||||
match socket.write(&buffer[..len]) {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
imports::console_error(&format!("USDPL error: socket write failed with error {}", e));
|
||||
return None;
|
||||
}
|
||||
}
|
||||
let len = match socket.read(&mut buffer) {
|
||||
Ok(len) => len,
|
||||
Err(e) => {
|
||||
imports::console_error(&format!("USDPL error: socket read failed with error {}", e));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
socket::Packet::load(&buffer[..len]).0
|
||||
}*/
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use js_sys::JsString;
|
||||
use js_sys::JSON::{parse, stringify};
|
||||
use wasm_bindgen::prelude::JsValue;
|
||||
use js_sys::JSON::{stringify, parse};
|
||||
|
||||
use usdpl_core::serdes::Primitive;
|
||||
|
||||
|
@ -33,3 +34,7 @@ pub(crate) fn js_to_primitive(val: JsValue) -> Primitive {
|
|||
Primitive::Empty
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn str_to_js<S: std::string::ToString>(s: S) -> JsString {
|
||||
s.to_string().into()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern {
|
||||
extern "C" {
|
||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||
pub fn console_log(s: &str);
|
||||
|
||||
|
|
|
@ -8,17 +8,14 @@ mod connection;
|
|||
mod convert;
|
||||
mod imports;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
use js_sys::Array;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use usdpl_core::{socket::Packet, RemoteCall};
|
||||
//const REMOTE_CALL_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
||||
//const REMOTE_PORT: std::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::new(31337);
|
||||
|
||||
static mut CTX: UsdplContext = UsdplContext {
|
||||
port: 31337,
|
||||
id: 1,
|
||||
};
|
||||
static mut CTX: UsdplContext = UsdplContext { port: 31337, id: 1 };
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Debug)]
|
||||
|
@ -33,7 +30,9 @@ fn get_port() -> u16 {
|
|||
|
||||
fn increment_id() -> u64 {
|
||||
let current_id = unsafe { CTX.id };
|
||||
unsafe {CTX.id += 1;}
|
||||
unsafe {
|
||||
CTX.id += 1;
|
||||
}
|
||||
current_id
|
||||
}
|
||||
|
||||
|
@ -49,29 +48,25 @@ pub fn init_usdpl(port: u16) {
|
|||
console_error_panic_hook::set_once();
|
||||
//REMOTE_PORT.store(port, std::sync::atomic::Ordering::SeqCst);
|
||||
unsafe {
|
||||
CTX = UsdplContext {
|
||||
port: port,
|
||||
id: 1,
|
||||
};
|
||||
CTX = UsdplContext { port: port, id: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the targeted plugin framework, or "any" if unknown
|
||||
#[wasm_bindgen]
|
||||
pub fn target() -> String {
|
||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
||||
{"decky".to_string()}
|
||||
#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))]
|
||||
{"crankshaft".to_string()}
|
||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
||||
{"any".to_string()}
|
||||
usdpl_core::api::Platform::current().to_string()
|
||||
}
|
||||
|
||||
/// Call a function on the back-end.
|
||||
/// Returns null (None) if this fails for any reason.
|
||||
#[wasm_bindgen]
|
||||
pub async fn call_backend(name: String, parameters: Vec<JsValue>) -> JsValue {
|
||||
imports::console_log(&format!("call_backend({}, [params; {}])", name, parameters.len()));
|
||||
imports::console_log(&format!(
|
||||
"call_backend({}, [params; {}])",
|
||||
name,
|
||||
parameters.len()
|
||||
));
|
||||
let next_id = increment_id();
|
||||
let mut params = Vec::with_capacity(parameters.len());
|
||||
for val in parameters {
|
||||
|
@ -79,11 +74,15 @@ pub async fn call_backend(name: String, parameters: Vec<JsValue>) -> JsValue {
|
|||
}
|
||||
let port = get_port();
|
||||
imports::console_log(&format!("USDPL: Got port {}", port));
|
||||
let results = connection::send_js(Packet::Call(RemoteCall {
|
||||
let results = connection::send_js(
|
||||
Packet::Call(RemoteCall {
|
||||
id: next_id,
|
||||
function: name.clone(),
|
||||
parameters: params,
|
||||
}), port).await;
|
||||
}),
|
||||
port,
|
||||
)
|
||||
.await;
|
||||
let results = match results {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
|
|
Loading…
Reference in a new issue