Expand Graph module test coverage
This commit is contained in:
parent
6eea10f74d
commit
817777d7d6
3 changed files with 357 additions and 18 deletions
370
src/graph.rs
370
src/graph.rs
|
|
@ -22,7 +22,9 @@ pub mod meta;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, Debug)]
|
||||||
pub struct Graph {
|
pub struct Graph {
|
||||||
|
#[serde(default)]
|
||||||
pub nodes: HashMap<String, Node>,
|
pub nodes: HashMap<String, Node>,
|
||||||
|
#[serde(default)]
|
||||||
pub root_node: String,
|
pub root_node: String,
|
||||||
#[serde(skip_deserializing)]
|
#[serde(skip_deserializing)]
|
||||||
pub incoming: HashMap<String, Vec<Edge>>,
|
pub incoming: HashMap<String, Vec<Edge>>,
|
||||||
|
|
@ -37,6 +39,7 @@ pub struct Graph {
|
||||||
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, Debug)]
|
||||||
pub struct Stats {
|
pub struct Stats {
|
||||||
pub detached: HashMap<String, u32>,
|
pub detached: HashMap<String, u32>,
|
||||||
|
pub detached_total: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Graph {
|
impl Graph {
|
||||||
|
|
@ -63,9 +66,10 @@ impl Graph {
|
||||||
graph
|
graph
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a TOML file from the default location and returns a modulated Graph
|
/// Loads a Graph TOML file from CLI arguments or their defaults and
|
||||||
|
/// returns a modulated Graph.
|
||||||
///
|
///
|
||||||
/// Returns a graph with an error message if any errors are propagated to it.
|
/// Returns a graph with an error message if any errors are propagated.
|
||||||
pub fn load() -> Graph {
|
pub fn load() -> Graph {
|
||||||
let result = Graph::load_file(None);
|
let result = Graph::load_file(None);
|
||||||
match result {
|
match result {
|
||||||
|
|
@ -76,12 +80,12 @@ impl Graph {
|
||||||
|
|
||||||
/// Takes a file path to a TOML file and returns a modulated Graph
|
/// Takes a file path to a TOML file and returns a modulated Graph
|
||||||
///
|
///
|
||||||
/// If `path` is an empty string, it will fallback to CLI arguments
|
/// If `path` is None, it will fallback to CLI arguments or their defaults.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Propagates errors from `Graph::read_file`.
|
/// Propagates errors from `Graph::read_file`.
|
||||||
pub fn load_file(path: Option<&str>) -> Result<Graph, String> {
|
pub fn load_file(path: Option<&str>) -> Result<Graph, String> {
|
||||||
let mut graph = Graph::read_file(path)?;
|
let mut graph = Graph::from_file(path)?;
|
||||||
graph.modulate();
|
graph.modulate();
|
||||||
Ok(graph)
|
Ok(graph)
|
||||||
}
|
}
|
||||||
|
|
@ -91,7 +95,7 @@ impl Graph {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Returns Err if it can't read the contents of `in_path`.
|
/// Returns Err if it can't read the contents of `in_path`.
|
||||||
/// Propagates errors from `Graph::from_serial`.
|
/// Propagates errors from `Graph::from_serial`.
|
||||||
pub fn read_file(in_path: Option<&str>) -> Result<Graph, String> {
|
pub fn from_file(in_path: Option<&str>) -> Result<Graph, String> {
|
||||||
let cli_path = Arguments::default().parse().graph_path;
|
let cli_path = Arguments::default().parse().graph_path;
|
||||||
let path = in_path.map_or(cli_path, PathBuf::from);
|
let path = in_path.map_or(cli_path, PathBuf::from);
|
||||||
|
|
||||||
|
|
@ -143,19 +147,16 @@ impl Graph {
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// Errors on unsupported formats.
|
/// Errors on unsupported formats.
|
||||||
/// Propagates serialization errors.
|
/// Propagates serialization errors.
|
||||||
pub fn to_serial(
|
pub fn to_serial(&self, format: &Format) -> Result<String, SerialError> {
|
||||||
graph: &Graph,
|
|
||||||
format: &Format,
|
|
||||||
) -> Result<String, SerialError> {
|
|
||||||
match *format {
|
match *format {
|
||||||
Format::TOML => match toml::to_string(graph) {
|
Format::TOML => match toml::to_string(self) {
|
||||||
Ok(s) => Ok(s),
|
Ok(s) => Ok(s),
|
||||||
Err(e) => Err(SerialError {
|
Err(e) => Err(SerialError {
|
||||||
cause: SerialErrorCause::MalformedInput,
|
cause: SerialErrorCause::MalformedInput,
|
||||||
message: e.to_string(),
|
message: e.to_string(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Format::JSON => match serde_json::to_string(graph) {
|
Format::JSON => match serde_json::to_string(self) {
|
||||||
Ok(s) => Ok(s),
|
Ok(s) => Ok(s),
|
||||||
Err(e) => Err(SerialError {
|
Err(e) => Err(SerialError {
|
||||||
cause: SerialErrorCause::MalformedInput,
|
cause: SerialErrorCause::MalformedInput,
|
||||||
|
|
@ -184,7 +185,7 @@ impl Graph {
|
||||||
tlog!(&instant, "Parsed configuration");
|
tlog!(&instant, "Parsed configuration");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct a HashMap with incoming connections (reversed edges)
|
/// Construct a HashMap with incoming connections (reversed edges)
|
||||||
fn map_incoming(&mut self) {
|
fn map_incoming(&mut self) {
|
||||||
for node in self.nodes.clone().into_values() {
|
for node in self.nodes.clone().into_values() {
|
||||||
for edge in node.connections.clone().unwrap_or_default().values() {
|
for edge in node.connections.clone().unwrap_or_default().values() {
|
||||||
|
|
@ -210,13 +211,18 @@ impl Graph {
|
||||||
for (connection_key, edge) in connections {
|
for (connection_key, edge) in connections {
|
||||||
let mut new_edge = edge.clone();
|
let mut new_edge = edge.clone();
|
||||||
|
|
||||||
// Populate empty "from" IDs in edges with node's ID
|
// Populate empty "to" and "from" IDs
|
||||||
if edge.from.is_empty() {
|
if edge.from.is_empty() {
|
||||||
new_edge.from.clone_from(&connection_key);
|
new_edge.from.clone_from(&key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if edge.to.is_empty() {
|
||||||
|
new_edge.to.clone_from(&connection_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flag detached edges
|
// Flag detached edges
|
||||||
if in_nodes.contains_key(&edge.to) {
|
if (!edge.to.is_empty() && in_nodes.contains_key(&edge.to)) ||
|
||||||
|
(edge.to.is_empty() && in_nodes.contains_key(&new_edge.to)) {
|
||||||
new_edge.detached = false;
|
new_edge.detached = false;
|
||||||
} else {
|
} else {
|
||||||
new_edge.detached = true;
|
new_edge.detached = true;
|
||||||
|
|
@ -255,7 +261,7 @@ impl Graph {
|
||||||
// Populate empty summaries with the leading part of the node text
|
// Populate empty summaries with the leading part of the node text
|
||||||
let summary = if node.summary.is_empty() {
|
let summary = if node.summary.is_empty() {
|
||||||
let first_line = if let Some(first) =
|
let first_line = if let Some(first) =
|
||||||
node.text.lines().find(|s| !s.is_empty())
|
node.text.split("\n\n").find(|s| !s.is_empty())
|
||||||
{
|
{
|
||||||
String::from(first)
|
String::from(first)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -295,6 +301,7 @@ impl Graph {
|
||||||
let graph = self.clone();
|
let graph = self.clone();
|
||||||
let iterator = self.nodes.iter_mut();
|
let iterator = self.nodes.iter_mut();
|
||||||
for (key, node) in iterator {
|
for (key, node) in iterator {
|
||||||
|
|
||||||
// Parse node text
|
// Parse node text
|
||||||
let parse_output = content::rich_parse(&node.text, &graph);
|
let parse_output = content::rich_parse(&node.text, &graph);
|
||||||
node.text = parse_output.text.unwrap_or_default();
|
node.text = parse_output.text.unwrap_or_default();
|
||||||
|
|
@ -352,6 +359,8 @@ impl Graph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_detached(&mut self, node_id: &str) {
|
fn increment_detached(&mut self, node_id: &str) {
|
||||||
|
|
@ -360,6 +369,7 @@ impl Graph {
|
||||||
.entry(node_id.to_string())
|
.entry(node_id.to_string())
|
||||||
.and_modify(|count| *count = count.saturating_add(1))
|
.and_modify(|count| *count = count.saturating_add(1))
|
||||||
.or_insert(1);
|
.or_insert(1);
|
||||||
|
self.stats.detached_total = self.stats.detached_total.saturating_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map_lowercase_keys(&mut self) {
|
pub fn map_lowercase_keys(&mut self) {
|
||||||
|
|
@ -557,6 +567,334 @@ mod tests {
|
||||||
e.message.contains("expected value at line 1 column 1")
|
e.message.contains("expected value at line 1 column 1")
|
||||||
},));
|
},));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn with_message() {
|
||||||
|
let payload = "QmMxohuLe9DZCOzcaxH2wzZGqOot1In6";
|
||||||
|
let graph = Graph::with_message(payload);
|
||||||
|
assert_eq!(payload, graph.meta.messages.first().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_without_message() {
|
||||||
|
let graph = Graph::malformed(None);
|
||||||
|
assert!(graph.meta.messages.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn malformed_with_message() {
|
||||||
|
let payload = "s8LuGwRQA4GdNGvAlaIUrryZYBGkY5Ev";
|
||||||
|
let graph = Graph::malformed(Some(payload));
|
||||||
|
assert_eq!(payload, graph.meta.messages.first().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bad_deserial_input() {
|
||||||
|
let result = Graph::from_serial("not toml", &Format::TOML);
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(matches!(result.unwrap_err().cause, SerialErrorCause::MalformedInput));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bad_deserial_format() {
|
||||||
|
let result = Graph::from_serial("not toml", &Format::Unsupported);
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(matches!(result.unwrap_err().cause, SerialErrorCause::UnsupportedFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bad_serial_format() {
|
||||||
|
let result = Graph::load().to_serial(&Format::Unsupported);
|
||||||
|
assert!(result.is_err());
|
||||||
|
assert!(matches!(result.unwrap_err().cause, SerialErrorCause::UnsupportedFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_modulated_graph_is_empty() {
|
||||||
|
let mut graph = Graph::from_serial("", &Format::TOML).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
assert!(graph.nodes.is_empty());
|
||||||
|
assert!(graph.incoming.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn title_population_from_id() {
|
||||||
|
let mut graph = Graph::from_serial(concat!(
|
||||||
|
"[nodes.TitlelessNode]\n",
|
||||||
|
r#"text = "Some text""#,
|
||||||
|
),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
let node = graph.nodes.get("TitlelessNode");
|
||||||
|
assert_eq!(node.unwrap().title, "TitlelessNode");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_title_population_from_id_if_title_set() {
|
||||||
|
let mut graph = Graph::from_serial(concat!(
|
||||||
|
"[nodes.TitlefulNode]\n",
|
||||||
|
r#"title = "A Title""#, "\n",
|
||||||
|
r#"text = "Some text""#,
|
||||||
|
),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
let node = graph.nodes.get("TitlefulNode");
|
||||||
|
assert_eq!(node.unwrap().title, "A Title");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detached_edge_is_flagged() {
|
||||||
|
let mut graph = Graph::from_serial(concat!(
|
||||||
|
"[nodes.Node]\n",
|
||||||
|
r#"text = "Some text here""#, "\n\n",
|
||||||
|
"[nodes.Node.connections.Nowhere]\n",
|
||||||
|
),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
let node = graph.nodes.get("Node").unwrap();
|
||||||
|
let connections = node.connections.as_ref().unwrap();
|
||||||
|
let connection = connections.get("Nowhere").unwrap();
|
||||||
|
assert!(connection.detached);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn attached_edge_is_not_flagged() {
|
||||||
|
let mut graph = Graph::from_serial(concat!(
|
||||||
|
"[nodes.NodeOne]\n",
|
||||||
|
r#"text = "Some text here""#, "\n\n",
|
||||||
|
"[nodes.NodeOne.connections.NodeTwo]\n\n",
|
||||||
|
"[nodes.NodeTwo]\n",
|
||||||
|
r#"text = "Some other text here""#, "\n\n",
|
||||||
|
),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
let node = graph.nodes.get("NodeOne").unwrap();
|
||||||
|
let connections = node.connections.as_ref().unwrap();
|
||||||
|
let connection = connections.get("NodeTwo").unwrap();
|
||||||
|
println!("{connection:#?}");
|
||||||
|
assert!(!connection.detached);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_and_from_population() {
|
||||||
|
let mut graph = Graph::from_serial(concat!(
|
||||||
|
"[nodes.n01]\n",
|
||||||
|
"[nodes.n01.connections.n02]\n\n",
|
||||||
|
"[nodes.n02]\n",
|
||||||
|
"[nodes.n02.connections.n03]\n\n",
|
||||||
|
),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
let n01 = graph.nodes.get("n01").unwrap();
|
||||||
|
let n02 = graph.nodes.get("n02").unwrap();
|
||||||
|
let n01_connections = n01.connections.as_ref().unwrap();
|
||||||
|
let n02_connections = n02.connections.as_ref().unwrap();
|
||||||
|
let n01_to_n02 = n01_connections.get("n02").unwrap();
|
||||||
|
let n02_to_n03 = n02_connections.get("n03").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(n01_to_n02.from, "n01");
|
||||||
|
assert_eq!(n01_to_n02.to, "n02");
|
||||||
|
assert!(!n01_to_n02.detached);
|
||||||
|
assert_eq!(n02_to_n03.from, "n02");
|
||||||
|
assert_eq!(n02_to_n03.to, "n03");
|
||||||
|
assert!(n02_to_n03.detached);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn links_become_connections() {
|
||||||
|
let mut graph = Graph::from_serial(concat!(
|
||||||
|
"[nodes.n01]\n",
|
||||||
|
r#"links = [ "n02", "n03", "n04" ]"#, "\n\n",
|
||||||
|
"[nodes.n02]\n",
|
||||||
|
"[nodes.n04]\n",
|
||||||
|
r#"links = [ "n01", "n03" ]"#, "\n\n",
|
||||||
|
),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
let n01 = graph.nodes.get("n01").unwrap();
|
||||||
|
let n02 = graph.nodes.get("n02").unwrap();
|
||||||
|
let n04 = graph.nodes.get("n04").unwrap();
|
||||||
|
|
||||||
|
let n01_connections = n01.connections.as_ref().unwrap();
|
||||||
|
let n02_connections = n02.connections.as_ref().unwrap();
|
||||||
|
let n04_connections = n04.connections.as_ref().unwrap();
|
||||||
|
|
||||||
|
let n01_to_n02 = n01_connections.get("n02").unwrap();
|
||||||
|
let n01_to_n03 = n01_connections.get("n03").unwrap();
|
||||||
|
let n01_to_n04 = n01_connections.get("n04").unwrap();
|
||||||
|
|
||||||
|
let n04_to_n01 = n04_connections.get("n01").unwrap();
|
||||||
|
let n04_to_n03 = n04_connections.get("n03").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(n01_to_n02.from, "n01");
|
||||||
|
assert_eq!(n01_to_n02.to, "n02");
|
||||||
|
assert!(!n01_to_n02.detached);
|
||||||
|
|
||||||
|
assert_eq!(n01_to_n03.from, "n01");
|
||||||
|
assert_eq!(n01_to_n03.to, "n03");
|
||||||
|
assert!(n01_to_n03.detached);
|
||||||
|
|
||||||
|
assert_eq!(n01_to_n04.from, "n01");
|
||||||
|
assert_eq!(n01_to_n04.to, "n04");
|
||||||
|
assert!(!n01_to_n04.detached);
|
||||||
|
|
||||||
|
assert!(n02_connections.is_empty());
|
||||||
|
|
||||||
|
assert_eq!(n04_to_n01.from, "n04");
|
||||||
|
assert_eq!(n04_to_n01.to, "n01");
|
||||||
|
assert!(!n04_to_n01.detached);
|
||||||
|
|
||||||
|
assert_eq!(n04_to_n03.from, "n04");
|
||||||
|
assert_eq!(n04_to_n03.to, "n03");
|
||||||
|
assert!(n04_to_n03.detached);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detached_count_increments() {
|
||||||
|
let mut graph = Graph::from_serial(concat!(
|
||||||
|
"[nodes.n01]\n",
|
||||||
|
r#"links = [ "n02", "n03", "n04", "n05", "n06", "n10" ]"#, "\n\n",
|
||||||
|
"[nodes.n02]\n",
|
||||||
|
"[nodes.n04]\n",
|
||||||
|
r#"links = [ "n01", "n02", "n03", "n06", "n11", "n15" ]"#, "\n\n"),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
assert_eq!(graph.stats.detached_total, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn populated_summary() {
|
||||||
|
let text = "vh18qEUN22X2SxLj6lpOOzMBB4N6S0UG";
|
||||||
|
let mut graph = Graph::from_serial(&format!(
|
||||||
|
"[nodes.n01]\n\
|
||||||
|
text = \"{text}\"\n\
|
||||||
|
"),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
assert!(graph.nodes.get("n01").unwrap().summary.contains(text));
|
||||||
|
assert!(graph.nodes.get("n01").unwrap().text.contains(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn supplied_summary() {
|
||||||
|
let text = "vh18qEUN22X2SxLj6lpOOzMBB4N6S0UG";
|
||||||
|
let summary = "W5dhPgNs7S1Zsq6uPK47MAw8xXyNxwep";
|
||||||
|
let mut graph = Graph::from_serial(&format!(
|
||||||
|
"[nodes.n01]\n\
|
||||||
|
summary = \"{summary}\"\n\
|
||||||
|
text = \"{text}\"\n\
|
||||||
|
",
|
||||||
|
),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
assert_eq!(graph.nodes.get("n01").unwrap().summary, summary);
|
||||||
|
assert!(graph.nodes.get("n01").unwrap().text.contains(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn summary_from_first_sentence() {
|
||||||
|
let first_sentence = "zTWFX0a8 tYTO2g.";
|
||||||
|
let text = format!(
|
||||||
|
"{first_sentence} QoGa PtDsJ vh18qE U N22 X2S. MBB4N6S0UG\n\n\
|
||||||
|
6FokUX o OCEc LzZFfR1nkqa hWIF LdrtD3G. PDQwv Ba2PnZ yEBVpqQdt\n\n\
|
||||||
|
Py6aoPK FV7iU UdrYB vD UeMvvg u 5kbt 9ZW9x7MR"
|
||||||
|
);
|
||||||
|
let mut graph = Graph::from_serial(&format!(
|
||||||
|
"[nodes.n01]\ntext = \"\"\"{text}\"\"\"\n"),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
assert_eq!(graph.nodes.get("n01").unwrap().summary, first_sentence);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn summary_from_first_paragraph() {
|
||||||
|
let first_paragraph = "EGR6IS fTQsRp rv7 g jvItnYU 2HNciS MID\n\
|
||||||
|
iz3vx vXxa vW4JI6l E5itd6qm2Yx gFw 1D Nq0805 bXHe3h iqABe ilnHKl\n\
|
||||||
|
F4AHvMLto cz3C Z279r9 jtIbBnY JqjwZPQdepf cdv6";
|
||||||
|
let text = format!(
|
||||||
|
"{first_paragraph}\n\nn0D CvHcIU7R oPcZy V1Iy9PgXO gOw lfeDy\n\n\
|
||||||
|
jrOSq 0uVtJLd Idx08Bpy BBj 4PVS R9lt RqjTs s AURUx93 Xu9WiI0rP.\n"
|
||||||
|
);
|
||||||
|
let mut graph = Graph::from_serial(
|
||||||
|
format!("[nodes.n01]\ntext = \"\"\"{text}\"\"\"\n").as_str(),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
assert_eq!(graph.nodes.get("n01").unwrap().summary, first_paragraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn summary_from_first_300_chars() {
|
||||||
|
let first_300 = concat!("Primis, quod, cum in rerum natura duo ",
|
||||||
|
"quaerenda sint, unum, quae materia sit, ex qua quaeque res ",
|
||||||
|
"efficiatur, alterum, quae naturales essent nec tamen id, cuius ",
|
||||||
|
"causa haec finxerat, assecutus est: Nam si omnes veri erunt, ut ",
|
||||||
|
"Epicuri ratio docet, tum denique poterit aliquid cognosci et ",
|
||||||
|
"percipi? Quos q");
|
||||||
|
let tail = concat!("uam autem et praeterita grate meminit et ",
|
||||||
|
"praesentibus ita potitur, ut animadvertat quanta sint ea ",
|
||||||
|
"quamque iucunda, neque pendet ex futuris, sed expectat illa, ",
|
||||||
|
"fruitur praesentibus ab iisque vitii");
|
||||||
|
let text = format!("{first_300}{tail}");
|
||||||
|
let summary = format!("{first_300}…");
|
||||||
|
|
||||||
|
let mut graph = Graph::from_serial(
|
||||||
|
format!("[nodes.n01]\ntext = \"\"\"{text}\"\"\"\n").as_str(),
|
||||||
|
&Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
assert_eq!(graph.nodes.get("n01").unwrap().summary, summary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anchors_become_connections() {
|
||||||
|
|
||||||
|
let mut graph = Graph::from_serial("\
|
||||||
|
[nodes.n1]
|
||||||
|
text = 'an anchor to |n2|, the existing node'
|
||||||
|
|
||||||
|
[nodes.n2]
|
||||||
|
text = 'an anchor to |n0|, the nonexistent node'
|
||||||
|
|
||||||
|
", &Format::TOML,
|
||||||
|
).unwrap();
|
||||||
|
graph.modulate();
|
||||||
|
|
||||||
|
let n1_to_n2 = graph.nodes.get("n1").unwrap()
|
||||||
|
.connections.as_ref().unwrap().get("n2");
|
||||||
|
|
||||||
|
let n2_to_n0 = graph.nodes.get("n2").unwrap()
|
||||||
|
.connections.as_ref().unwrap().get("n0");
|
||||||
|
|
||||||
|
println!("{n1_to_n2:#?}");
|
||||||
|
println!("{n2_to_n0:#?}");
|
||||||
|
|
||||||
|
assert!(!n1_to_n2.unwrap().detached);
|
||||||
|
assert!(n2_to_n0.unwrap().detached);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, Debug)]
|
||||||
pub struct Edge {
|
pub struct Edge {
|
||||||
|
#[serde(default)]
|
||||||
pub to: String,
|
pub to: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub from: String,
|
pub from: String,
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,7 @@ If you want to render a literal backslash, you can escape the backslash itself b
|
||||||
|
|
||||||
## Interactions with TOML escaping
|
## Interactions with TOML escaping
|
||||||
|
|
||||||
Notice that TOML itself also handles escape codes, so to pass a backslash you will need to escape it as a double backslash inside strings delimited by double quotes or triple double quotes. You can use a single backslash inside a string delimited by single quotes:
|
Notice that |TOML| itself also handles escape codes, so to pass a backslash you will need to escape it as a double backslash inside strings delimited by double quotes or triple double quotes. You can use a single backslash inside a string delimited by single quotes:
|
||||||
|
|
||||||
`
|
`
|
||||||
[node.Double]
|
[node.Double]
|
||||||
|
|
@ -396,7 +396,7 @@ Here too: \\\\*
|
||||||
'''
|
'''
|
||||||
`
|
`
|
||||||
|
|
||||||
This has nothing to do with en's markup syntax per se, it's just a consequence of how backslashes are also special in |TOML| syntax. For more details, see the |TOML documentation on Strings|https://toml.io/en/v1.1.0#string|.
|
This has nothing to do with en's markup syntax per se, it's just a consequence of how backslashes are also special in TOML syntax. For more details, see the |TOML documentation on Strings|https://toml.io/en/v1.1.0#string|.
|
||||||
|
|
||||||
## Interactions with HTML
|
## Interactions with HTML
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue