mirror of
https://framagit.org/ppom/reaction
synced 2026-03-14 12:45:47 +01:00
Client-Daemon protocol: use json instead of bincode
This permits to have a pretty PatternStatus easily Also having a human-readable protocol format is good! Closes #109
This commit is contained in:
parent
9642f47512
commit
1a57481110
4 changed files with 47 additions and 50 deletions
|
|
@ -3,7 +3,6 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use bincode::Options;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use tokio::net::UnixStream;
|
||||
use tokio_util::{
|
||||
|
|
@ -13,7 +12,7 @@ use tokio_util::{
|
|||
|
||||
use crate::{
|
||||
cli::Format,
|
||||
protocol::{bincode_options, ClientRequest, ClientStatus, DaemonResponse, Order},
|
||||
protocol::{Cleanable, ClientRequest, ClientStatus, DaemonResponse, Order},
|
||||
};
|
||||
|
||||
macro_rules! or_quit {
|
||||
|
|
@ -23,14 +22,13 @@ macro_rules! or_quit {
|
|||
}
|
||||
|
||||
async fn send_retrieve(socket: &Path, req: &ClientRequest) -> Result<DaemonResponse, String> {
|
||||
let bin = bincode_options();
|
||||
let conn = or_quit!(
|
||||
"opening connection to daemon",
|
||||
UnixStream::connect(socket).await
|
||||
);
|
||||
// Encode
|
||||
let mut transport = Framed::new(conn, LengthDelimitedCodec::new());
|
||||
let encoded_request = or_quit!("failed to encode request", bin.serialize(req));
|
||||
let encoded_request = or_quit!("failed to encode request", serde_json::to_string(req));
|
||||
or_quit!(
|
||||
"failed to send request",
|
||||
transport.send(Bytes::from(encoded_request)).await
|
||||
|
|
@ -43,11 +41,12 @@ async fn send_retrieve(socket: &Path, req: &ClientRequest) -> Result<DaemonRespo
|
|||
let encoded_response = or_quit!("failed to decode response", encoded_response);
|
||||
Ok(or_quit!(
|
||||
"failed to decode response",
|
||||
bin.deserialize::<DaemonResponse>(&encoded_response)
|
||||
serde_json::from_slice::<DaemonResponse>(&encoded_response)
|
||||
))
|
||||
}
|
||||
|
||||
fn print_status(cs: ClientStatus, format: Format) -> Result<(), Box<dyn Error>> {
|
||||
let cs = cs.clean();
|
||||
let encoded = match format {
|
||||
Format::JSON => serde_json::to_string_pretty(&cs)?,
|
||||
Format::YAML => serde_yaml::to_string(&cs)?,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use bincode::Options;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use regex::Regex;
|
||||
use tokio::net::UnixListener;
|
||||
|
|
@ -18,7 +17,7 @@ use tracing::{error, warn};
|
|||
|
||||
use crate::{
|
||||
concepts::{Config, Filter, Pattern, Stream},
|
||||
protocol::{bincode_options, ClientRequest, ClientStatus, DaemonResponse},
|
||||
protocol::{ClientRequest, ClientStatus, DaemonResponse},
|
||||
};
|
||||
|
||||
use super::{filter::FilterManager, shutdown::ShutdownToken};
|
||||
|
|
@ -153,7 +152,6 @@ pub async fn socket_manager(
|
|||
}
|
||||
};
|
||||
|
||||
let bin = bincode_options();
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = shutdown.wait() => break,
|
||||
|
|
@ -172,7 +170,7 @@ pub async fn socket_manager(
|
|||
};
|
||||
let request = or_next!(
|
||||
"failed to decode request",
|
||||
bin.deserialize(&encoded_request)
|
||||
serde_json::from_slice(&encoded_request)
|
||||
);
|
||||
// Process
|
||||
let response = match answer_order(config, &shared_state, request) {
|
||||
|
|
@ -181,7 +179,7 @@ pub async fn socket_manager(
|
|||
};
|
||||
// Encode
|
||||
let encoded_response =
|
||||
or_next!("failed to serialize response", bin.serialize::<DaemonResponse>(&response));
|
||||
or_next!("failed to serialize response", serde_json::to_string::<DaemonResponse>(&response));
|
||||
or_next!(
|
||||
"failed to send response:",
|
||||
transport.send(Bytes::from(encoded_response)).await
|
||||
|
|
|
|||
|
|
@ -40,34 +40,45 @@ pub type ClientStatus = BTreeMap<String, BTreeMap<String, BTreeMap<String, Patte
|
|||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
pub struct PatternStatus {
|
||||
#[serde(default, skip_serializing_if = "is_zero")]
|
||||
pub matches: usize,
|
||||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||||
pub actions: BTreeMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
// impl Serialize for PatternStatus {
|
||||
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
// where
|
||||
// S: serde::Serializer,
|
||||
// {
|
||||
// // We only skip serializing emptiness if we're on a human-readable format
|
||||
// // This means we're printing for user, not exchanging it over a socket
|
||||
// if serializer.is_human_readable() {
|
||||
// let ser_matches = self.matches != 0;
|
||||
// let ser_actions = !self.actions.is_empty();
|
||||
// let mut state =
|
||||
// serializer.serialize_map(Some(ser_matches as usize + ser_actions as usize))?;
|
||||
// if ser_matches {
|
||||
// state.serialize_entry("matches", &self.matches)?;
|
||||
// }
|
||||
// if ser_actions {
|
||||
// state.serialize_entry("actions", &self.actions)?;
|
||||
// }
|
||||
// state.end()
|
||||
// } else {
|
||||
// let mut state = serializer.serialize_struct("PatternStatus", 2)?;
|
||||
// state.serialize_field("matches", &self.matches)?;
|
||||
// state.serialize_field("actions", &self.actions)?;
|
||||
// state.end()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
fn is_zero(n: &usize) -> bool {
|
||||
*n == 0
|
||||
}
|
||||
|
||||
pub trait Cleanable {
|
||||
/// Remove empty fields
|
||||
fn clean(self) -> Self;
|
||||
}
|
||||
|
||||
impl Cleanable for ClientStatus {
|
||||
fn clean(self) -> Self {
|
||||
self.into_iter()
|
||||
.map(|(key, value)| {
|
||||
(
|
||||
key,
|
||||
value
|
||||
.into_iter()
|
||||
.map(|(key, value)| {
|
||||
(
|
||||
key,
|
||||
value
|
||||
.into_iter()
|
||||
.filter(|(_, value)| {
|
||||
value.matches != 0 || !value.actions.is_empty()
|
||||
})
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)
|
||||
})
|
||||
.filter(|(_, value)| !value.is_empty())
|
||||
.collect::<BTreeMap<_, _>>(),
|
||||
)
|
||||
})
|
||||
.filter(|(_, value)| !value.is_empty())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,19 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use bincode::Options;
|
||||
use serde::de::DeserializeOwned;
|
||||
use thiserror::Error;
|
||||
use tokio_util::codec::{Decoder, LengthDelimitedCodec};
|
||||
|
||||
pub type BincodeOptions = bincode::config::WithOtherIntEncoding<
|
||||
bincode::config::DefaultOptions,
|
||||
bincode::config::VarintEncoding,
|
||||
>;
|
||||
pub fn bincode_options() -> BincodeOptions {
|
||||
bincode::DefaultOptions::new().with_varint_encoding()
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum DecodeError {
|
||||
#[error("{0}")]
|
||||
Io(#[from] tokio::io::Error),
|
||||
#[error("{0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
Json(#[from] serde_json::Error),
|
||||
}
|
||||
|
||||
pub struct LengthPrefixedBincode<T: DeserializeOwned> {
|
||||
inner: LengthDelimitedCodec,
|
||||
bin: BincodeOptions,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +21,6 @@ impl<T: DeserializeOwned> LengthPrefixedBincode<T> {
|
|||
pub fn new() -> Self {
|
||||
LengthPrefixedBincode {
|
||||
inner: LengthDelimitedCodec::new(),
|
||||
bin: bincode_options(),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +41,7 @@ impl<T: DeserializeOwned> Decoder for LengthPrefixedBincode<T> {
|
|||
src: &mut tokio_util::bytes::BytesMut,
|
||||
) -> Result<Option<Self::Item>, Self::Error> {
|
||||
match self.inner.decode(src) {
|
||||
Ok(Some(data)) => match self.bin.deserialize(&data) {
|
||||
Ok(Some(data)) => match serde_json::from_slice(&data) {
|
||||
Ok(thing) => Ok(Some(thing)),
|
||||
Err(err) => Err(err.into()),
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue