Restructure and start working on CLI
This commit is contained in:
226
libjrpc/templates/Rust/src/lib.rs
Normal file
226
libjrpc/templates/Rust/src/lib.rs
Normal file
@ -0,0 +1,226 @@
|
||||
use log::{info, trace, warn};
|
||||
use nanoid::nanoid;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::boxed::Box;
|
||||
use std::error::Error;
|
||||
use std::marker::Send;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::sync::{mpsc::Sender, Mutex};
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct JRPCRequest {
|
||||
pub jsonrpc: String,
|
||||
pub id: Option<String>,
|
||||
pub method: String,
|
||||
pub params: Value,
|
||||
}
|
||||
|
||||
impl JRPCRequest {
|
||||
pub fn new_request(method: String, params: Value) -> JRPCRequest {
|
||||
JRPCRequest {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
id: None,
|
||||
method,
|
||||
params,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct JRPCError {
|
||||
pub code: i64,
|
||||
pub message: String,
|
||||
pub data: Value,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct JRPCResult {
|
||||
pub jsonrpc: String,
|
||||
pub id: String,
|
||||
pub result: Option<Value>,
|
||||
pub error: Option<JRPCError>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JRPCClient {
|
||||
message_sender: Sender<JRPCRequest>,
|
||||
requests: Arc<Mutex<HashMap<String, Sender<JRPCResult>>>>,
|
||||
}
|
||||
|
||||
impl JRPCClient {
|
||||
pub fn new(sender: Sender<JRPCRequest>) -> JRPCClient {
|
||||
JRPCClient {
|
||||
message_sender: sender,
|
||||
requests: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_request(&self, mut request: JRPCRequest) -> Result<Value> {
|
||||
let (sender, mut receiver) = tokio::sync::mpsc::channel(1);
|
||||
|
||||
if request.id.is_none() {
|
||||
request.id = Some(nanoid!());
|
||||
}
|
||||
|
||||
{
|
||||
let mut self_requests = self.requests.lock().await;
|
||||
self_requests.insert(request.id.clone().unwrap(), sender);
|
||||
}
|
||||
self.message_sender.send(request).await?;
|
||||
|
||||
let result = receiver.recv().await;
|
||||
|
||||
if let Some(result) = result {
|
||||
if let Some(error) = result.error {
|
||||
return Err(format!("Error while receiving result: {}", error.message).into());
|
||||
} else if let Some(result) = result.result {
|
||||
return Ok(result);
|
||||
} else {
|
||||
return Ok(Value::Null);
|
||||
// return Err(format!("No result received").into());
|
||||
}
|
||||
} else {
|
||||
return Err("Error while receiving result".into());
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_notification(&self, mut request: JRPCRequest) {
|
||||
request.id = None;
|
||||
|
||||
_ = self.message_sender.send(request).await;
|
||||
}
|
||||
|
||||
pub async fn on_result(&self, result: JRPCResult) {
|
||||
let id = result.id.clone();
|
||||
let mut self_requests = self.requests.lock().await;
|
||||
let sender = self_requests.get(&id);
|
||||
if let Some(sender) = sender {
|
||||
_ = sender.send(result).await;
|
||||
self_requests.remove(&id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait JRPCServerService: Send + Sync + 'static {
|
||||
fn get_id(&self) -> String;
|
||||
async fn handle(&self, request: &JRPCRequest, function: &str) -> Result<(bool, Value)>;
|
||||
}
|
||||
|
||||
pub type JRPCServiceHandle = Arc<dyn JRPCServerService>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct JRPCSession {
|
||||
server: JRPCServer,
|
||||
message_sender: Sender<JRPCResult>,
|
||||
}
|
||||
|
||||
impl JRPCSession {
|
||||
pub fn new(server: JRPCServer, sender: Sender<JRPCResult>) -> JRPCSession {
|
||||
JRPCSession {
|
||||
server,
|
||||
message_sender: sender,
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_error(&self, request: JRPCRequest, error_msg: String, error_code: i64) -> () {
|
||||
if let Some(request_id) = request.id {
|
||||
let error = JRPCError {
|
||||
code: error_code,
|
||||
message: error_msg,
|
||||
data: Value::Null,
|
||||
};
|
||||
let result = JRPCResult {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
id: request_id,
|
||||
result: None,
|
||||
error: Some(error),
|
||||
};
|
||||
|
||||
// Send result
|
||||
let result = self.message_sender.send(result).await;
|
||||
if let Err(err) = result {
|
||||
warn!("Error while sending result: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_request(&self, request: JRPCRequest) -> () {
|
||||
let session = self.clone();
|
||||
tokio::task::spawn(async move {
|
||||
info!("Received request: {}", request.method);
|
||||
trace!("Request data: {:?}", request);
|
||||
let method: Vec<&str> = request.method.split('.').collect();
|
||||
if method.len() != 2 {
|
||||
warn!("Invalid method received: {}", request.method);
|
||||
return;
|
||||
}
|
||||
let service = method[0];
|
||||
let function = method[1];
|
||||
|
||||
let service = session.server.services.get(service);
|
||||
if let Some(service) = service {
|
||||
let result = service.handle(&request, function).await;
|
||||
match result {
|
||||
Ok((is_send, result)) => {
|
||||
if is_send && request.id.is_some() {
|
||||
let result = session
|
||||
.message_sender
|
||||
.send(JRPCResult {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
id: request.id.unwrap(),
|
||||
result: Some(result),
|
||||
error: None,
|
||||
})
|
||||
.await;
|
||||
if let Err(err) = result {
|
||||
warn!("Error while sending result: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Error while handling request: {}", err);
|
||||
session
|
||||
.send_error(
|
||||
request,
|
||||
format!("Error while handling request: {}", err),
|
||||
1,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!("Service not found: {}", method[0]);
|
||||
session
|
||||
.send_error(request, "Service not found".to_string(), 1)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct JRPCServer {
|
||||
services: HashMap<String, JRPCServiceHandle>,
|
||||
}
|
||||
|
||||
impl JRPCServer {
|
||||
pub fn new() -> JRPCServer {
|
||||
JRPCServer {
|
||||
services: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_service(&mut self, service: JRPCServiceHandle) -> () {
|
||||
let id = service.get_id();
|
||||
self.services.insert(id, service);
|
||||
}
|
||||
|
||||
pub fn get_session(&self, sender: Sender<JRPCResult>) -> JRPCSession {
|
||||
JRPCSession::new(self.clone(), sender)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user