1use crate::error::{Result, UrbitAPIError};
2use chrono::prelude::*;
3use json::{object, JsonValue};
4use regex::Regex;
5
6#[derive(Clone, Debug)]
9pub struct Graph {
10 pub nodes: Vec<Node>,
12}
13
14#[derive(Clone, Debug)]
16pub struct Node {
17 pub index: String,
18 pub author: String,
19 pub time_sent: u64,
20 pub signatures: Vec<Signature>,
21 pub contents: NodeContents,
22 pub hash: Option<String>,
23 pub children: Vec<Node>,
24}
25
26#[derive(Debug, Clone)]
28pub struct NodeContents {
29 pub content_list: Vec<JsonValue>,
30}
31
32#[derive(Debug, Clone)]
34pub struct Signature {
35 signature: String,
36 life: u64,
37 ship: String,
38}
39
40impl Graph {
41 pub fn new(nodes: Vec<Node>) -> Graph {
43 Graph { nodes: nodes }
44 }
45
46 pub fn insert(&mut self, node: Node) {
48 self.nodes.push(node);
49 }
50
51 pub fn from_json(graph_json: JsonValue) -> Result<Graph> {
53 let mut graph = Graph::new(vec![]);
55 let mut childless_nodes = vec![];
57 let mut graph_text = format!("{}", graph_json["graph-update"]["add-graph"]["graph"]);
59 if graph_text == "null" {
60 graph_text = format!("{}", graph_json["graph-update"]["add-nodes"]["nodes"]);
61 }
62
63 if graph_text == "{}" {
65 return Ok(Graph::new(vec![]));
66 }
67
68 let re = Regex::new(r#"\d+":(.+?children":).+?"#)
70 .map_err(|_| UrbitAPIError::FailedToCreateGraphFromJSON)?;
71 for capture in re.captures_iter(&graph_text) {
73 let node_string = capture
75 .get(1)
76 .ok_or(UrbitAPIError::FailedToCreateGraphFromJSON)?
77 .as_str()
78 .to_string()
79 + r#"null}"#;
80 let json = json::parse(&node_string)
81 .map_err(|_| UrbitAPIError::FailedToCreateGraphNodeFromJSON)?;
82 if json["post"].is_string() {
84 continue;
85 }
86 let processed_node_opt = Node::from_json(&json);
88 if processed_node_opt.is_err() {
89 println!("Failed to process graph node: \n{}", node_string);
90 }
91 childless_nodes.push(processed_node_opt?);
92 }
93
94 if childless_nodes.len() == 0 {
96 return Err(UrbitAPIError::FailedToCreateGraphFromJSON);
97 }
98
99 let mut building_node = childless_nodes[0].clone();
102 for i in 1..childless_nodes.len() {
105 if building_node.is_ancestor(&childless_nodes[i]) {
106 building_node = building_node.add_child(&childless_nodes[i]);
108 } else {
109 graph.insert(building_node.clone());
111 building_node = childless_nodes[i].clone();
112 }
113 }
114 graph.insert(building_node.clone());
117
118 Ok(graph)
120 }
121
122 pub fn to_json(&self) -> JsonValue {
124 let nodes_json: Vec<JsonValue> = self.nodes.iter().map(|n| n.to_json()).collect();
125 object! {
126 "graph-update": {
127 "add-graph": {
128 "graph": nodes_json,
129 }
130 }
131 }
132 }
133}
134
135impl Node {
136 pub fn new(
138 index: String,
139 author: String,
140 time_sent: u64,
141 signatures: Vec<Signature>,
142 contents: NodeContents,
143 hash: Option<String>,
144 ) -> Node {
145 Node {
146 index: index,
147 author: author,
148 time_sent: time_sent,
149 signatures: signatures,
150 contents: contents,
151 hash: hash,
152 children: vec![],
153 }
154 }
155
156 pub fn index_tail(&self) -> String {
158 let split_index: Vec<&str> = self.index.split("/").collect();
159 split_index[split_index.len() - 1].to_string()
160 }
161
162 pub fn parent_index(&self) -> Option<String> {
164 let rev_index = self.index.chars().rev().collect::<String>();
165 let split_index: Vec<&str> = rev_index.splitn(2, "/").collect();
166 if split_index.len() < 2 {
168 return None;
169 }
170
171 let parent_index = split_index[1].chars().rev().collect::<String>();
172
173 Some(parent_index)
174 }
175
176 pub fn is_parent(&self, potential_child: &Node) -> bool {
178 if let Some(index) = potential_child.parent_index() {
179 return self.index == index;
180 }
181 false
182 }
183
184 pub fn is_ancestor(&self, potential_child: &Node) -> bool {
186 let pc_split_index: Vec<&str> = potential_child.index.split("/").collect();
187 let parent_split_index: Vec<&str> = self.index.split("/").collect();
188
189 if parent_split_index.len() > pc_split_index.len() {
191 return false;
192 }
193
194 let mut matching = false;
197 for n in 0..parent_split_index.len() {
198 matching = parent_split_index[n] == pc_split_index[n]
199 }
200
201 matching
203 }
204
205 pub fn add_child(&self, new_child: &Node) -> Node {
208 let mut new_self = self.clone();
209 for i in 0..self.children.len() {
210 let child = &new_self.children[i];
211 if child.is_parent(new_child) {
212 new_self.children[i].children.push(new_child.clone());
213 return new_self;
214 } else if child.is_ancestor(new_child) {
215 new_self.children[i] = child.add_child(new_child);
216 return new_self;
217 }
218 }
219 new_self.children.push(new_child.clone());
220 new_self
221 }
222 pub fn time_sent_formatted(&self) -> String {
224 let unix_time = self.time_sent as i64 / 1000;
225 let date_time: DateTime<Utc> =
226 DateTime::from_utc(NaiveDateTime::from_timestamp(unix_time, 0), Utc);
227 let new_date = date_time.format("%Y-%m-%d %H:%M:%S");
228 format!("{}", new_date)
229 }
230
231 pub fn to_json(&self) -> JsonValue {
234 let mut node_json = object!();
235 node_json[self.index.clone()] = self.to_json_value();
236 node_json
237 }
238
239 fn to_json_value(&self) -> JsonValue {
242 let mut children = object!();
243 for child in &self.children {
244 children[child.index_tail()] = child.to_json_value();
245 }
246
247 let result_json = object! {
248 "post": {
249 "author": self.author.clone(),
250 "index": self.index.clone(),
251 "time-sent": self.time_sent,
252 "contents": self.contents.to_json(),
253 "hash": null,
254 "signatures": []
255 },
256 "children": children
257 };
258 result_json
259 }
260
261 pub fn from_graph_update_json(wrapped_json: &JsonValue) -> Result<Node> {
264 let dumped = wrapped_json["graph-update"]["add-nodes"]["nodes"].dump();
265 let split: Vec<&str> = dumped.splitn(2, ":").collect();
266 if split.len() <= 1 {
267 return Err(UrbitAPIError::FailedToCreateGraphNodeFromJSON);
268 }
269
270 let mut inner_string = split[1].to_string();
271 inner_string.remove(inner_string.len() - 1);
272
273 let inner_json = json::parse(&inner_string)
274 .map_err(|_| UrbitAPIError::FailedToCreateGraphNodeFromJSON)?;
275
276 Self::from_json(&inner_json)
277 }
278
279 pub fn from_json(json: &JsonValue) -> Result<Node> {
281 let children = json["children"].clone();
283 let post_json = json["post"].clone();
284
285 let index = post_json["index"]
286 .as_str()
287 .ok_or(UrbitAPIError::FailedToCreateGraphNodeFromJSON)?;
288 let author = post_json["author"]
289 .as_str()
290 .ok_or(UrbitAPIError::FailedToCreateGraphNodeFromJSON)?;
291 let time_sent = post_json["time-sent"]
292 .as_u64()
293 .ok_or(UrbitAPIError::FailedToCreateGraphNodeFromJSON)?;
294
295 let mut json_contents = vec![];
297 for content in post_json["contents"].members() {
298 json_contents.push(content.clone());
299 }
300 let contents = NodeContents::from_json(json_contents);
301
302 let hash = match post_json["contents"]["hash"].is_null() {
304 true => None,
305 false => Some(
306 post_json["contents"]["hash"]
307 .as_str()
308 .ok_or(UrbitAPIError::FailedToCreateGraphNodeFromJSON)?
309 .to_string(),
310 ),
311 };
312
313 let mut signatures = vec![];
315 for signature in post_json["signatures"].members() {
316 let sig = Signature {
317 signature: signature["signature"]
318 .as_str()
319 .ok_or(UrbitAPIError::FailedToCreateGraphNodeFromJSON)?
320 .to_string(),
321 life: signature["life"]
322 .as_u64()
323 .ok_or(UrbitAPIError::FailedToCreateGraphNodeFromJSON)?,
324 ship: signature["ship"]
325 .as_str()
326 .ok_or(UrbitAPIError::FailedToCreateGraphNodeFromJSON)?
327 .to_string(),
328 };
329
330 signatures.push(sig);
331 }
332
333 let mut node_children: Vec<Node> = vec![];
335 if let JsonValue::Object(o) = children {
336 for (_, val) in o.iter() {
337 if let Ok(child_node) = Node::from_json(val) {
338 node_children.push(child_node);
339 }
340 }
341 }
342
343 Ok(Node {
344 index: index.to_string(),
345 author: author.to_string(),
346 time_sent: time_sent,
347 signatures: signatures,
348 contents: contents,
349 hash: hash,
350 children: node_children,
351 })
352 }
353}
354
355impl NodeContents {
357 pub fn new() -> NodeContents {
359 NodeContents {
360 content_list: vec![],
361 }
362 }
363
364 pub fn is_empty(&self) -> bool {
366 self.content_list.len() == 0
367 }
368
369 pub fn add_text(&self, text: &str) -> NodeContents {
371 let formatted = object! {
372 "text": text
373 };
374 self.add_to_contents(formatted)
375 }
376
377 pub fn add_url(&self, url: &str) -> NodeContents {
379 let formatted = object! {
380 "url": url
381 };
382 self.add_to_contents(formatted)
383 }
384
385 pub fn add_mention(&self, referenced_ship: &str) -> NodeContents {
387 let formatted = object! {
388 "mention": referenced_ship
389 };
390 self.add_to_contents(formatted)
391 }
392
393 pub fn add_code(&self, expression: &str, output: &str) -> NodeContents {
395 let formatted = object! {
396 "code": {
397 "expression": expression,
398 "output": [[output]]
399 }
400 };
401 self.add_to_contents(formatted)
402 }
403
404 pub fn from_json(json_contents: Vec<JsonValue>) -> NodeContents {
406 NodeContents {
407 content_list: json_contents,
408 }
409 }
410
411 pub fn to_json(&self) -> JsonValue {
413 self.content_list.clone().into()
414 }
415
416 pub fn to_formatted_string(&self) -> String {
419 let mut result = "".to_string();
420 for item in &self.content_list {
421 let text = Self::extract_content_text(item);
423 result = result + " " + text.trim();
424 }
425 result
426 }
427
428 pub fn to_formatted_words(&self) -> Vec<String> {
432 let formatted_string = self.to_formatted_string();
433 formatted_string
434 .split_whitespace()
435 .map(|s| s.to_string())
436 .collect()
437 }
438
439 fn extract_content_text(json: &JsonValue) -> String {
441 let mut result = " ".to_string();
442 if !json["text"].is_empty() {
443 result = json["text"].dump();
444 } else if !json["url"].is_empty() {
445 result = json["url"].dump();
446 } else if !json["mention"].is_empty() {
447 result = json["mention"].dump();
448 result.remove(0);
449 result.remove(result.len() - 1);
450 return format!("~{}", result);
451 } else if !json["code"].is_empty() {
452 result = json["code"].dump();
453 }
454 result.remove(0);
455 result.remove(result.len() - 1);
456 result
457 }
458
459 fn add_to_contents(&self, json: JsonValue) -> NodeContents {
461 let mut contents = self.content_list.clone();
462 contents.append(&mut vec![json]);
463 NodeContents {
464 content_list: contents,
465 }
466 }
467}