use conciliator::{
Conciliator,
Paint,
Print
};
use futures::future::{
FutureExt,
TryFutureExt,
BoxFuture
};
use serde::{Serialize, Deserialize};
use crate::event;
use crate::message;
use crate::net::InterfaceAddress;
use crate::peer::{
Address,
Invitation
};
use crate::util::*;
use super::{
AnyRequest,
DaemonReq,
NodeReq,
MirrorReq,
DispatcherReq,
RouterReq,
CnsprcyStatus,
Conspirator,
NodeJoinRequest,
NodeArg,
NodeIDArg,
KeyArg
};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum Command {
GetStatus,
StopServer,
Advertise,
Invite(KeyArg),
Accept(Invitation),
EnableInterface(String),
DisableInterface(String),
GetInterfaces,
BindInterface(InterfaceAddress),
RemoveInterface(Address),
GetHandlers,
DispatchEvent(event::DynamicEvent),
GetConspirator(NodeArg),
GetAllConspirators,
Write {
key: String,
value: String
},
AddAddress {
arg: NodeArg,
addr: Address
},
Disable(NodeArg),
Join {
id: NodeIDArg,
name: String,
addr: Address
},
SendPayload(NodeArg, message::Payload, Option<Address>)
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum Response {
Status(CnsprcyStatus),
StopServer(bool),
Advertise(KeyArg),
Invite(Invitation),
Accept(bool),
EnableInterface(bool),
DisableInterface(bool),
Interfaces(Vec<InterfaceAddress>),
BindInterface(bool),
RemoveInterface(bool),
Handlers(Vec<String>),
DispatchEvent(bool),
GetConspirator(Conspirator),
GetAllConspirators(Vec<Conspirator>),
Write(bool),
AddAddress(bool),
Disable(bool),
Join(bool),
SendPayload(bool),
Err(String)
}
impl Response {
pub fn is_ok(self) -> bool {
match self {
Self::StopServer(b) |
Self::Accept(b) |
Self::EnableInterface(b) |
Self::DisableInterface(b) |
Self::BindInterface(b) |
Self::RemoveInterface(b) |
Self::DispatchEvent(b) |
Self::Write(b) |
Self::AddAddress(b) |
Self::Disable(b) |
Self::Join(b) |
Self::SendPayload(b) => b,
Self::Err(_) => false,
_ => false
}
}
pub fn error<E>(_discard: E) -> Self {
Self::Err(
"failed to query daemon".to_string()
)
}
}
pub fn request(sender: &Sender<AnyRequest>, cmd: Command)
-> BoxFuture<'static, Response>
{
use Command::*;
match cmd {
GetStatus => sender
.get(DaemonReq::GetStatus)
.map_ok_or_else(Response::error, Response::Status)
.boxed(),
StopServer => sender
.get(DaemonReq::StopServer)
.map_ok_or_else(Response::error, Response::StopServer)
.boxed(),
Invite(key) => sender
.request(DaemonReq::Invite, key)
.map_ok_or_else(Response::error, Response::Invite)
.boxed(),
Accept(invitation) => sender
.request(DaemonReq::Accept, invitation)
.map_ok_or_else(Response::error, Response::Accept)
.boxed(),
GetConspirator(arg) => sender
.request(NodeReq::GetConspirator, arg)
.map_ok_or_else(Response::error, Response::GetConspirator)
.boxed(),
GetAllConspirators => sender
.get(NodeReq::GetConspirators)
.map_ok_or_else(Response::error, Response::GetAllConspirators)
.boxed(),
Write { key, value } => sender
.request(NodeReq::Write, (key, value))
.map_ok_or_else(Response::error, Response::Write)
.boxed(),
Advertise => sender
.get(NodeReq::Advertise)
.map_ok_or_else(Response::error, Response::Advertise)
.boxed(),
AddAddress {arg, addr} => sender
.request(NodeReq::AddAddress, (arg, addr))
.map_ok_or_else(Response::error, Response::AddAddress)
.boxed(),
Disable(arg) => sender
.request(NodeReq::Disable, arg)
.map_ok_or_else(Response::error, Response::Disable)
.boxed(),
Join {id, name, addr} => sender
.request(NodeReq::Join, NodeJoinRequest {id, name, addr})
.map_ok_or_else(Response::error, Response::Join)
.boxed(),
SendPayload(arg, pyl, addr) => sender
.request(NodeReq::SendPayload, (arg, pyl, addr))
.map_ok_or_else(Response::error, Response::SendPayload)
.boxed(),
EnableInterface(interface) => sender
.request(MirrorReq::Enable, interface)
.map_ok_or_else(Response::error, Response::EnableInterface)
.boxed(),
DisableInterface(interface) => sender
.request(MirrorReq::Disable, interface)
.map_ok_or_else(Response::error, Response::DisableInterface)
.boxed(),
GetInterfaces => sender
.get(RouterReq::GetInterfaces)
.map_ok_or_else(Response::error, Response::Interfaces)
.boxed(),
BindInterface(addr) => sender
.request(RouterReq::BindInterface, addr)
.map_ok_or_else(Response::error, Response::BindInterface)
.boxed(),
RemoveInterface(addr) => sender
.request(RouterReq::RemoveInterface, addr)
.map_ok_or_else(Response::error, Response::RemoveInterface)
.boxed(),
GetHandlers => sender
.get(DispatcherReq::GetHandlers)
.map_ok_or_else(Response::error, Response::Handlers)
.boxed(),
DispatchEvent(evt) => sender
.request(DispatcherReq::Dispatch, evt)
.map_ok_or_else(Response::error, Response::DispatchEvent)
.boxed()
}
}
impl Print for Response {
fn print<C: Conciliator + ?Sized>(self, con: &C) {
match self {
Self::Err(why) => {
con.error("Query unsuccessful!");
con.error(why);
},
Self::Status(status) => con.print(&status),
Self::Interfaces(addrs) => match &addrs[..] {
[] => {
con.warn("Not bound to any interfaces!");
},
[addrs @ .., last] => {
con.status("Interfaces:");
for addr in addrs {
let mut line = con.line(" ");
line.push_omega(tree::KNOT)
.push(addr);
if let Some(name) = addr.get_name() {
line
.push(" (")
.push_zeta_bold(name)
.push(")");
}
}
let mut line = con.line(" ");
line.push_omega(tree::TAIL)
.push(last);
if let Some(name) = last.get_name() {
line
.push(" (")
.push_zeta_bold(name)
.push(")");
}
}
},
Self::Handlers(handlers) => match &handlers[..] {
[] => {
con.warn("No handlers registered!");
},
[handlers @ .., last] => {
con.status("Event handlers:");
for handler in handlers {
con.line(" ")
.push_omega(tree::KNOT)
.push(handler);
}
con.line(" ")
.push_omega(tree::TAIL)
.push(last);
}
},
Self::GetConspirator(conspirator) => con.print(&conspirator),
Self::GetAllConspirators(conspirators) => {
if conspirators.is_empty() {
con.warn("No conspirators!");
}
for conspirator in conspirators {
con.print(&conspirator);
}
},
Self::Advertise(key) => {
con.status("Created new key pair");
con.info("Advertise the public key: ").push(key);
},
Self::Invite(invite) => {
con.status("Invitation created");
con.info("Transmit it to the new conspirator");
con.line(invite.to_string());
},
Self::Accept(true) => {
con.status("Invitation accepted!");
},
Self::Accept(false) => {
con.error("Failed to accept invitation!");
},
Self::StopServer(true) => {
con.status("Server stopped");
},
Self::StopServer(false) => {
con.error("Failed to stop server!");
},
Self::DispatchEvent(true) => {
con.status("Event dispatched");
},
Self::DispatchEvent(false) => {
con.error("Failed to dispatch event!");
},
Self::BindInterface(true) => {
con.status("Bound interface");
},
Self::BindInterface(false) => {
con.error("Failed to bind interface!");
},
Self::EnableInterface(true) => {
con.status("Enabled interface");
},
Self::EnableInterface(false) => {
con.error("Failed to enable interface!");
},
Self::DisableInterface(true) => {
con.status("Disabled interface");
},
Self::DisableInterface(false) => {
con.error("Failed to disable interface!");
},
Self::Write(true) => {
con.status("Value set!");
},
Self::Write(false) => {
con.error("Failed to set value!");
},
Self::AddAddress(true) => {
con.status("Added address");
},
Self::AddAddress(false) => {
con.error("Failed to add address!");
},
Self::RemoveInterface(true) => {
con.status("Interface removed");
},
Self::RemoveInterface(false) => {
con.error("Failed to remove interface!");
},
Self::Disable(true) => {
con.status("Conspirator disabled");
},
Self::Disable(false) => {
con.error("Failed to disable conspirator!");
},
Self::Join(true) => {
con.status("Join request sent");
},
Self::Join(false) => {
con.error("Failed to join!");
},
Self::SendPayload(true) => {
con.status("Message sent");
},
Self::SendPayload(false) => {
con.error("Failed to send message!");
}
};
}
}
#[test]
fn serialize() {
const EXAMPLE: &str = "example";
let name_arg = NodeArg::Name(EXAMPLE.to_string());
let id_arg = NodeIDArg::random();
let address = Address::V4UdpChaCha20(
"127.0.0.1:3030".parse().unwrap()
);
let if_address = InterfaceAddress::V4UdpChaCha20 {
addr: "127.0.0.1:3030".parse().unwrap(),
if_name: Some("lo".to_string()),
routes: vec!["127.0.0.0/8".parse().unwrap()]
};
let event = event::DynamicEvent {
src: id_arg.get(),
pyl: message::DynamicPayload::Push {
tag: "example.tag".to_string(),
msg: "Example Message".to_string()
}
};
use Command::*;
let commands = [
GetStatus,
StopServer,
Advertise,
Invite(KeyArg::random()),
Accept(Invitation::random()),
GetInterfaces,
BindInterface(if_address),
RemoveInterface(address),
GetHandlers,
DispatchEvent(event),
GetConspirator(NodeArg::Name(EXAMPLE.to_string())),
GetAllConspirators,
Write {
key: "String".to_string(),
value: "String".to_string()
},
AddAddress {
arg: name_arg.clone(),
addr: address
},
Disable(name_arg.clone()),
Join {
id: id_arg,
name: EXAMPLE.to_string(),
addr: address
},
SendPayload(
name_arg,
message::Payload::None,
Some(address)
)
];
for command in &commands {
let encoded = serde_json::to_string_pretty(command).unwrap();
println!("{encoded}");
let decoded: Command = serde_json::from_str(&encoded).unwrap();
assert_eq!(command, &decoded);
}
}