1
0

refactor: refactoring into async first

This commit is contained in:
Ishan Jain 2024-05-06 13:32:01 +05:30
parent e64f16dd95
commit cec2eeda62
Signed by: ishan
GPG Key ID: 0506DB2A1CC75C27
6 changed files with 370 additions and 212 deletions

105
Cargo.lock generated
View File

@ -80,12 +80,102 @@ name = "distributed-systems-flyio"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"dashmap", "dashmap",
"futures",
"rand", "rand",
"serde", "serde",
"serde_json", "serde_json",
"tokio", "tokio",
] ]
[[package]]
name = "futures"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.11" version = "0.2.11"
@ -217,6 +307,12 @@ version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.17" version = "0.2.17"
@ -338,6 +434,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.13.2" version = "1.13.2"

View File

@ -7,6 +7,7 @@ edition = "2021"
[dependencies] [dependencies]
dashmap = "5.5.3" dashmap = "5.5.3"
futures = "0.3.30"
rand = { version = "0.8.5", features = ["small_rng"] } rand = { version = "0.8.5", features = ["small_rng"] }
serde = { version = "1.0.193", features = ["serde_derive"] } serde = { version = "1.0.193", features = ["serde_derive"] }
serde_json = "1.0.109" serde_json = "1.0.109"

View File

@ -2,59 +2,73 @@
#![feature(hash_set_entry)] #![feature(hash_set_entry)]
#![feature(trait_alias)] #![feature(trait_alias)]
pub mod seq_kv;
pub mod types; pub mod types;
use crate::{seq_kv::MonotonicCounter, types::Message}; use crate::types::{Message, MessageBody, SeqKvInput};
use rand::{rngs::SmallRng, Rng, SeedableRng}; use futures::future::BoxFuture;
use rand::{rngs::SmallRng, SeedableRng};
use serde_json::Value; use serde_json::Value;
use std::{ use std::{
collections::HashMap, collections::HashMap,
io::{stdin, stdout, BufRead, Error as IoError, Stdin, Stdout, Write}, future::Future,
io::{stdin, stdout, BufRead, Error as IoError, Stdout, Write},
pin::Pin,
sync::{Arc, Mutex, RwLock}, sync::{Arc, Mutex, RwLock},
time::{SystemTime, UNIX_EPOCH}, time::{Duration, SystemTime, UNIX_EPOCH},
};
use tokio::{
runtime::Handle,
sync::oneshot::{self, Receiver, Sender},
}; };
use tokio::runtime::Runtime;
pub trait Handler = Fn(Message, Arc<RwLock<Malestorm>>, Arc<Mutex<MalestormIo>>) -> Result<(), String> trait Callback = FnOnce(Message) -> Result<(), String> + Send + Sync + 'static;
+ Send
+ Sync
+ 'static;
pub struct Malestorm { type Handler = Arc<
dyn Fn(
Message,
Arc<RwLock<Maelstorm>>,
Arc<Mutex<MaelstormIo>>,
) -> BoxFuture<'static, Result<(), String>>
+ Send
+ Sync,
>;
pub struct Maelstorm {
mutex: Mutex<()>,
pub node: Node, pub node: Node,
pub handlers: HashMap<String, Arc<dyn Handler>>, pub handlers: HashMap<String, Handler>,
callbacks: HashMap<i64, Box<dyn Callback>>,
} }
pub struct Node { pub struct Node {
id: String, pub id: String,
nodes: Vec<String>, pub nodes: Vec<String>,
counter: MonotonicCounter,
rng: SmallRng, rng: SmallRng,
} }
pub struct MalestormIo { pub struct MaelstormIo {
stdout: Stdout, stdout: Stdout,
} }
impl Default for Malestorm { impl Default for Maelstorm {
fn default() -> Self { fn default() -> Self {
let seed = SystemTime::now() let seed = SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.expect("time went backwards??"); .expect("time went backwards??");
Self { Self {
mutex: Mutex::new(()),
node: Node { node: Node {
id: String::new(), id: String::new(),
nodes: Vec::new(), nodes: Vec::new(),
counter: MonotonicCounter::new(),
rng: SmallRng::seed_from_u64(seed.as_secs()), rng: SmallRng::seed_from_u64(seed.as_secs()),
}, },
handlers: HashMap::new(), handlers: HashMap::new(),
callbacks: HashMap::new(),
} }
} }
} }
impl MalestormIo { impl MaelstormIo {
fn write(&self, buf: &[u8]) -> Result<(), IoError> { fn write(&self, buf: &[u8]) -> Result<(), IoError> {
let mut writer = self.stdout.lock(); let mut writer = self.stdout.lock();
writer.write_all(buf)?; writer.write_all(buf)?;
@ -63,14 +77,14 @@ impl MalestormIo {
} }
} }
impl Default for MalestormIo { impl Default for MaelstormIo {
fn default() -> Self { fn default() -> Self {
Self { stdout: stdout() } Self { stdout: stdout() }
} }
} }
impl Malestorm { impl Maelstorm {
pub fn run(self, runtime: Runtime, io: MalestormIo) { pub async fn run(self, io: MaelstormIo) {
let program = Arc::new(RwLock::new(self)); let program = Arc::new(RwLock::new(self));
let io = Arc::new(Mutex::new(io)); let io = Arc::new(Mutex::new(io));
let stdin = stdin(); let stdin = stdin();
@ -101,12 +115,29 @@ impl Malestorm {
} }
}; };
let mtype = message.body.message_type.clone(); if let Some(reply_msg_id) = message.body.in_reply_to {
let mut program = program.write().unwrap();
let handler: Arc<dyn Handler> = { let callback = match program.callbacks.remove(&reply_msg_id) {
Some(v) => v,
None => {
eprintln!("no callback for msg with reply id {}", reply_msg_id);
continue;
}
};
if let Err(e) = callback(message)
/* add await*/
{
eprintln!("error in callback: {e}");
}
continue;
}
let handler = {
let program = program.read().unwrap(); let program = program.read().unwrap();
match program.handlers.get(&mtype) { match program.handlers.get(&message.body.message_type) {
Some(v) => v.clone(), Some(v) => v.clone(),
None => { None => {
//eprintln!("no handler found for {}", message.body.message_type); //eprintln!("no handler found for {}", message.body.message_type);
@ -117,42 +148,24 @@ impl Malestorm {
let pc = program.clone(); let pc = program.clone();
let io = io.clone(); let io = io.clone();
runtime.spawn(async move { tokio::spawn(async move {
if let Err(e) = handler(message, pc, io) { if let Err(e) = handler(message, pc, io).await {
eprintln!("error in serving request: {}", e); eprintln!("error in serving request: {}", e);
} }
}); });
} }
} }
pub fn set_node_id(&mut self, node_id: String) { pub fn init(&mut self, node_id: String, nodes: Vec<String>) {
self.node.id = node_id;
}
pub fn set_nodes(&mut self, nodes: Vec<String>) {
self.node.nodes = nodes; self.node.nodes = nodes;
} self.node.id = node_id;
pub fn get_nodes(&self) -> Vec<String> {
self.node.nodes.clone()
}
pub fn read_counter(&self, src: &str) -> u64 {
self.node.counter.read(src)
}
pub fn write_counter(&mut self, src: &str, v: u64) {
self.node.counter.write(src, v);
}
pub fn add_counter(&mut self, src: &str, v: u64) {
self.node.counter.add(src, v);
} }
pub fn sync( pub fn sync(
&self, &self,
dest: &str, dest: &str,
prev_msg: Message, prev_msg: Message,
io: Arc<Mutex<MalestormIo>>, io: Arc<Mutex<MaelstormIo>>,
) -> Result<(), IoError> { ) -> Result<(), IoError> {
let msg = Message { let msg = Message {
id: Some(prev_msg.id.unwrap_or(0) + 1), id: Some(prev_msg.id.unwrap_or(0) + 1),
@ -167,32 +180,91 @@ impl Malestorm {
}; };
let out = serde_json::to_vec(&msg)?; let out = serde_json::to_vec(&msg)?;
eprintln!("wrote = {:?}", String::from_utf8_lossy(&out));
let io = io.lock().unwrap(); let io = io.lock().unwrap();
io.write(&out)?; io.write(&out)?;
Ok(()) Ok(())
} }
pub fn generate_client_id(&mut self) -> String { pub fn register<F, Fut>(&mut self, name: &str, func: F)
let s2: u64 = self.node.rng.gen(); where
let s1 = &self.node.id; F: Fn(Message, Arc<RwLock<Maelstorm>>, Arc<Mutex<MaelstormIo>>) -> Fut
format!("{}_{}", s1, s2) + Send
+ Sync
+ 'static,
Fut: Future<Output = Result<(), String>> + Send + 'static,
{
self.handlers.insert(
name.to_string(),
Arc::new(move |a, b, c| Box::pin(func(a, b, c))),
);
} }
pub fn register(&mut self, name: &str, func: impl Handler) { pub async fn sync_rpc(
self.handlers.insert(name.to_string(), Arc::new(func)); &mut self,
io: Arc<Mutex<MaelstormIo>>,
msg: Message,
) -> Result<Message, String> {
let (tx, rx): (Sender<Message>, Receiver<Message>) = oneshot::channel();
self.rpc(
io,
msg,
Box::new(|msg| {
tx.send(msg)
.map_err(|e| format!("error in sending to tx chan: {:?}", e))?;
Ok(())
}),
)
.map_err(|e| e.to_string())?;
match tokio::time::timeout(Duration::from_secs(2), rx).await {
Ok(result) => match result {
Ok(v) => Ok(v),
Err(e) => {
eprintln!("sync callback error: {}", e);
Err(e.to_string())
}
},
Err(e) => {
eprintln!("sync callback timeout: {}", e);
Err(e.to_string())
}
}
} }
pub fn send(&self, io: Arc<Mutex<MalestormIo>>, mut msg: Message) -> Result<(), IoError> { fn rpc(
&mut self,
io: Arc<Mutex<MaelstormIo>>,
mut msg: Message,
handler: impl Callback,
) -> Result<(), IoError> {
let next_msg_id = msg.body.msg_id.unwrap() + 1;
self.callbacks.insert(next_msg_id, Box::new(handler));
msg.body.msg_id = Some(next_msg_id);
self.send(io, msg.clone(), &msg.src)
}
pub fn reply(&mut self, io: Arc<Mutex<MaelstormIo>>, mut msg: Message) -> Result<(), IoError> {
msg.body.in_reply_to = msg.body.msg_id;
msg.body.msg_id = None;
self.send(io, msg.clone(), &msg.src)
}
fn send(
&self,
io: Arc<Mutex<MaelstormIo>>,
mut msg: Message,
dst: &str,
) -> Result<(), IoError> {
// Before replying, Swap src / dst in original message // Before replying, Swap src / dst in original message
// Add the correct value for in_reply_to // Add the correct value for in_reply_to
std::mem::swap(&mut msg.src, &mut msg.dest); msg.dest = dst.to_string();
msg.src = self.node.id.clone(); msg.src = self.node.id.clone();
msg.body.in_reply_to = msg.body.msg_id;
let out = serde_json::to_vec(&msg)?; let out = serde_json::to_vec(&msg)?;
let io = io.lock().unwrap(); let io = io.lock().unwrap();
@ -204,9 +276,62 @@ impl Malestorm {
} }
} }
async fn parse_request_and_handle( //impl Maelstorm {
program: Arc<RwLock<Malestorm>>, // pub fn read_counter(
io: Arc<Mutex<MalestormIo>>, // &mut self,
buf: &[u8], // io: Arc<Mutex<MaelstormIo>>,
) { // store: &str,
} // node_to_read: String,
// ) -> Result<u64, String> {
// let msg = Message {
// id: None,
// src: self.node.id.clone(),
// dest: store.to_string(),
// body: MessageBody {
// msg_id: Some(0),
// in_reply_to: None,
// message_type: "read".to_string(),
// message_body: serde_json::to_value(SeqKvInput {
// key: node_to_read,
// value: None,
// })
// .unwrap(),
// },
// };
// let mut result = self.sync_rpc(io, msg)?;
//
// let body: SeqKvInput = serde_json::from_value(result.body.message_body.take())
// .map_err(|e| format!("error in parsing response body: {}", e))?;
//
// Ok(body.value.unwrap())
// }
//
// pub fn write_counter(
// &mut self,
// io: Arc<Mutex<MaelstormIo>>,
// store: &str,
// val: u64,
// ) -> Result<(), String> {
// let msg = Message {
// id: None,
// src: self.node.id.clone(),
// dest: store.to_string(),
// body: MessageBody {
// msg_id: Some(0),
// in_reply_to: None,
// message_type: "write".to_string(),
// message_body: serde_json::to_value(SeqKvInput {
// key: self.node.id.clone(),
// value: Some(val),
// })
// .unwrap(),
// },
// };
// let mut result = self.sync_rpc(io, msg)?;
//
// let body: SeqKvInput = serde_json::from_value(result.body.message_body.take())
// .map_err(|e| format!("error in parsing response body: {}", e))?;
//
// Ok(())
// }
//}

View File

@ -2,108 +2,100 @@
use distributed_systems_flyio::{ use distributed_systems_flyio::{
types::{GrowCounterReadMessage, GrowCounterUpdateMessage, InitMessage, Message}, types::{GrowCounterReadMessage, GrowCounterUpdateMessage, InitMessage, Message},
Malestorm, MalestormIo, Maelstorm, MaelstormIo,
}; };
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use tokio::runtime::Runtime;
fn main() { async fn test(
let mut program = Malestorm::default(); msg: Message,
program: Arc<RwLock<Maelstorm>>,
io: Arc<Mutex<MaelstormIo>>,
) -> Result<(), String> {
Ok(())
}
#[tokio::main]
async fn main() {
let mut program = Maelstorm::default();
program.register( program.register(
"init", "init",
// TODO: Replace error string with a type // TODO: Replace error string with a type
|mut msg: Message, async |mut msg: Message,
program: Arc<RwLock<Malestorm>>, program: Arc<RwLock<Maelstorm>>,
io: Arc<Mutex<MalestormIo>>| io: Arc<Mutex<MaelstormIo>>|
-> Result<(), String> { -> Result<(), String> {
let message_body: InitMessage = serde_json::from_value(msg.body.message_body.take()) let message_body: InitMessage = serde_json::from_value(msg.body.message_body.take())
.map_err(|e| format!("error in parsing response body: {}", e))?; .map_err(|e| format!("error in parsing response body: {}", e))?;
let mut program = program.write().unwrap(); let mut program = program.write().unwrap();
program.set_node_id(message_body.node_id); program.init(message_body.node_id, message_body.nodes);
program.set_nodes(message_body.nodes);
msg.body.message_type = "init_ok".into(); msg.body.message_type = "init_ok".into();
program program
.send(io, msg) .reply(io, msg)
.map_err(|e| format!("init: error in writing response: {}", e)) .map_err(|e| format!("init: error in writing response: {}", e))
}, },
); );
program.register( // program.register(
"add", // "add",
|mut msg: Message, // async |mut msg: Message,
program: Arc<RwLock<Malestorm>>, // program: Arc<RwLock<Maelstorm>>,
io: Arc<Mutex<MalestormIo>>| // io: Arc<Mutex<MaelstormIo>>|
-> Result<(), String> { // -> Result<(), String> {
let body: GrowCounterUpdateMessage = // let body: GrowCounterUpdateMessage =
serde_json::from_value(msg.body.message_body.take()) // serde_json::from_value(msg.body.message_body.take())
.map_err(|e| format!("error in parsing response body: {}", e))?; // .map_err(|e| format!("error in parsing response body: {}", e))?;
msg.body.message_type = "add_ok".into();
let mut program = program.write().unwrap(); // msg.body.message_type = "add_ok".into();
program.add_counter(&msg.src, body.delta);
program // let mut program = program.write().unwrap();
.send(io, msg.clone())
.map_err(|e| format!("add: error in writing response: {}", e))
},
);
program.register( // let id = program.node.id.clone();
"read",
|mut msg: Message,
program: Arc<RwLock<Malestorm>>,
io: Arc<Mutex<MalestormIo>>|
-> Result<(), String> {
msg.body.message_type = "read_ok".into();
let program = program.read().unwrap(); // // let current = program
let mut sum = 0; // // .read_counter(io.clone(), "seq-kv", id)
for node in program.get_nodes() { // // .expect("error in reading value");
if node == msg.src {
sum += program.read_counter(&node);
continue;
}
// Sync first then add
program.sync(&node, msg.clone(), io.clone()); // // program.write_counter(io.clone(), "seq-kv", current + body.delta)?;
sum += program.read_counter(&msg.src); // program
} // .reply(io, msg)
// .map_err(|e| format!("add: error in writing response: {}", e))
// },
// );
msg.body.message_body = // program.register(
serde_json::to_value(GrowCounterReadMessage { value: sum }).unwrap(); // "read",
// async |mut msg: Message,
// program: Arc<RwLock<Maelstorm>>,
// io: Arc<Mutex<MaelstormIo>>|
// -> Result<(), String> {
// msg.body.message_type = "read_ok".into();
program // let mut program = program.write().unwrap();
.send(io, msg.clone()) // let mut sum = 0;
.map_err(|e| format!("read: error in writing response: {}", e)) // for node in program.node.nodes.clone() {
}, // if *node == msg.src {
); // sum += program.read_counter(io.clone(), "seq-kv", node)?;
// continue;
// }
// // Sync first then add
program.register( // sum += program.read_counter(io.clone(), "seq-kv", node)?;
"counter_sync", // }
|mut msg: Message,
program: Arc<RwLock<Malestorm>>,
io: Arc<Mutex<MalestormIo>>|
-> Result<(), String> {
msg.body.message_type = "counter_sync_ok".into();
let program = program.read().unwrap(); // msg.body.message_body =
msg.body.message_body = serde_json::to_value(GrowCounterReadMessage { // serde_json::to_value(GrowCounterReadMessage { value: sum }).unwrap();
value: program.read_counter(&msg.src),
})
.unwrap();
program // program
.send(io, msg.clone()) // .reply(io, msg.clone())
.map_err(|e| format!("read: error in writing response: {}", e)) // .map_err(|e| format!("read: error in writing response: {}", e))
}, // },
); // );
let io = MalestormIo::default(); let io = MaelstormIo::default();
let runtime = Runtime::new().unwrap();
program.run(runtime, io); program.run(io).await;
} }

View File

@ -1,28 +0,0 @@
use std::collections::HashMap;
#[derive(Debug)]
pub struct MonotonicCounter {
counter: HashMap<String, u64>,
}
impl MonotonicCounter {
pub fn new() -> Self {
Self {
counter: HashMap::new(),
}
}
pub fn read(&self, node: &str) -> u64 {
*self.counter.get(node).unwrap_or(&0)
}
pub fn write(&mut self, node: &str, v: u64) {
*self.counter.entry(node.to_string()).or_insert(0) = v;
}
pub fn add(&mut self, node: &str, v: u64) {
eprintln!("{:?}", self.counter);
*self.counter.entry(node.to_string()).or_insert(0) += v;
}
}

View File

@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Clone, Deserialize)] #[derive(Debug, Serialize, Clone, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Message { pub struct Message {
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<i64>, pub id: Option<i64>,
pub src: String, pub src: String,
pub dest: String, pub dest: String,
@ -11,7 +12,9 @@ pub struct Message {
#[derive(Debug, Serialize, Clone, Deserialize)] #[derive(Debug, Serialize, Clone, Deserialize)]
pub struct MessageBody { pub struct MessageBody {
#[serde(skip_serializing_if = "Option::is_none")]
pub msg_id: Option<i64>, pub msg_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub in_reply_to: Option<i64>, pub in_reply_to: Option<i64>,
#[serde(rename = "type")] #[serde(rename = "type")]
@ -41,50 +44,10 @@ pub struct GrowCounterReadMessage {
pub value: u64, pub value: u64,
} }
#[cfg(test)] #[derive(Debug, Serialize, Deserialize)]
mod test { #[serde(deny_unknown_fields)]
pub struct SeqKvInput {
use super::*; pub key: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub fn extraneous_fields_fail() { pub value: Option<u64>,
let body = "{
\"src\": \"c1\",
\"dest\": \"n1\",
\"body\": {
\"type\": \"echo\",
\"msg_id\": 1,
\"echo\": \"Please echo 35\",
\"extraneous\": \"hi\"
}
}";
let resp: Result<Message, serde_json::Error> = serde_json::from_str(body);
if resp.is_ok() {
eprintln!("successfully parsed into Message {:#?}", resp);
}
assert!(resp.is_err());
}
#[test]
pub fn parse_echo() {
let body = "{
\"src\": \"c1\",
\"dest\": \"n1\",
\"body\": {
\"type\": \"echo\",
\"msg_id\": 1,
\"echo\": \"Please echo 35\"
}
}";
let resp: Result<Message, serde_json::Error> = serde_json::from_str(body);
if resp.is_err() {
eprintln!("failed to parsed into Echo Message {:#?}", resp);
}
assert!(resp.is_ok());
}
} }