use anyhow::Error;
use async_trait::async_trait;
use futures::channel::oneshot;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::fmt::Debug;
use std::str::FromStr;
use thiserror::Error;
use crate::server::constants::{COMMAND_DELIMITER, COMMAND_SIZE};
#[async_trait(?Send)]
pub trait Facade: Debug {
async fn handle_request(&self, method: String, args: Value) -> Result<Value, Error>;
fn cleanup(&self) {}
fn print(&self) {}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ClientData {
pub command_id: Value,
pub command_result: AsyncResponse,
}
impl ClientData {
pub fn new(id: Value, result: AsyncResponse) -> ClientData {
ClientData { command_id: id, command_result: result }
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct RequestId {
client: Option<String>,
id: Value,
}
impl RequestId {
pub fn new(raw: Value) -> Self {
if let Some(s) = raw.as_str() {
let parts = s.split('.').collect::<Vec<_>>();
if parts.len() == 2 {
return Self {
client: Some(parts[0].to_owned()),
id: Value::String(parts[1..].join(".")),
};
}
}
Self { client: None, id: raw }
}
pub fn session_id(&self) -> Option<&str> {
self.client.as_ref().map(String::as_str)
}
pub fn response_id(&self) -> &Value {
&self.id
}
pub fn into_response_id(self) -> Value {
self.id
}
}
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct MethodId {
pub facade: String,
pub method: String,
}
impl FromStr for MethodId {
type Err = MethodIdParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts = s.split(COMMAND_DELIMITER).collect::<Vec<_>>();
if parts.len() != COMMAND_SIZE {
return Err(MethodIdParseError(s.to_string()));
}
Ok(Self { facade: parts[0].to_string(), method: parts[1].to_string() })
}
}
#[derive(Debug, PartialEq, Eq, Clone, Error)]
#[error("invalid method id: {}", _0)]
pub struct MethodIdParseError(String);
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CommandRequest {
pub method: String,
pub id: Value,
pub params: Value,
}
#[derive(Serialize, Clone, Debug)]
pub struct CommandResponse {
pub id: Value,
pub result: Option<Value>,
pub error: Option<String>,
}
impl CommandResponse {
pub fn new(id: Value, result: Option<Value>, error: Option<String>) -> CommandResponse {
CommandResponse { id, result, error }
}
}
#[derive(Debug)]
pub enum AsyncRequest {
Cleanup(oneshot::Sender<()>),
Command(AsyncCommandRequest),
}
#[derive(Debug)]
pub struct AsyncCommandRequest {
pub tx: oneshot::Sender<AsyncResponse>,
pub method_id: MethodId,
pub params: Value,
}
impl AsyncCommandRequest {
pub fn new(
tx: oneshot::Sender<AsyncResponse>,
method_id: MethodId,
params: Value,
) -> AsyncCommandRequest {
AsyncCommandRequest { tx, method_id, params }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AsyncResponse {
pub result: Option<Value>,
pub error: Option<String>,
}
impl AsyncResponse {
pub fn new(res: Result<Value, Error>) -> AsyncResponse {
match res {
Ok(v) => AsyncResponse { result: Some(v), error: None },
Err(e) => AsyncResponse { result: None, error: Some(e.to_string()) },
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn parse_method_id_ok() {
assert_eq!(
"bt.send".parse(),
Ok(MethodId { facade: "bt".to_string(), method: "send".to_string() })
);
assert_eq!(
"FooFacade.BarMethod".parse(),
Ok(MethodId { facade: "FooFacade".to_string(), method: "BarMethod".to_string() })
);
assert_eq!(
"EmptyMethod.".parse(),
Ok(MethodId { facade: "EmptyMethod".to_string(), method: "".to_string() })
);
assert_eq!(
".EmptyFacade".parse(),
Ok(MethodId { facade: "".to_string(), method: "EmptyFacade".to_string() })
);
}
#[test]
fn parse_method_id_invalid() {
fn assert_parse_error(s: &str) {
assert_eq!(s.parse::<MethodId>(), Err(MethodIdParseError(s.to_string())));
}
assert_parse_error("bluetooth_send");
assert_parse_error("wlan.scan.start");
assert_parse_error("");
assert_parse_error("BluetoothSend");
assert_parse_error("Bluetooth,Scan");
}
#[test]
fn parse_request_id_int() {
let id = RequestId::new(json!(42));
assert_eq!(id, RequestId { client: None, id: json!(42) });
assert_eq!(id.session_id(), None);
assert_eq!(id.response_id(), &json!(42));
assert_eq!(id.into_response_id(), json!(42));
}
#[test]
fn parse_request_id_single_str() {
assert_eq!(RequestId::new(json!("123")), RequestId { client: None, id: json!("123") });
}
#[test]
fn parse_request_id_too_many_dots() {
assert_eq!(RequestId::new(json!("1.2.3")), RequestId { client: None, id: json!("1.2.3") });
}
#[test]
fn parse_request_id_with_session_id() {
let id = RequestId::new(json!("12.34"));
assert_eq!(id, RequestId { client: Some("12".to_string()), id: json!("34") });
assert_eq!(id.session_id(), Some("12"));
assert_eq!(id.response_id(), &json!("34"));
assert_eq!(id.into_response_id(), json!("34"));
}
}