use scraps_libs::model::page_path::PagePath;
use crate::usecase::build::model::scrap_graph::{GraphNode, LinkDir, NODE_R, ScrapGraph};
#[derive(serde::Serialize, Clone, PartialEq, Debug)]
struct SerializeGraphNode {
ctx: Option<String>,
key: String,
title: String,
label: String,
url_path: String,
dir: &'static str,
x: f32,
y: f32,
r: f32,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
label_x: f32,
label_y: f32,
anchor: &'static str,
arrow_start: bool,
arrow_end: bool,
}
impl SerializeGraphNode {
fn new(node: &GraphNode, center_x: f32, center_y: f32) -> SerializeGraphNode {
let (x1, y1) = node.edge_start(center_x, center_y);
let (x2, y2) = node.edge_end(center_x, center_y);
SerializeGraphNode {
ctx: node.key.ctx().as_ref().map(|ctx| ctx.to_string()),
key: node.key.to_string(),
label: node.label.clone(),
title: node.title.clone(),
url_path: PagePath::of_scrap(&node.key).to_url_path(),
dir: match node.dir {
LinkDir::In => "in",
LinkDir::Out => "out",
LinkDir::Both => "both",
},
x: node.x,
y: node.y,
r: NODE_R,
x1,
y1,
x2,
y2,
label_x: node.label_x(center_x),
label_y: node.label_y(),
anchor: node.label_anchor(center_x),
arrow_start: node.arrow_at_center(),
arrow_end: node.arrow_at_node(),
}
}
}
#[derive(serde::Serialize, PartialEq, Debug)]
pub struct ScrapGraphTera {
width: f32,
height: f32,
center_x: f32,
center_y: f32,
center_r: f32,
center_label: String,
center_label_y: f32,
more_x: f32,
more_y: f32,
dropped: usize,
nodes: Vec<SerializeGraphNode>,
}
impl From<&ScrapGraph> for ScrapGraphTera {
fn from(graph: &ScrapGraph) -> Self {
ScrapGraphTera {
width: graph.width,
height: graph.height,
center_x: graph.center_x,
center_y: graph.center_y,
center_r: graph.center_r(),
center_label: graph.center_label.clone(),
center_label_y: graph.center_label_y(),
more_x: graph.width - 10.0,
more_y: graph.height - 10.0,
dropped: graph.dropped,
nodes: graph
.nodes
.iter()
.map(|node| SerializeGraphNode::new(node, graph.center_x, graph.center_y))
.collect(),
}
}
}
#[cfg(test)]
mod tests {
use scraps_libs::model::{context::Ctx, scrap::Scrap};
use crate::usecase::build::model::scrap_graph::MAX_NODES;
use super::*;
#[test]
fn it_serializes_direction_and_link_target() {
let inbound = Scrap::new("in", &Some(Ctx::from("guide")), "");
let outbound = Scrap::new("out", &None, "");
let graph = ScrapGraph::new("center", &[&inbound], &[&outbound], MAX_NODES).unwrap();
let tera = ScrapGraphTera::from(&graph);
let backlink = tera.nodes.iter().find(|n| n.title == "in").unwrap();
assert_eq!(backlink.dir, "in");
assert_eq!(backlink.ctx, Some("guide".to_string()));
assert_eq!(backlink.url_path, "scraps/guide/in.html");
assert!(backlink.arrow_start && !backlink.arrow_end);
assert_eq!(backlink.anchor, "end");
let link = tera.nodes.iter().find(|n| n.title == "out").unwrap();
assert_eq!(link.dir, "out");
assert!(!link.arrow_start && link.arrow_end);
assert_eq!(link.anchor, "start");
}
#[test]
fn it_stops_edges_short_of_both_endpoints() {
let outbound = Scrap::new("out", &None, "");
let graph = ScrapGraph::new("center", &[], &[&outbound], MAX_NODES).unwrap();
let tera = ScrapGraphTera::from(&graph);
let node = &tera.nodes[0];
let from_center =
((node.x1 - tera.center_x).powi(2) + (node.y1 - tera.center_y).powi(2)).sqrt();
assert!(from_center > 0.0, "edge starts away from the centre point");
let to_node = ((node.x - node.x2).powi(2) + (node.y - node.y2).powi(2)).sqrt();
assert!(to_node > 0.0, "edge stops short of the node circle");
}
#[test]
fn it_shortens_a_long_title_but_keeps_it_for_the_tooltip() {
let long = "a very long scrap title that will not fit on one node";
let graph =
ScrapGraph::new("center", &[], &[&Scrap::new(long, &None, "")], MAX_NODES).unwrap();
let tera = ScrapGraphTera::from(&graph);
assert_eq!(tera.nodes[0].title, long);
assert_eq!(tera.nodes[0].label, "a very long scra…");
}
#[test]
fn it_names_a_node_by_its_title_and_links_it_by_its_key() {
let orders = Scrap::new(
"orders",
&Some(Ctx::from("tables")),
"---\ntitle: Customer Orders Of The Whole Year\n---\n",
);
let graph = ScrapGraph::new("center", &[], &[&orders], MAX_NODES).unwrap();
let tera = ScrapGraphTera::from(&graph);
assert_eq!(tera.nodes[0].title, "Customer Orders Of The Whole Year");
assert_eq!(tera.nodes[0].label, "Customer Orders …");
assert_eq!(tera.nodes[0].key, "tables/orders");
assert_eq!(tera.nodes[0].url_path, "scraps/tables/orders.html");
}
}