use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Request {
pub req_id: Uuid,
#[serde(flatten)]
pub op: Op,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "op", rename_all = "snake_case")]
pub enum Op {
NodeId,
Import {
path: PathBuf,
rings: Vec<String>,
open: bool,
},
BlobList,
BlobRemove {
target: String,
},
Tag {
target: String,
rings: Vec<String>,
open: bool,
},
Tags {
target: String,
},
RingNew {
name: String,
},
RingList,
RingAdd {
ring: String,
peer: String,
nickname: Option<String>,
},
RingRemove {
ring: String,
peer: String,
},
RingMembers {
ring: String,
},
Receive {
ticket: String,
dest: PathBuf,
force_overwrite: bool,
},
Shutdown,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Event {
pub req_id: Uuid,
#[serde(flatten)]
pub kind: EventKind,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum EventKind {
Line {
text: String,
},
Progress {
done: u64,
total: u64,
},
Done,
Error {
message: String,
},
}
impl Event {
pub fn line(req_id: Uuid, text: impl Into<String>) -> Self {
Self {
req_id,
kind: EventKind::Line { text: text.into() },
}
}
pub fn progress(req_id: Uuid, done: u64, total: u64) -> Self {
Self {
req_id,
kind: EventKind::Progress { done, total },
}
}
pub fn done(req_id: Uuid) -> Self {
Self {
req_id,
kind: EventKind::Done,
}
}
pub fn error(req_id: Uuid, message: impl Into<String>) -> Self {
Self {
req_id,
kind: EventKind::Error {
message: message.into(),
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn op_node_id_serializes_to_snake_case_tag() {
assert_eq!(
serde_json::to_string(&Op::NodeId).unwrap(),
r#"{"op":"node_id"}"#
);
}
#[test]
fn op_blob_list_serializes_correctly() {
assert_eq!(
serde_json::to_string(&Op::BlobList).unwrap(),
r#"{"op":"blob_list"}"#
);
}
#[test]
fn op_ring_new_serializes_with_name_field() {
let json = serde_json::to_string(&Op::RingNew {
name: "friends".into(),
})
.unwrap();
assert_eq!(json, r#"{"op":"ring_new","name":"friends"}"#);
}
#[test]
fn request_serializes_req_id() {
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
let req = Request {
req_id: id,
op: Op::BlobList,
};
assert_eq!(
serde_json::to_string(&req).unwrap(),
r#"{"req_id":"550e8400-e29b-41d4-a716-446655440000","op":"blob_list"}"#
);
}
#[test]
fn request_without_req_id_fails_to_deserialize() {
let result: Result<Request, _> = serde_json::from_str(r#"{"op":"node_id"}"#);
assert!(result.is_err());
}
#[test]
fn request_with_empty_req_id_fails_to_deserialize() {
let result: Result<Request, _> = serde_json::from_str(r#"{"req_id":"","op":"node_id"}"#);
assert!(result.is_err());
}
#[test]
fn request_with_malformed_req_id_fails_to_deserialize() {
let result: Result<Request, _> =
serde_json::from_str(r#"{"req_id":"not-a-uuid","op":"node_id"}"#);
assert!(result.is_err());
}
#[test]
fn event_done_serializes_req_id_and_type() {
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
assert_eq!(
serde_json::to_string(&Event::done(id)).unwrap(),
r#"{"req_id":"550e8400-e29b-41d4-a716-446655440000","type":"done"}"#
);
}
#[test]
fn event_line_serializes_req_id_type_and_text() {
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
assert_eq!(
serde_json::to_string(&Event::line(id, "hello world")).unwrap(),
r#"{"req_id":"550e8400-e29b-41d4-a716-446655440000","type":"line","text":"hello world"}"#
);
}
#[test]
fn event_progress_serializes_correctly() {
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
assert_eq!(
serde_json::to_string(&Event::progress(id, 50, 100)).unwrap(),
r#"{"req_id":"550e8400-e29b-41d4-a716-446655440000","type":"progress","done":50,"total":100}"#
);
}
#[test]
fn event_error_serializes_correctly() {
let id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
assert_eq!(
serde_json::to_string(&Event::error(id, "something went wrong")).unwrap(),
r#"{"req_id":"550e8400-e29b-41d4-a716-446655440000","type":"error","message":"something went wrong"}"#
);
}
#[test]
fn event_without_req_id_fails_to_deserialize() {
let result: Result<Event, _> = serde_json::from_str(r#"{"type":"done"}"#);
assert!(result.is_err());
}
#[test]
fn request_round_trips_through_json() {
let original = Request {
req_id: Uuid::new_v4(),
op: Op::RingNew {
name: "work".into(),
},
};
let parsed: Request =
serde_json::from_str(&serde_json::to_string(&original).unwrap()).unwrap();
assert_eq!(parsed, original);
}
#[test]
fn event_round_trips_through_json() {
let original = Event::progress(Uuid::new_v4(), 42, 100);
let parsed: Event =
serde_json::from_str(&serde_json::to_string(&original).unwrap()).unwrap();
assert_eq!(parsed, original);
}
}