use std::sync::Arc;
use serde_json::{Map, Value};
use crate::{
apperror::{self, AppError},
channel::{Channel, ChannelReadHalf, ChannelWriteHalf},
model::{command::CommandWithoutId, command_response::CommandResponse, site_info::SiteInfo},
rpc_helper::{Response, RpcHelper},
session::LoginInfo,
};
pub(crate) const COMMAND_CHANNEL_ID: i32 = 0;
pub struct CommandChannelWriteHalf {
channel: ChannelWriteHalf,
rpc_helper: Arc<RpcHelper>,
}
pub struct CommandChannelReadHalf {
channel: ChannelReadHalf,
rpc_helper: Arc<RpcHelper>,
}
pub struct CommandChannel {
rpc_helper: RpcHelper,
channel: Channel,
}
impl CommandChannelWriteHalf {
pub async fn send_command(
&self,
command: CommandWithoutId,
) -> Result<serde_json::Value, AppError> {
let (receive, command_id) = self.rpc_helper.add().await;
let command = command.to_command(command_id);
self.channel.send(&command.to_json_buffer()?).await?;
let result = receive
.await
.map_err(|_| AppError::new("failed to receive rpc result"))?;
match result {
Response::Success(result) => Ok(result),
Response::Error(error) => Err(apperror::AppError::new(format_args!(
"request failed: {error}"
))),
}
}
pub async fn send_info(&self, login_info: LoginInfo) -> Result<Value, AppError> {
let client = "tnnl-rust-client".to_string();
let protocol_version = 1;
let command = CommandWithoutId::create_send_info(login_info, client, protocol_version);
self.send_command(command).await
}
pub async fn get_download(&self) -> Result<Value, AppError> {
let command = CommandWithoutId::create_get_download();
self.send_command(command).await
}
pub async fn post_upload(&self, data: Map<String, Value>) -> Result<Value, AppError> {
let command = CommandWithoutId::create_post_upload(data);
self.send_command(command).await
}
pub async fn get_site_info(&self) -> Result<SiteInfo, AppError> {
let command = CommandWithoutId::create_get_site_info();
SiteInfo::from_value(self.send_command(command).await?)
}
}
impl CommandChannelReadHalf {
pub fn spawn(mut self) {
tokio::spawn(async move {
loop {
match self.channel.recv().await {
Some(data) => {
let Ok(response) = CommandResponse::from_json_buffer(&data) else {
log::warn!("failed to parse command response");
return;
};
let command_id = response.command_id;
let response: Response = match (response.error, response.response) {
(None, None) => {
log::warn!(
"we got no response but also no error -> we assume that it is a null value"
);
Response::Success(Value::Null)
}
(None, Some(response)) => Response::Success(response),
(Some(error), None) => Response::Error(error),
(Some(error), Some(response)) => {
log::warn!(
"response has an error {:} and a response {:}",
error,
response
);
Response::Error(error)
}
};
self.rpc_helper.complete(command_id, response).await;
}
None => {
log::warn!("command channel got closed");
break;
}
}
}
});
}
}
impl CommandChannel {
pub fn new(channel: Channel) -> CommandChannel {
CommandChannel {
channel,
rpc_helper: RpcHelper::new(),
}
}
pub fn split(self) -> (CommandChannelReadHalf, CommandChannelWriteHalf) {
let rpc_helper = Arc::new(self.rpc_helper);
let (channel_read, channel_write) = self.channel.split();
(
CommandChannelReadHalf {
rpc_helper: rpc_helper.clone(),
channel: channel_read,
},
CommandChannelWriteHalf {
rpc_helper,
channel: channel_write,
},
)
}
}