use crate::error::{Result, UrbitAPIError};
use crate::graph::{Node, NodeContents};
use crate::Channel;
use crossbeam::channel::{unbounded, Receiver};
use json::JsonValue;
use std::thread;
use std::time::Duration;
pub type Message = NodeContents;
#[derive(Clone, Debug)]
pub struct AuthoredMessage {
pub author: String,
pub contents: Message,
pub time_sent: String,
pub index: String,
}
impl AuthoredMessage {
pub fn new(author: &str, contents: &Message, time_sent: &str, index: &str) -> Self {
AuthoredMessage {
author: author.to_string(),
contents: contents.clone(),
time_sent: time_sent.to_string(),
index: index.to_string(),
}
}
pub fn from_node(node: &Node) -> Self {
Self::new(
&node.author,
&node.contents,
&node.time_sent_formatted(),
&node.index,
)
}
pub fn to_formatted_string(&self) -> String {
let content = self.contents.to_formatted_string();
format!("{} - ~{}:{}", self.time_sent, self.author, content)
}
}
pub trait Messaging {
fn channel(&mut self) -> &mut Channel;
fn send_message(
&mut self,
resource_ship: &str,
resource_name: &str,
message: &Message,
) -> Result<String> {
let node = self.channel().graph_store().new_node(message);
if let Ok(_) = self
.channel()
.graph_store()
.add_node(resource_ship, resource_name, &node)
{
Ok(node.index)
} else {
Err(UrbitAPIError::FailedToSendChatMessage(
message.to_json().dump(),
))
}
}
fn export_message_log(
&mut self,
resource_ship: &str,
resource_name: &str,
) -> Result<Vec<String>> {
let mut export_log = vec![];
let authored_messages = self.export_authored_messages(resource_ship, resource_name)?;
for am in authored_messages {
if !am.contents.is_empty() {
export_log.push(am.to_formatted_string());
}
}
Ok(export_log)
}
fn export_authored_messages(
&mut self,
resource_ship: &str,
resource_name: &str,
) -> Result<Vec<AuthoredMessage>> {
let mut authored_messages = vec![];
let nodes = self.export_message_nodes(resource_ship, resource_name)?;
for node in nodes {
if !node.contents.is_empty() {
let authored_message = AuthoredMessage::from_node(&node);
authored_messages.push(authored_message);
}
}
Ok(authored_messages)
}
fn export_message_nodes(
&mut self,
resource_ship: &str,
resource_name: &str,
) -> Result<Vec<Node>> {
let messages_graph = &self
.channel()
.graph_store()
.get_graph(resource_ship, resource_name)?;
let mut nodes = messages_graph.clone().nodes;
nodes.sort_by(|a, b| a.time_sent.cmp(&b.time_sent));
Ok(nodes)
}
fn subscribe_to_messages(
&mut self,
resource_ship: &str,
resource_name: &str,
) -> Result<Receiver<AuthoredMessage>> {
let resource_ship = resource_ship.to_string();
let resource_name = resource_name.to_string();
let (s, r) = unbounded();
let mut new_channel = self.channel().ship_interface.create_channel()?;
thread::spawn(move || {
let channel = &mut new_channel;
channel
.create_new_subscription("graph-store", "/updates")
.ok();
loop {
channel.parse_event_messages();
let res_graph_updates = &mut channel.find_subscription("graph-store", "/updates");
if let Some(graph_updates) = res_graph_updates {
loop {
let pop_res = graph_updates.pop_message();
if let Some(mess) = &pop_res {
if let Ok(json) = json::parse(mess) {
if !check_resource_json(&resource_ship, &resource_name, &json) {
continue;
}
if let Ok(node) = Node::from_graph_update_json(&json) {
let authored_message = AuthoredMessage::from_node(&node);
let _ = s.send(authored_message);
}
}
}
if let None = &pop_res {
break;
}
}
}
thread::sleep(Duration::new(0, 500000000));
}
});
Ok(r)
}
}
fn check_resource_json(
resource_ship: &str,
resource_name: &str,
resource_json: &JsonValue,
) -> bool {
let resource = resource_json["graph-update"]["add-nodes"]["resource"].clone();
let json_resource_name = format!("{}", resource["name"]);
let json_resource_ship = format!("~{}", resource["ship"]);
if json_resource_name == resource_name && json_resource_ship == resource_ship {
return true;
}
false
}