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]]
|
[[package]]
|
||||||
name = "usdpl"
|
name = "usdpl"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
@ -940,10 +940,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "usdpl-back"
|
name = "usdpl-back"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"lazy_static",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"usdpl-core",
|
"usdpl-core",
|
||||||
"warp",
|
"warp",
|
||||||
|
@ -951,7 +950,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "usdpl-core"
|
name = "usdpl-core"
|
||||||
version = "0.1.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
]
|
]
|
||||||
|
|
6
build.sh
6
build.sh
|
@ -7,9 +7,11 @@ $0 [decky|crankshaft|<nothing>]"
|
||||||
elif [ "$1" == "decky" ]; then
|
elif [ "$1" == "decky" ]; then
|
||||||
echo "Building back & front for decky framework"
|
echo "Building back & front for decky framework"
|
||||||
# usdpl-back
|
# usdpl-back
|
||||||
|
echo "...Running usdpl-back build..."
|
||||||
cd ./usdpl-back
|
cd ./usdpl-back
|
||||||
./build.sh decky
|
./build.sh decky
|
||||||
# usdpl-front
|
# usdpl-front
|
||||||
|
echo "...Running usdpl-front build..."
|
||||||
cd ../usdpl-front
|
cd ../usdpl-front
|
||||||
./build.sh decky
|
./build.sh decky
|
||||||
cd ..
|
cd ..
|
||||||
|
@ -35,11 +37,11 @@ else
|
||||||
# usdpl-back
|
# usdpl-back
|
||||||
echo "...Running usdpl-back build..."
|
echo "...Running usdpl-back build..."
|
||||||
cd ./usdpl-back
|
cd ./usdpl-back
|
||||||
cargo build --release
|
./build.sh
|
||||||
# usdpl-front
|
# usdpl-front
|
||||||
echo "...Running usdpl-front build..."
|
echo "...Running usdpl-front build..."
|
||||||
cd ../usdpl-front
|
cd ../usdpl-front
|
||||||
./build.sh crankshaft
|
./build.sh
|
||||||
cd ..
|
cd ..
|
||||||
echo "Built usdpl back & front for any"
|
echo "Built usdpl back & front for any"
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "usdpl-back"
|
name = "usdpl-back"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
repository = "https://github.com/NGnius/usdpl-rs"
|
repository = "https://github.com/NGnius/usdpl-rs"
|
||||||
|
@ -13,10 +13,9 @@ decky = []
|
||||||
crankshaft = []
|
crankshaft = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
usdpl-core = { version = "0.3.0", path = "../usdpl-core" }
|
usdpl-core = { version = "0.4.0", path = "../usdpl-core" }
|
||||||
|
|
||||||
# HTTP web framework
|
# HTTP web framework
|
||||||
warp = { version = "0.3" }
|
warp = { version = "0.3" }
|
||||||
bytes = { version = "1.1" }
|
bytes = { version = "1.1" }
|
||||||
tokio = { version = "1.19", features = ["rt", "rt-multi-thread"] }
|
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::collections::HashMap;
|
||||||
//use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
//use std::io::{Read, Write};
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
|
||||||
use usdpl_core::serdes::{Dumpable, Loadable, Primitive};
|
use usdpl_core::serdes::{Dumpable, Loadable};
|
||||||
use usdpl_core::{RemoteCallResponse, socket};
|
use usdpl_core::{socket, RemoteCallResponse};
|
||||||
|
|
||||||
type Callable = Box<(dyn (Fn(Vec<Primitive>) -> Vec<Primitive>) + Send + Sync)>;
|
use super::Callable;
|
||||||
|
|
||||||
lazy_static! {
|
type WrappedCallable = Arc<Mutex<Box<dyn Callable>>>; // thread-safe, cloneable Callable
|
||||||
static ref CALLS: std::sync::Mutex<HashMap<String, Callable>> = std::sync::Mutex::new(HashMap::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Back-end instance for interacting with the front-end
|
/// Back-end instance for interacting with the front-end
|
||||||
pub struct Instance {
|
pub struct Instance {
|
||||||
//calls: HashMap<String, Callable>,
|
calls: HashMap<String, WrappedCallable>,
|
||||||
port: u16,
|
port: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,86 +22,25 @@ impl Instance {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(port_usdpl: u16) -> Self {
|
pub fn new(port_usdpl: u16) -> Self {
|
||||||
Instance {
|
Instance {
|
||||||
//calls: HashMap::new(),
|
calls: HashMap::new(),
|
||||||
port: port_usdpl,
|
port: port_usdpl,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a function which can be invoked by the front-end
|
/// 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 {
|
pub fn register<S: std::convert::Into<String>, F: Callable + 'static>(
|
||||||
CALLS.lock().unwrap().insert(name.into(), Box::new(f));
|
&mut self,
|
||||||
//self.calls.insert(name.into(), Box::new(f));
|
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/*fn handle_packet<const ERROR: bool>(&mut self, packet: socket::Packet, buffer: &mut [u8], peer_addr: &SocketAddr) -> Result<String, super::ServerError> {
|
pub fn serve(&self) -> Result<(), ()> {
|
||||||
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 {
|
|
||||||
let result = self.serve_internal();
|
let result = self.serve_internal();
|
||||||
//println!("Stopping server due to serve_internal returning a result");
|
|
||||||
tokio::runtime::Builder::new_multi_thread()
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.build()
|
.build()
|
||||||
|
@ -114,13 +48,18 @@ impl Instance {
|
||||||
.block_on(result)
|
.block_on(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_call(packet: socket::Packet) -> socket::Packet {
|
fn handle_call(
|
||||||
println!("Got packet");
|
packet: socket::Packet,
|
||||||
|
handlers: &HashMap<String, WrappedCallable>,
|
||||||
|
) -> socket::Packet {
|
||||||
match packet {
|
match packet {
|
||||||
socket::Packet::Call(call) => {
|
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) {
|
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 {
|
socket::Packet::CallResponse(RemoteCallResponse {
|
||||||
id: call.id,
|
id: call.id,
|
||||||
response: result,
|
response: result,
|
||||||
|
@ -128,51 +67,61 @@ impl Instance {
|
||||||
} else {
|
} else {
|
||||||
socket::Packet::Invalid
|
socket::Packet::Invalid
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
socket::Packet::Many(packets) => {
|
socket::Packet::Many(packets) => {
|
||||||
let mut result = Vec::with_capacity(packets.len());
|
let mut result = Vec::with_capacity(packets.len());
|
||||||
for packet in packets {
|
for packet in packets {
|
||||||
result.push(Self::handle_call(packet));
|
result.push(Self::handle_call(packet, handlers));
|
||||||
}
|
}
|
||||||
socket::Packet::Many(result)
|
socket::Packet::Many(result)
|
||||||
},
|
}
|
||||||
_ => socket::Packet::Invalid,
|
_ => socket::Packet::Invalid,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive and execute callbacks forever
|
/// Receive and execute callbacks forever
|
||||||
pub async fn serve_internal(self) -> super::ServerResult {
|
pub async fn serve_internal(&self) -> Result<(), ()> {
|
||||||
//let handlers = self.calls;
|
let handlers = self.calls.clone();
|
||||||
//self.calls = HashMap::new();
|
//self.calls = HashMap::new();
|
||||||
let calls = warp::post()
|
let calls = warp::post()
|
||||||
.and(warp::path("usdpl/call"))
|
.and(warp::path!("usdpl" / "call"))
|
||||||
.and(warp::body::content_length_limit((socket::PACKET_BUFFER_SIZE * 2) as _))
|
.and(warp::body::content_length_limit(
|
||||||
|
(socket::PACKET_BUFFER_SIZE * 2) as _,
|
||||||
|
))
|
||||||
.and(warp::body::bytes())
|
.and(warp::body::bytes())
|
||||||
.map(|data: bytes::Bytes| {
|
.map(move |data: bytes::Bytes| {
|
||||||
let (obj_maybe, _) = socket::Packet::load_base64(&data);
|
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];
|
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||||
if let Some(packet) = obj_maybe {
|
let response = Self::handle_call(packet, &handlers);
|
||||||
let response = Self::handle_call(packet);
|
let len = match response.dump_base64(&mut buffer) {
|
||||||
let (ok, len) = response.dump_base64(&mut buffer);
|
Ok(x) => x,
|
||||||
if !ok {
|
Err(_) => {
|
||||||
eprintln!("Failed to dump response packet");
|
return warp::reply::with_status(
|
||||||
warp::reply::with_status(warp::http::Response::builder()
|
warp::http::Response::builder()
|
||||||
.body("Failed to dump response packet".to_string()), warp::http::StatusCode::from_u16(400).unwrap())
|
.body("Failed to dump response packet".to_string()),
|
||||||
} else {
|
warp::http::StatusCode::from_u16(500).unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
let string: String = String::from_utf8_lossy(&buffer[..len]).into();
|
let string: String = String::from_utf8_lossy(&buffer[..len]).into();
|
||||||
warp::reply::with_status(warp::http::Response::builder()
|
warp::reply::with_status(
|
||||||
.body(string), warp::http::StatusCode::from_u16(200).unwrap())
|
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())
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.map(|reply| {
|
.map(|reply| warp::reply::with_header(reply, "Access-Control-Allow-Origin", "*"));
|
||||||
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;
|
warp::serve(calls).run(([127, 0, 0, 1], self.port)).await;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -180,110 +129,6 @@ impl Instance {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
//use std::net::TcpStream;
|
#[allow(unused_imports)]
|
||||||
//use super::*;
|
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");
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,13 @@
|
||||||
//! This is a minimalist TCP server for handling events from the front-end.
|
//! This is a minimalist TCP server for handling events from the front-end.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
mod errors;
|
mod callable;
|
||||||
|
//mod errors;
|
||||||
mod instance;
|
mod instance;
|
||||||
|
|
||||||
|
pub use callable::Callable;
|
||||||
pub use instance::Instance;
|
pub use instance::Instance;
|
||||||
pub use errors::{ServerError, ServerResult};
|
//pub use errors::{ServerError, ServerResult};
|
||||||
|
|
||||||
pub mod core {
|
pub mod core {
|
||||||
pub use usdpl_core::*;
|
pub use usdpl_core::*;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "usdpl-core"
|
name = "usdpl-core"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
repository = "https://github.com/NGnius/usdpl-rs"
|
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.
|
//! This contains serialization functionality and networking datatypes.
|
||||||
mod remote_call;
|
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 serdes;
|
||||||
|
pub mod socket;
|
||||||
|
|
||||||
pub use remote_call::{RemoteCall, RemoteCallResponse};
|
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
|
/// Remote call packet representing a function to call on the back-end, sent from the front-end
|
||||||
pub struct RemoteCall {
|
pub struct RemoteCall {
|
||||||
|
@ -8,42 +8,27 @@ pub struct RemoteCall {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loadable for RemoteCall {
|
impl Loadable for RemoteCall {
|
||||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||||
let (id_num, len0) = u64::load(buffer);
|
let (id_num, len0) = u64::load(buffer)?;
|
||||||
if id_num.is_none() {
|
let (function_name, len1) = String::load(&buffer[len0..])?;
|
||||||
return (None, len0);
|
let (params, len2) = Vec::<Primitive>::load(&buffer[len0 + len1..])?;
|
||||||
}
|
Ok((
|
||||||
let (function_name, len1) = String::load(&buffer[len0..]);
|
Self {
|
||||||
if function_name.is_none() {
|
id: id_num,
|
||||||
return (None, len1);
|
function: function_name,
|
||||||
}
|
parameters: params,
|
||||||
let (params, len2) = Vec::<Primitive>::load(&buffer[len0+len1..]);
|
},
|
||||||
if params.is_none() {
|
len0 + len1 + len2,
|
||||||
return (None, len1 + len2);
|
))
|
||||||
}
|
|
||||||
(
|
|
||||||
Some(Self {
|
|
||||||
id: id_num.unwrap(),
|
|
||||||
function: function_name.unwrap(),
|
|
||||||
parameters: params.unwrap(),
|
|
||||||
}),
|
|
||||||
len0 + len1 + len2
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dumpable for RemoteCall {
|
impl Dumpable for RemoteCall {
|
||||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize) {
|
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||||
let (ok0, len0) = self.id.dump(buffer);
|
let len0 = self.id.dump(buffer)?;
|
||||||
if !ok0 {
|
let len1 = self.function.dump(&mut buffer[len0..])?;
|
||||||
return (ok0, len0);
|
let len2 = self.parameters.dump(&mut buffer[len0 + len1..])?;
|
||||||
}
|
Ok(len0 + len1 + len2)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,33 +39,23 @@ pub struct RemoteCallResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loadable for RemoteCallResponse {
|
impl Loadable for RemoteCallResponse {
|
||||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||||
let (id_num, len0) = u64::load(buffer);
|
let (id_num, len0) = u64::load(buffer)?;
|
||||||
if id_num.is_none() {
|
let (response_var, len1) = Vec::<Primitive>::load(&buffer[len0..])?;
|
||||||
return (None, len0);
|
Ok((
|
||||||
}
|
Self {
|
||||||
let (response_var, len1) = Vec::<Primitive>::load(&buffer[len0..]);
|
id: id_num,
|
||||||
if response_var.is_none() {
|
response: response_var,
|
||||||
return (None, len1);
|
},
|
||||||
}
|
len0 + len1,
|
||||||
(
|
))
|
||||||
Some(Self {
|
|
||||||
id: id_num.unwrap(),
|
|
||||||
response: response_var.unwrap(),
|
|
||||||
}),
|
|
||||||
len0 + len1
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dumpable for RemoteCallResponse {
|
impl Dumpable for RemoteCallResponse {
|
||||||
fn dump(&self, buffer: &mut [u8]) -> (bool, usize) {
|
fn dump(&self, buffer: &mut [u8]) -> Result<usize, DumpError> {
|
||||||
let (ok0, len0) = self.id.dump(buffer);
|
let len0 = self.id.dump(buffer)?;
|
||||||
if !ok0 {
|
let len1 = self.response.dump(&mut buffer[len0..])?;
|
||||||
return (ok0, len0);
|
Ok(len0 + len1)
|
||||||
}
|
|
||||||
let (ok1, len1) = self.response.dump(&mut buffer[len0..]);
|
|
||||||
(ok1, len0 + len1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,77 +1,74 @@
|
||||||
use super::Dumpable;
|
use super::{DumpError, Dumpable};
|
||||||
|
|
||||||
impl Dumpable for String {
|
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 str_bytes = self.as_bytes();
|
||||||
let len_bytes = (str_bytes.len() as u32).to_le_bytes();
|
let len_bytes = (str_bytes.len() as u32).to_le_bytes();
|
||||||
let total_len = str_bytes.len() + 4;
|
let total_len = str_bytes.len() + 4;
|
||||||
if buffer.len() < total_len {
|
if buffer.len() < total_len {
|
||||||
return (false, 0);
|
return Err(DumpError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
(&mut buffer[..4]).copy_from_slice(&len_bytes);
|
(&mut buffer[..4]).copy_from_slice(&len_bytes);
|
||||||
(&mut buffer[4..total_len]).copy_from_slice(str_bytes);
|
(&mut buffer[4..total_len]).copy_from_slice(str_bytes);
|
||||||
(true, total_len)
|
Ok(total_len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Dumpable> Dumpable for Vec<T> {
|
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();
|
let len_bytes = (self.len() as u32).to_le_bytes();
|
||||||
(&mut buffer[..4]).copy_from_slice(&len_bytes);
|
(&mut buffer[..4]).copy_from_slice(&len_bytes);
|
||||||
let mut cursor = 4;
|
let mut cursor = 4;
|
||||||
for obj in self.iter() {
|
for obj in self.iter() {
|
||||||
let (ok, len) = obj.dump(&mut buffer[cursor..]);
|
let len = obj.dump(&mut buffer[cursor..])?;
|
||||||
cursor += len;
|
cursor += len;
|
||||||
if !ok {
|
|
||||||
return (false, cursor);
|
|
||||||
}
|
}
|
||||||
}
|
Ok(cursor)
|
||||||
(true, cursor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dumpable for bool {
|
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 {
|
if buffer.len() < 1 {
|
||||||
return (false, 0);
|
return Err(DumpError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
buffer[0] = *self as u8;
|
buffer[0] = *self as u8;
|
||||||
(true, 1)
|
Ok(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dumpable for u8 {
|
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 {
|
if buffer.len() < 1 {
|
||||||
return (false, 0);
|
return Err(DumpError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
buffer[0] = *self;
|
buffer[0] = *self;
|
||||||
(true, 1)
|
Ok(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dumpable for i8 {
|
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 {
|
if buffer.len() < 1 {
|
||||||
return (false, 0);
|
return Err(DumpError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
buffer[0] = self.to_le_bytes()[0];
|
buffer[0] = self.to_le_bytes()[0];
|
||||||
(true, 1)
|
Ok(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! int_impl {
|
macro_rules! int_impl {
|
||||||
($type:ty, $size:literal) => {
|
($type:ty, $size:literal) => {
|
||||||
impl Dumpable for $type {
|
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 {
|
if buffer.len() < $size {
|
||||||
return (false, 0);
|
return Err(DumpError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
(&mut buffer[..$size]).copy_from_slice(&self.to_le_bytes());
|
(&mut buffer[..$size]).copy_from_slice(&self.to_le_bytes());
|
||||||
(true, $size)
|
Ok($size)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
int_impl! {u16, 2}
|
int_impl! {u16, 2}
|
||||||
|
@ -97,12 +94,11 @@ mod tests {
|
||||||
fn $fn_name() {
|
fn $fn_name() {
|
||||||
let data = $data;
|
let data = $data;
|
||||||
let mut buffer = [0u8; 128];
|
let mut buffer = [0u8; 128];
|
||||||
let (ok, write_len) = data.dump(&mut buffer);
|
let write_len = data.dump(&mut buffer).expect("Dump not ok");
|
||||||
assert!(ok, "Dump not ok");
|
|
||||||
assert_eq!(write_len, $expected_len, "Wrong amount written");
|
assert_eq!(write_len, $expected_len, "Wrong amount written");
|
||||||
assert_eq!(&buffer[..write_len], $expected_dump);
|
assert_eq!(&buffer[..write_len], $expected_dump);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
test_impl! {string_dump_test, "test".to_string(), 8, &[4, 0, 0, 0, 116, 101, 115, 116]}
|
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 {
|
impl Loadable for String {
|
||||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||||
if buffer.len() < 4 {
|
if buffer.len() < 4 {
|
||||||
return (None, 0);
|
return Err(LoadError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
let mut u32_bytes: [u8; 4] = [u8::MAX; 4];
|
let mut u32_bytes: [u8; 4] = [u8::MAX; 4];
|
||||||
u32_bytes.copy_from_slice(&buffer[..4]);
|
u32_bytes.copy_from_slice(&buffer[..4]);
|
||||||
let str_size = u32::from_le_bytes(u32_bytes) as usize;
|
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> {
|
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 {
|
if buffer.len() < 4 {
|
||||||
return (None, 0);
|
return Err(LoadError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
let mut u32_bytes: [u8; 4] = [u8::MAX; 4];
|
let mut u32_bytes: [u8; 4] = [u8::MAX; 4];
|
||||||
u32_bytes.copy_from_slice(&buffer[..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 cursor = 4;
|
||||||
let mut items = Vec::with_capacity(count);
|
let mut items = Vec::with_capacity(count);
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let (obj, len) = T::load(&buffer[cursor..]);
|
let (obj, len) = T::load(&buffer[cursor..])?;
|
||||||
cursor += len;
|
cursor += len;
|
||||||
if let Some(obj) = obj {
|
|
||||||
items.push(obj);
|
items.push(obj);
|
||||||
} else {
|
|
||||||
return (None, cursor);
|
|
||||||
}
|
}
|
||||||
}
|
Ok((items, cursor))
|
||||||
(Some(items), cursor)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loadable for bool {
|
impl Loadable for bool {
|
||||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||||
if buffer.len() < 1 {
|
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 {
|
impl Loadable for u8 {
|
||||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||||
if buffer.len() < 1 {
|
if buffer.len() < 1 {
|
||||||
return (None, 0);
|
return Err(LoadError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
(Some(buffer[0]), 1)
|
Ok((buffer[0], 1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loadable for i8 {
|
impl Loadable for i8 {
|
||||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||||
if buffer.len() < 1 {
|
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 {
|
macro_rules! int_impl {
|
||||||
($type:ty, $size:literal) => {
|
($type:ty, $size:literal) => {
|
||||||
impl Loadable for $type {
|
impl Loadable for $type {
|
||||||
fn load(buffer: &[u8]) -> (Option<Self>, usize) {
|
fn load(buffer: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||||
if buffer.len() < $size {
|
if buffer.len() < $size {
|
||||||
return (None, 0);
|
return Err(LoadError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
let mut bytes: [u8; $size] = [u8::MAX; $size];
|
let mut bytes: [u8; $size] = [u8::MAX; $size];
|
||||||
bytes.copy_from_slice(&buffer[..$size]);
|
bytes.copy_from_slice(&buffer[..$size]);
|
||||||
let i = <$type>::from_le_bytes(bytes);
|
let i = <$type>::from_le_bytes(bytes);
|
||||||
(Some(i), $size)
|
Ok((i, $size))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
int_impl! {u16, 2}
|
int_impl! {u16, 2}
|
||||||
|
@ -100,12 +99,11 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn $fn_name() {
|
fn $fn_name() {
|
||||||
let buffer = $data;
|
let buffer = $data;
|
||||||
let (obj, read_len) = <$type>::load(&buffer);
|
let (obj, read_len) = <$type>::load(&buffer).expect("Load not ok");
|
||||||
assert!(obj.is_some(), "Load not ok");
|
|
||||||
assert_eq!(read_len, $expected_len, "Wrong amount read");
|
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"}
|
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 primitive;
|
||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
pub use traits::{Dumpable, Loadable};
|
|
||||||
pub use primitive::Primitive;
|
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.
|
/// Primitive types supported for communication between the USDPL back- and front-end.
|
||||||
/// These are used for sending over the TCP connection.
|
/// These are used for sending over the TCP connection.
|
||||||
|
@ -33,65 +33,37 @@ impl Primitive {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loadable for Primitive {
|
impl Loadable for Primitive {
|
||||||
fn load(buf: &[u8]) -> (Option<Self>, usize) {
|
fn load(buf: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||||
if buf.len() == 0 {
|
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),
|
//0 => (None, 0),
|
||||||
1 => (Some(Self::Empty), 0),
|
1 => (Self::Empty, 0),
|
||||||
2 => {
|
2 => String::load(&buf[1..]).map(|(obj, len)| (Self::String(obj), len))?,
|
||||||
let (obj, len) = String::load(&buf[1..]);
|
3 => f32::load(&buf[1..]).map(|(obj, len)| (Self::F32(obj), len))?,
|
||||||
(obj.map(Self::String), 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))?,
|
||||||
3 => {
|
6 => u64::load(&buf[1..]).map(|(obj, len)| (Self::U64(obj), len))?,
|
||||||
let (obj, len) = f32::load(&buf[1..]);
|
7 => i32::load(&buf[1..]).map(|(obj, len)| (Self::I32(obj), len))?,
|
||||||
(obj.map(Self::F32), 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))?,
|
||||||
4 => {
|
10 => String::load(&buf[1..]).map(|(obj, len)| (Self::Json(obj), len))?,
|
||||||
let (obj, len) = f64::load(&buf[1..]);
|
_ => return Err(LoadError::InvalidData),
|
||||||
(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)
|
|
||||||
};
|
};
|
||||||
result.1 += 1;
|
result.1 += 1;
|
||||||
result
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Dumpable for Primitive {
|
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 {
|
if buf.len() == 0 {
|
||||||
return (false, 0);
|
return Err(DumpError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
buf[0] = self.discriminant();
|
buf[0] = self.discriminant();
|
||||||
let mut result = match self {
|
let mut result = match self {
|
||||||
Self::Empty => (true, 0),
|
Self::Empty => Ok(0),
|
||||||
Self::String(s) => s.dump(&mut buf[1..]),
|
Self::String(s) => s.dump(&mut buf[1..]),
|
||||||
Self::F32(x) => x.dump(&mut buf[1..]),
|
Self::F32(x) => x.dump(&mut buf[1..]),
|
||||||
Self::F64(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::I64(x) => x.dump(&mut buf[1..]),
|
||||||
Self::Bool(x) => x.dump(&mut buf[1..]),
|
Self::Bool(x) => x.dump(&mut buf[1..]),
|
||||||
Self::Json(x) => x.dump(&mut buf[1..]),
|
Self::Json(x) => x.dump(&mut buf[1..]),
|
||||||
};
|
}?;
|
||||||
result.1 += 1;
|
result += 1;
|
||||||
result
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,12 +106,13 @@ mod tests {
|
||||||
let data = "Test";
|
let data = "Test";
|
||||||
let primitive = Primitive::String(data.to_string());
|
let primitive = Primitive::String(data.to_string());
|
||||||
let mut buffer = [0u8; 128];
|
let mut buffer = [0u8; 128];
|
||||||
let (ok, write_len) = primitive.dump(&mut buffer);
|
let write_len = primitive.dump(&mut buffer).expect("Dump not ok");
|
||||||
assert!(ok, "Dump not ok");
|
let (obj, read_len) = Primitive::load(&buffer).expect("Load not ok");
|
||||||
let (obj, read_len) = Primitive::load(&buffer);
|
assert_eq!(
|
||||||
assert_eq!(write_len, read_len, "Amount written and amount read do not match");
|
write_len, read_len,
|
||||||
assert!(obj.is_some(), "Load not ok");
|
"Amount written and amount read do not match"
|
||||||
if let Some(Primitive::String(result)) = obj {
|
);
|
||||||
|
if let Primitive::String(result) = obj {
|
||||||
assert_eq!(data, result, "Data written and read does not match");
|
assert_eq!(data, result, "Data written and read does not match");
|
||||||
} else {
|
} else {
|
||||||
panic!("Read non-string primitive");
|
panic!("Read non-string primitive");
|
||||||
|
@ -150,12 +123,13 @@ mod tests {
|
||||||
fn empty_idempotence_test() {
|
fn empty_idempotence_test() {
|
||||||
let primitive = Primitive::Empty;
|
let primitive = Primitive::Empty;
|
||||||
let mut buffer = [0u8; 128];
|
let mut buffer = [0u8; 128];
|
||||||
let (ok, write_len) = primitive.dump(&mut buffer);
|
let write_len = primitive.dump(&mut buffer).expect("Dump not ok");
|
||||||
assert!(ok, "Dump not ok");
|
let (obj, read_len) = Primitive::load(&buffer).expect("Load not ok");
|
||||||
let (obj, read_len) = Primitive::load(&buffer);
|
assert_eq!(
|
||||||
assert_eq!(write_len, read_len, "Amount written and amount read do not match");
|
write_len, read_len,
|
||||||
assert!(obj.is_some(), "Load not ok");
|
"Amount written and amount read do not match"
|
||||||
if let Some(Primitive::Empty) = obj {
|
);
|
||||||
|
if let Primitive::Empty = obj {
|
||||||
//assert_eq!(data, result, "Data written and read does not match");
|
//assert_eq!(data, result, "Data written and read does not match");
|
||||||
} else {
|
} else {
|
||||||
panic!("Read non-string primitive");
|
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);
|
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
|
/// Load an object from the buffer
|
||||||
pub trait Loadable: Sized {
|
pub trait Loadable: Sized {
|
||||||
/// Read the buffer, building the object and returning the amount of bytes read.
|
/// Read the buffer, building the object and returning the amount of bytes read.
|
||||||
/// If anything is wrong with the buffer, None should be returned.
|
/// 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 mut buffer2 = [0u8; crate::socket::PACKET_BUFFER_SIZE];
|
||||||
let len = match decode_config_slice(buffer, B64_CONF, &mut buffer2) {
|
let len = decode_config_slice(buffer, B64_CONF, &mut buffer2)
|
||||||
Ok(len) => len,
|
.map_err(|_| LoadError::InvalidData)?;
|
||||||
Err(_) => return (None, 0),
|
|
||||||
};
|
|
||||||
Self::load(&buffer2[..len])
|
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
|
/// Dump an object into the buffer
|
||||||
pub trait Dumpable {
|
pub trait Dumpable {
|
||||||
/// Write the object to the buffer, returning the amount of bytes written.
|
/// Write the object to the buffer, returning the amount of bytes written.
|
||||||
/// If anything is wrong, false should be returned.
|
/// 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 mut buffer2 = [0u8; crate::socket::PACKET_BUFFER_SIZE];
|
||||||
let (ok, len) = self.dump(&mut buffer2);
|
let len = self.dump(&mut buffer2)?;
|
||||||
if !ok {
|
|
||||||
return (false, len)
|
|
||||||
}
|
|
||||||
let len = encode_config_slice(&buffer2[..len], B64_CONF, buffer);
|
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};
|
use crate::{RemoteCall, RemoteCallResponse};
|
||||||
|
|
||||||
pub const HOST_STR: &str = "127.0.0.1";
|
pub const HOST_STR: &str = "127.0.0.1";
|
||||||
|
@ -42,56 +42,56 @@ impl Packet {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Loadable for Packet {
|
impl Loadable for Packet {
|
||||||
fn load(buf: &[u8]) -> (Option<Self>, usize) {
|
fn load(buf: &[u8]) -> Result<(Self, usize), LoadError> {
|
||||||
if buf.len() == 0 {
|
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),
|
//0 => (None, 0),
|
||||||
1 => {
|
1 => {
|
||||||
let (obj, len) = RemoteCall::load(&buf[1..]);
|
let (obj, len) = RemoteCall::load(&buf[1..])?;
|
||||||
(obj.map(Self::Call), len)
|
(Self::Call(obj), 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)
|
|
||||||
}
|
}
|
||||||
_ => (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.1 += 1;
|
||||||
result
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dumpable for Packet {
|
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 {
|
if buf.len() == 0 {
|
||||||
return (false, 0);
|
return Err(DumpError::TooSmallBuffer);
|
||||||
}
|
}
|
||||||
buf[0] = self.discriminant();
|
buf[0] = self.discriminant();
|
||||||
let mut result = match self {
|
let mut result = match self {
|
||||||
Self::Call(c) => c.dump(&mut buf[1..]),
|
Self::Call(c) => c.dump(&mut buf[1..]),
|
||||||
Self::CallResponse(c) => c.dump(&mut buf[1..]),
|
Self::CallResponse(c) => c.dump(&mut buf[1..]),
|
||||||
Self::KeepAlive => (true, 0),
|
Self::KeepAlive => Ok(0),
|
||||||
Self::Invalid => (true, 0),
|
Self::Invalid => Ok(0),
|
||||||
Self::Message(s) => s.dump(&mut buf[1..]),
|
Self::Message(s) => s.dump(&mut buf[1..]),
|
||||||
Self::Unsupported => (true, 0),
|
Self::Unsupported => Ok(0),
|
||||||
Self::Bad => (false, 0),
|
Self::Bad => return Err(DumpError::Unsupported),
|
||||||
Self::Many(v) => v.dump(&mut buf[1..]),
|
Self::Many(v) => v.dump(&mut buf[1..]),
|
||||||
};
|
}?;
|
||||||
result.1 += 1;
|
result += 1;
|
||||||
result
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "usdpl"
|
name = "usdpl"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
authors = ["NGnius (Graham) <ngniusness@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
|
@ -43,7 +43,7 @@ web-sys = { version = "0.3", features = [
|
||||||
]}#["WebSocket", "MessageEvent", "ErrorEvent", "BinaryType"] }
|
]}#["WebSocket", "MessageEvent", "ErrorEvent", "BinaryType"] }
|
||||||
js-sys = { version = "0.3" }
|
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]
|
[dev-dependencies]
|
||||||
wasm-bindgen-test = "0.3.13"
|
wasm-bindgen-test = "0.3.13"
|
||||||
|
|
|
@ -6,14 +6,12 @@ use wasm_bindgen::JsCast;
|
||||||
use wasm_bindgen_futures::JsFuture;
|
use wasm_bindgen_futures::JsFuture;
|
||||||
|
|
||||||
//use web_sys::{WebSocket, MessageEvent, ErrorEvent};
|
//use web_sys::{WebSocket, MessageEvent, ErrorEvent};
|
||||||
|
use js_sys::JsString;
|
||||||
use web_sys::{Request, RequestInit, RequestMode, Response};
|
use web_sys::{Request, RequestInit, RequestMode, Response};
|
||||||
use js_sys::{ArrayBuffer, DataView, Uint8Array, JsString};
|
|
||||||
//use wasm_rs_shared_channel::{Expects, spsc::{Receiver, Sender}};
|
//use wasm_rs_shared_channel::{Expects, spsc::{Receiver, Sender}};
|
||||||
|
|
||||||
use usdpl_core::socket;
|
|
||||||
use usdpl_core::serdes::{Dumpable, Loadable, Primitive};
|
use usdpl_core::serdes::{Dumpable, Loadable, Primitive};
|
||||||
|
use usdpl_core::socket;
|
||||||
use super::imports;
|
|
||||||
|
|
||||||
pub async fn send_js(packet: socket::Packet, port: u16) -> Result<Vec<Primitive>, JsValue> {
|
pub async fn send_js(packet: socket::Packet, port: u16) -> Result<Vec<Primitive>, JsValue> {
|
||||||
let mut opts = RequestInit::new();
|
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 url = format!("http://localhost:{}/usdpl/call", port);
|
||||||
|
|
||||||
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
let mut buffer = [0u8; socket::PACKET_BUFFER_SIZE];
|
||||||
let (ok, len) = packet.dump_base64(&mut buffer);
|
let len = packet
|
||||||
if !ok {
|
.dump_base64(&mut buffer)
|
||||||
imports::console_error("USDPL error: packet dump failed");
|
.map_err(super::convert::str_to_js)?;
|
||||||
return Err("Packet dump failed".into());
|
|
||||||
}
|
|
||||||
let string: String = String::from_utf8_lossy(&buffer[..len]).into();
|
let string: String = String::from_utf8_lossy(&buffer[..len]).into();
|
||||||
opts.body(Some(&string.into()));
|
opts.body(Some(&string.into()));
|
||||||
|
|
||||||
let request = Request::new_with_str_and_init(&url, &opts)?;
|
let request = Request::new_with_str_and_init(&url, &opts)?;
|
||||||
|
|
||||||
request
|
request.headers().set("Accept", "application/bytes")?;
|
||||||
.headers()
|
|
||||||
.set("Accept", "application/bytes")?;
|
|
||||||
//.set("Authorization", "wasm TODO_KEY")?;
|
//.set("Authorization", "wasm TODO_KEY")?;
|
||||||
|
|
||||||
let window = web_sys::window().unwrap();
|
let window = web_sys::window().unwrap();
|
||||||
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
|
let resp_value = JsFuture::from(window.fetch_with_request(&request)).await?;
|
||||||
|
|
||||||
let resp: Response = resp_value.dyn_into()?;
|
let resp: Response = resp_value.dyn_into()?;
|
||||||
|
|
||||||
// Convert this other `Promise` into a rust `Future`.
|
|
||||||
let text = JsFuture::from(resp.text()?).await?;
|
let text = JsFuture::from(resp.text()?).await?;
|
||||||
|
|
||||||
let string: JsString = text.dyn_into()?;
|
let string: JsString = text.dyn_into()?;
|
||||||
|
|
||||||
match socket::Packet::load_base64(&string.as_string().unwrap().as_bytes()).0 {
|
match socket::Packet::load_base64(string.as_string().unwrap().as_bytes())
|
||||||
Some(socket::Packet::CallResponse(resp)) => {
|
.map_err(super::convert::str_to_js)?
|
||||||
Ok(resp.response)
|
.0
|
||||||
},
|
{
|
||||||
|
socket::Packet::CallResponse(resp) => Ok(resp.response),
|
||||||
_ => {
|
_ => {
|
||||||
imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url()));
|
//imports::console_warn(&format!("USDPL warning: Got non-call-response message from {}", resp.url()));
|
||||||
Err("".into())
|
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 wasm_bindgen::prelude::JsValue;
|
||||||
use js_sys::JSON::{stringify, parse};
|
|
||||||
|
|
||||||
use usdpl_core::serdes::Primitive;
|
use usdpl_core::serdes::Primitive;
|
||||||
|
|
||||||
|
@ -33,3 +34,7 @@ pub(crate) fn js_to_primitive(val: JsValue) -> Primitive {
|
||||||
Primitive::Empty
|
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::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
extern {
|
extern "C" {
|
||||||
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||||
pub fn console_log(s: &str);
|
pub fn console_log(s: &str);
|
||||||
|
|
||||||
|
|
|
@ -8,17 +8,14 @@ mod connection;
|
||||||
mod convert;
|
mod convert;
|
||||||
mod imports;
|
mod imports;
|
||||||
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
use js_sys::Array;
|
use js_sys::Array;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
use usdpl_core::{socket::Packet, RemoteCall};
|
use usdpl_core::{socket::Packet, RemoteCall};
|
||||||
//const REMOTE_CALL_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
//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);
|
//const REMOTE_PORT: std::sync::atomic::AtomicU16 = std::sync::atomic::AtomicU16::new(31337);
|
||||||
|
|
||||||
static mut CTX: UsdplContext = UsdplContext {
|
static mut CTX: UsdplContext = UsdplContext { port: 31337, id: 1 };
|
||||||
port: 31337,
|
|
||||||
id: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -33,7 +30,9 @@ fn get_port() -> u16 {
|
||||||
|
|
||||||
fn increment_id() -> u64 {
|
fn increment_id() -> u64 {
|
||||||
let current_id = unsafe { CTX.id };
|
let current_id = unsafe { CTX.id };
|
||||||
unsafe {CTX.id += 1;}
|
unsafe {
|
||||||
|
CTX.id += 1;
|
||||||
|
}
|
||||||
current_id
|
current_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,29 +48,25 @@ pub fn init_usdpl(port: u16) {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
//REMOTE_PORT.store(port, std::sync::atomic::Ordering::SeqCst);
|
//REMOTE_PORT.store(port, std::sync::atomic::Ordering::SeqCst);
|
||||||
unsafe {
|
unsafe {
|
||||||
CTX = UsdplContext {
|
CTX = UsdplContext { port: port, id: 1 };
|
||||||
port: port,
|
|
||||||
id: 1,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the targeted plugin framework, or "any" if unknown
|
/// Get the targeted plugin framework, or "any" if unknown
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn target() -> String {
|
pub fn target() -> String {
|
||||||
#[cfg(all(feature = "decky", not(any(feature = "crankshaft"))))]
|
usdpl_core::api::Platform::current().to_string()
|
||||||
{"decky".to_string()}
|
|
||||||
#[cfg(all(feature = "crankshaft", not(any(feature = "decky"))))]
|
|
||||||
{"crankshaft".to_string()}
|
|
||||||
#[cfg(not(any(feature = "decky", feature = "crankshaft")))]
|
|
||||||
{"any".to_string()}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a function on the back-end.
|
/// Call a function on the back-end.
|
||||||
/// Returns null (None) if this fails for any reason.
|
/// Returns null (None) if this fails for any reason.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub async fn call_backend(name: String, parameters: Vec<JsValue>) -> JsValue {
|
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 next_id = increment_id();
|
||||||
let mut params = Vec::with_capacity(parameters.len());
|
let mut params = Vec::with_capacity(parameters.len());
|
||||||
for val in parameters {
|
for val in parameters {
|
||||||
|
@ -79,11 +74,15 @@ pub async fn call_backend(name: String, parameters: Vec<JsValue>) -> JsValue {
|
||||||
}
|
}
|
||||||
let port = get_port();
|
let port = get_port();
|
||||||
imports::console_log(&format!("USDPL: Got port {}", 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,
|
id: next_id,
|
||||||
function: name.clone(),
|
function: name.clone(),
|
||||||
parameters: params,
|
parameters: params,
|
||||||
}), port).await;
|
}),
|
||||||
|
port,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
let results = match results {
|
let results = match results {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|
Loading…
Reference in a new issue