2022-11-26 23:57:50 +00:00
|
|
|
// Copyright 2020 The Jujutsu Authors
|
2020-12-12 08:00:42 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
use std::hash::Hash;
|
|
|
|
use std::io::Write;
|
2023-03-10 07:44:34 +00:00
|
|
|
use std::{cmp, io};
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2023-01-27 06:33:24 +00:00
|
|
|
use itertools::Itertools;
|
2023-01-16 13:20:42 +00:00
|
|
|
use jujutsu_lib::settings::UserSettings;
|
2023-01-27 06:33:24 +00:00
|
|
|
use renderdag::{Ancestor, GraphRowRenderer, Renderer};
|
2023-01-16 13:20:42 +00:00
|
|
|
|
2020-12-12 08:00:42 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
// An edge to another node in the graph
|
|
|
|
pub enum Edge<T> {
|
|
|
|
Present { target: T, direct: bool },
|
|
|
|
Missing,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Edge<T> {
|
|
|
|
pub fn missing() -> Self {
|
|
|
|
Edge::Missing
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn direct(id: T) -> Self {
|
|
|
|
Edge::Present {
|
|
|
|
target: id,
|
|
|
|
direct: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn indirect(id: T) -> Self {
|
|
|
|
Edge::Present {
|
|
|
|
target: id,
|
|
|
|
direct: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-16 13:20:42 +00:00
|
|
|
pub trait GraphLog<K: Clone + Eq + Hash> {
|
|
|
|
fn add_node(
|
|
|
|
&mut self,
|
|
|
|
id: &K,
|
|
|
|
edges: &[Edge<K>],
|
|
|
|
node_symbol: &str,
|
|
|
|
text: &str,
|
|
|
|
) -> io::Result<()>;
|
2023-03-10 07:44:34 +00:00
|
|
|
|
|
|
|
fn width(&self, id: &K, edges: &[Edge<K>]) -> usize;
|
2023-01-16 13:20:42 +00:00
|
|
|
}
|
|
|
|
|
2023-01-27 16:59:04 +00:00
|
|
|
pub struct SaplingGraphLog<'writer, R> {
|
2023-01-27 06:33:24 +00:00
|
|
|
renderer: R,
|
|
|
|
writer: &'writer mut dyn Write,
|
|
|
|
}
|
2023-01-16 13:20:42 +00:00
|
|
|
|
2023-01-27 06:33:24 +00:00
|
|
|
impl<K: Clone> From<&Edge<K>> for Ancestor<K> {
|
|
|
|
fn from(e: &Edge<K>) -> Self {
|
|
|
|
match e {
|
|
|
|
Edge::Present {
|
|
|
|
target,
|
|
|
|
direct: true,
|
|
|
|
} => Ancestor::Parent(target.clone()),
|
|
|
|
Edge::Present { target, .. } => Ancestor::Ancestor(target.clone()),
|
|
|
|
Edge::Missing => Ancestor::Anonymous,
|
2023-01-16 13:20:42 +00:00
|
|
|
}
|
|
|
|
}
|
2023-01-27 06:33:24 +00:00
|
|
|
}
|
2023-01-16 13:20:42 +00:00
|
|
|
|
2023-01-27 16:59:04 +00:00
|
|
|
impl<'writer, K, R> GraphLog<K> for SaplingGraphLog<'writer, R>
|
2023-01-27 06:33:24 +00:00
|
|
|
where
|
|
|
|
K: Clone + Eq + Hash,
|
|
|
|
R: Renderer<K, Output = String>,
|
|
|
|
{
|
|
|
|
fn add_node(
|
|
|
|
&mut self,
|
|
|
|
id: &K,
|
|
|
|
edges: &[Edge<K>],
|
|
|
|
node_symbol: &str,
|
|
|
|
text: &str,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
let row = self.renderer.next_row(
|
|
|
|
id.clone(),
|
|
|
|
edges.iter().map_into().collect(),
|
|
|
|
node_symbol.into(),
|
|
|
|
text.into(),
|
|
|
|
);
|
|
|
|
|
|
|
|
write!(self.writer, "{row}")
|
2023-01-16 13:20:42 +00:00
|
|
|
}
|
2023-03-10 07:44:34 +00:00
|
|
|
|
|
|
|
fn width(&self, id: &K, edges: &[Edge<K>]) -> usize {
|
|
|
|
let parents = edges.iter().map_into().collect();
|
|
|
|
let w: u64 = self.renderer.width(Some(id), Some(&parents));
|
|
|
|
w.try_into().unwrap()
|
|
|
|
}
|
2023-01-27 06:33:24 +00:00
|
|
|
}
|
2023-01-16 13:20:42 +00:00
|
|
|
|
2023-01-27 16:59:04 +00:00
|
|
|
impl<'writer, R> SaplingGraphLog<'writer, R> {
|
|
|
|
pub fn create<K>(
|
2023-01-27 06:33:24 +00:00
|
|
|
renderer: R,
|
|
|
|
formatter: &'writer mut dyn Write,
|
2023-01-27 16:59:04 +00:00
|
|
|
) -> Box<dyn GraphLog<K> + 'writer>
|
|
|
|
where
|
|
|
|
K: Clone + Eq + Hash + 'writer,
|
|
|
|
R: Renderer<K, Output = String> + 'writer,
|
|
|
|
{
|
2023-01-27 06:33:24 +00:00
|
|
|
Box::new(SaplingGraphLog {
|
|
|
|
renderer,
|
|
|
|
writer: formatter,
|
|
|
|
})
|
2023-01-16 13:20:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_graphlog<'a, K: Clone + Eq + Hash + 'a>(
|
2023-01-27 06:33:24 +00:00
|
|
|
settings: &UserSettings,
|
2023-01-16 13:20:42 +00:00
|
|
|
formatter: &'a mut dyn Write,
|
|
|
|
) -> Box<dyn GraphLog<K> + 'a> {
|
2023-01-27 06:33:24 +00:00
|
|
|
let builder = GraphRowRenderer::new().output().with_min_row_height(0);
|
2023-01-16 13:20:42 +00:00
|
|
|
|
2023-01-27 18:15:51 +00:00
|
|
|
match settings.graph_style().as_str() {
|
2023-01-27 06:33:24 +00:00
|
|
|
"curved" => SaplingGraphLog::create(builder.build_box_drawing(), formatter),
|
|
|
|
"square" => {
|
|
|
|
SaplingGraphLog::create(builder.build_box_drawing().with_square_glyphs(), formatter)
|
|
|
|
}
|
|
|
|
"ascii" => SaplingGraphLog::create(builder.build_ascii(), formatter),
|
|
|
|
"ascii-large" => SaplingGraphLog::create(builder.build_ascii_large(), formatter),
|
|
|
|
_ => Box::new(AsciiGraphDrawer::new(formatter)),
|
|
|
|
}
|
2023-01-16 13:20:42 +00:00
|
|
|
}
|
|
|
|
|
2020-12-12 08:00:42 +00:00
|
|
|
pub struct AsciiGraphDrawer<'writer, K> {
|
|
|
|
writer: &'writer mut dyn Write,
|
|
|
|
edges: Vec<Edge<K>>,
|
2023-01-16 09:08:48 +00:00
|
|
|
pending_text: Vec<String>,
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'writer, K> AsciiGraphDrawer<'writer, K>
|
|
|
|
where
|
|
|
|
K: Clone + Eq + Hash,
|
|
|
|
{
|
|
|
|
pub fn new(writer: &'writer mut dyn Write) -> Self {
|
|
|
|
Self {
|
|
|
|
writer,
|
|
|
|
edges: Default::default(),
|
|
|
|
pending_text: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
2023-01-16 13:20:42 +00:00
|
|
|
}
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2023-01-16 13:20:42 +00:00
|
|
|
impl<'writer, K: Clone + Eq + Hash> GraphLog<K> for AsciiGraphDrawer<'writer, K> {
|
|
|
|
fn add_node(
|
2021-04-07 05:27:34 +00:00
|
|
|
&mut self,
|
|
|
|
id: &K,
|
|
|
|
edges: &[Edge<K>],
|
2023-01-16 09:08:48 +00:00
|
|
|
node_symbol: &str,
|
|
|
|
text: &str,
|
2021-04-07 05:27:34 +00:00
|
|
|
) -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
assert!(self.pending_text.is_empty());
|
2023-01-16 09:08:48 +00:00
|
|
|
for line in text.split(|x| x == '\n') {
|
|
|
|
self.pending_text.push(line.into());
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2023-01-16 09:08:48 +00:00
|
|
|
if self.pending_text.last() == Some(&"".into()) {
|
2020-12-12 08:00:42 +00:00
|
|
|
self.pending_text.pop().unwrap();
|
|
|
|
}
|
|
|
|
self.pending_text.reverse();
|
|
|
|
|
|
|
|
// Check if an existing edge should be terminated by the new node. If there
|
|
|
|
// is, draw the new node in the same column. Otherwise, insert it at the right.
|
|
|
|
let edge_index = if let Some(edge_index) = self.index_by_target(id) {
|
|
|
|
// This edge terminates in the node we're adding
|
|
|
|
|
|
|
|
// If we're inserting a merge somewhere that's not the very right, the edges
|
|
|
|
// right of it will move further right, so we need to prepare by inserting rows
|
|
|
|
// of '\'.
|
|
|
|
if edges.len() > 2 && edge_index < self.edges.len() - 1 {
|
|
|
|
for i in 2..edges.len() {
|
|
|
|
for edge in self.edges.iter().take(edge_index + 1) {
|
2021-06-14 07:18:38 +00:00
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, edge)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
for _ in 0..i - 2 {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
for _ in edge_index + 1..self.edges.len() {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " \\")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2023-01-16 13:20:42 +00:00
|
|
|
writeln!(self.writer)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
self.edges.remove(edge_index);
|
|
|
|
edge_index
|
|
|
|
} else {
|
|
|
|
self.edges.len()
|
|
|
|
};
|
|
|
|
|
|
|
|
// Draw the edges to the left of the new node
|
|
|
|
for edge in self.edges.iter().take(edge_index) {
|
2021-06-14 07:18:38 +00:00
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, edge)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
// Draw the new node
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "{node_symbol}")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
// If it's a merge of many nodes, draw a vertical line to the right
|
|
|
|
for _ in 3..edges.len() {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "--")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
if edges.len() > 2 {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "-.")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
// Draw the edges to the right of the new node
|
|
|
|
for edge in self.edges.iter().skip(edge_index) {
|
2021-06-14 07:18:38 +00:00
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, edge)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
if edges.len() > 1 {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 05:27:34 +00:00
|
|
|
self.maybe_write_pending_text()?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
|
|
|
// Update the data model.
|
|
|
|
for (i, edge) in edges.iter().enumerate() {
|
|
|
|
self.edges.insert(edge_index + i, edge.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it's a merge commit, insert a row of '\'.
|
|
|
|
if edges.len() >= 2 {
|
|
|
|
for edge in self.edges.iter().take(edge_index) {
|
2021-06-14 07:18:38 +00:00
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, edge)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-04-07 05:27:34 +00:00
|
|
|
AsciiGraphDrawer::straight_edge_no_space(&mut self.writer, &self.edges[edge_index])?;
|
2020-12-12 08:00:42 +00:00
|
|
|
for _ in edge_index + 1..self.edges.len() {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "\\ ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2021-04-07 05:27:34 +00:00
|
|
|
self.maybe_write_pending_text()?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2022-10-09 18:52:42 +00:00
|
|
|
let pad_to_index = self.edges.len() + usize::from(edges.is_empty());
|
2020-12-12 08:00:42 +00:00
|
|
|
// Close any edges to missing nodes.
|
|
|
|
for (i, edge) in edges.iter().enumerate().rev() {
|
|
|
|
if *edge == Edge::Missing {
|
2021-04-24 02:09:50 +00:00
|
|
|
self.close_missing_edge(edge_index + i, pad_to_index)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-24 02:09:50 +00:00
|
|
|
// If this was the last node in a chain, add a "/" for any edges to the right of
|
|
|
|
// it.
|
|
|
|
if edges.is_empty() && edge_index < self.edges.len() {
|
|
|
|
self.close_edge(edge_index, pad_to_index)?;
|
|
|
|
}
|
2020-12-12 08:00:42 +00:00
|
|
|
|
|
|
|
// Merge new edges that share the same target.
|
|
|
|
let mut source_index = 1;
|
|
|
|
while source_index < self.edges.len() {
|
|
|
|
if let Edge::Present { target, .. } = &self.edges[source_index] {
|
|
|
|
if let Some(target_index) = self.index_by_target(target) {
|
|
|
|
// There already is an edge leading to the same target node. Mark that we
|
|
|
|
// want to merge the higher index into the lower index.
|
|
|
|
if source_index > target_index {
|
2021-04-07 05:27:34 +00:00
|
|
|
self.merge_edges(source_index, target_index, pad_to_index)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
// Don't increment source_index.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
source_index += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emit any remaining lines of text.
|
|
|
|
while !self.pending_text.is_empty() {
|
2022-05-03 03:45:02 +00:00
|
|
|
for edge in &self.edges {
|
2021-06-14 07:18:38 +00:00
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, edge)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-04-24 02:09:50 +00:00
|
|
|
for _ in self.edges.len()..pad_to_index {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2021-04-24 02:09:50 +00:00
|
|
|
}
|
2021-04-07 05:27:34 +00:00
|
|
|
self.maybe_write_pending_text()?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2023-03-10 07:44:34 +00:00
|
|
|
|
|
|
|
fn width(&self, id: &K, edges: &[Edge<K>]) -> usize {
|
|
|
|
let orig = self.edges.len() - usize::from(self.index_by_target(id).is_some());
|
|
|
|
let added = cmp::max(edges.len(), 1);
|
|
|
|
2 * (orig + added)
|
|
|
|
}
|
2023-01-16 13:20:42 +00:00
|
|
|
}
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2023-01-16 13:20:42 +00:00
|
|
|
impl<'writer, K: Clone + Eq + Hash> AsciiGraphDrawer<'writer, K> {
|
2020-12-12 08:00:42 +00:00
|
|
|
fn index_by_target(&self, id: &K) -> Option<usize> {
|
|
|
|
for (i, edge) in self.edges.iter().enumerate() {
|
|
|
|
match edge {
|
|
|
|
Edge::Present { target, .. } if target == id => return Some(i),
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Not an instance method so the caller doesn't need mutable access to the
|
|
|
|
/// whole struct.
|
2021-04-07 05:27:34 +00:00
|
|
|
fn straight_edge(writer: &mut dyn Write, edge: &Edge<K>) -> io::Result<()> {
|
|
|
|
AsciiGraphDrawer::straight_edge_no_space(writer, edge)?;
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(writer, " ")
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Not an instance method so the caller doesn't need mutable access to the
|
|
|
|
/// whole struct.
|
2021-04-07 05:27:34 +00:00
|
|
|
fn straight_edge_no_space(writer: &mut dyn Write, edge: &Edge<K>) -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
match edge {
|
|
|
|
Edge::Present { direct: true, .. } => {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(writer, "|")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
Edge::Present { direct: false, .. } => {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(writer, ":")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
Edge::Missing => {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(writer, "|")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-07 05:27:34 +00:00
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 05:27:34 +00:00
|
|
|
fn merge_edges(&mut self, source: usize, target: usize, pad_to_index: usize) -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
assert!(target < source);
|
|
|
|
self.edges.remove(source);
|
|
|
|
for i in 0..target {
|
2021-04-07 05:27:34 +00:00
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[i])?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
if source == target + 1 {
|
|
|
|
// If we're merging exactly one step to the left, draw a '/' to join the lines.
|
2021-04-07 05:27:34 +00:00
|
|
|
AsciiGraphDrawer::straight_edge_no_space(&mut self.writer, &self.edges[target])?;
|
2020-12-12 08:00:42 +00:00
|
|
|
for _ in source..self.edges.len() + 1 {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "/ ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
for _ in self.edges.len() + 1..pad_to_index {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// If we're merging more than one step to the left, we need two rows:
|
|
|
|
// | |_|_|/
|
|
|
|
// |/| | |
|
2021-04-07 05:27:34 +00:00
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[target])?;
|
2020-12-12 08:00:42 +00:00
|
|
|
for i in target + 1..source - 1 {
|
2021-04-07 05:27:34 +00:00
|
|
|
AsciiGraphDrawer::straight_edge_no_space(&mut self.writer, &self.edges[i])?;
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "_")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-04-07 05:27:34 +00:00
|
|
|
AsciiGraphDrawer::straight_edge_no_space(&mut self.writer, &self.edges[source - 1])?;
|
2020-12-12 08:00:42 +00:00
|
|
|
for _ in source..self.edges.len() + 1 {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "/ ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
for _ in self.edges.len() + 1..pad_to_index {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-04-07 05:27:34 +00:00
|
|
|
self.maybe_write_pending_text()?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
|
|
|
for i in 0..target {
|
2021-04-07 05:27:34 +00:00
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[i])?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-04-07 05:27:34 +00:00
|
|
|
AsciiGraphDrawer::straight_edge_no_space(&mut self.writer, &self.edges[target])?;
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "/")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
for i in target + 1..self.edges.len() {
|
2021-04-07 05:27:34 +00:00
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[i])?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
for _ in self.edges.len()..pad_to_index {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
2021-04-28 15:57:30 +00:00
|
|
|
self.maybe_write_pending_text()?;
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 02:09:50 +00:00
|
|
|
fn close_missing_edge(&mut self, source: usize, pad_to_index: usize) -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
self.edges.remove(source);
|
|
|
|
for i in 0..source {
|
2021-04-07 05:27:34 +00:00
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[i])?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "~")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
for _ in source..self.edges.len() {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "/ ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
for _ in self.edges.len() + 1..pad_to_index {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2021-04-07 05:27:34 +00:00
|
|
|
self.maybe_write_pending_text()
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 02:09:50 +00:00
|
|
|
fn close_edge(&mut self, source: usize, pad_to_index: usize) -> io::Result<()> {
|
|
|
|
for i in 0..source {
|
|
|
|
AsciiGraphDrawer::straight_edge(&mut self.writer, &self.edges[i])?;
|
|
|
|
}
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2021-04-24 02:09:50 +00:00
|
|
|
for _ in source..self.edges.len() {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "/ ")?;
|
2021-04-24 02:09:50 +00:00
|
|
|
}
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2021-04-24 02:09:50 +00:00
|
|
|
for _ in self.edges.len() + 1..pad_to_index {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, " ")?;
|
2021-04-24 02:09:50 +00:00
|
|
|
}
|
|
|
|
self.maybe_write_pending_text()
|
|
|
|
}
|
|
|
|
|
2021-04-07 05:27:34 +00:00
|
|
|
fn maybe_write_pending_text(&mut self) -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
if let Some(text) = self.pending_text.pop() {
|
2023-01-16 09:08:48 +00:00
|
|
|
write!(self.writer, "{text}")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
2023-01-16 13:20:42 +00:00
|
|
|
writeln!(self.writer)
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-03-14 17:37:28 +00:00
|
|
|
use super::*;
|
|
|
|
|
2023-03-10 07:44:34 +00:00
|
|
|
/// Adapter to insert width calculation for each `.add_node()`
|
|
|
|
struct AsciiGraphDrawerWithWidths<'writer, K> {
|
|
|
|
inner: AsciiGraphDrawer<'writer, K>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'writer, K: Clone + Eq + Hash> AsciiGraphDrawerWithWidths<'writer, K> {
|
|
|
|
fn new(writer: &'writer mut dyn Write) -> Self {
|
|
|
|
AsciiGraphDrawerWithWidths {
|
|
|
|
inner: AsciiGraphDrawer::new(writer),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_node(
|
|
|
|
&mut self,
|
|
|
|
id: &K,
|
|
|
|
edges: &[Edge<K>],
|
|
|
|
node_symbol: &str,
|
|
|
|
text: &str,
|
|
|
|
) -> io::Result<()> {
|
|
|
|
let width = self.inner.width(id, edges);
|
|
|
|
let text = format!("{text} [width={width}]");
|
|
|
|
self.inner.add_node(id, edges, node_symbol, &text)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-12 08:00:42 +00:00
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn single_node() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&1, &[], "@", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
@ node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn long_description() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
|
|
|
let mut graph = AsciiGraphDrawer::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&2, &[Edge::direct(1)], "@", "many\nlines\nof\ntext\n")?;
|
|
|
|
graph.add_node(&1, &[], "o", "single line")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
|
|
|
@ many
|
|
|
|
| lines
|
|
|
|
| of
|
|
|
|
| text
|
|
|
|
o single line
|
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn long_description_blank_lines() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
|
|
|
let mut graph = AsciiGraphDrawer::new(&mut buffer);
|
|
|
|
graph.add_node(
|
|
|
|
&2,
|
|
|
|
&[Edge::direct(1)],
|
2023-01-16 09:08:48 +00:00
|
|
|
"@",
|
|
|
|
"\n\nmany\n\nlines\n\nof\n\ntext\n\n\n",
|
2021-04-07 05:27:34 +00:00
|
|
|
)?;
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&1, &[], "o", "single line")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
|
|
|
// A final newline is ignored but all other newlines are respected.
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
|
|
|
@
|
|
|
|
|
|
|
|
|
| many
|
|
|
|
|
|
|
|
|
| lines
|
|
|
|
|
|
|
|
|
| of
|
|
|
|
|
|
|
|
|
| text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
o single line
|
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn chain() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&3, &[Edge::direct(2)], "@", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
@ node 3 [width=2]
|
|
|
|
o node 2 [width=2]
|
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn interleaved_chains() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&7, &[Edge::direct(5)], "o", "node 7")?;
|
|
|
|
graph.add_node(&6, &[Edge::direct(4)], "o", "node 6")?;
|
|
|
|
graph.add_node(&5, &[Edge::direct(3)], "o", "node 5")?;
|
|
|
|
graph.add_node(&4, &[Edge::direct(2)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "@", "node 3")?;
|
|
|
|
graph.add_node(&2, &[], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 7 [width=2]
|
|
|
|
| o node 6 [width=4]
|
|
|
|
o | node 5 [width=4]
|
|
|
|
| o node 4 [width=4]
|
|
|
|
@ | node 3 [width=4]
|
|
|
|
| o node 2 [width=4]
|
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn independent_nodes() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&3, &[Edge::missing()], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::missing()], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[Edge::missing()], "@", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 3 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
~
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 2 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
~
|
2023-03-10 07:44:34 +00:00
|
|
|
@ node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
~
|
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn left_chain_ends() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&4, &[Edge::direct(2)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::missing()], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 4 [width=2]
|
|
|
|
| o node 3 [width=4]
|
|
|
|
o | node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
~/
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 02:09:50 +00:00
|
|
|
#[test]
|
|
|
|
fn left_chain_ends_with_no_missing_edge() -> io::Result<()> {
|
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&4, &[Edge::direct(2)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[], "o", "node 2\nmore\ntext")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2021-04-24 02:09:50 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 4 [width=2]
|
|
|
|
| o node 3 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
o | node 2
|
2022-05-10 04:45:01 +00:00
|
|
|
/ more
|
2023-03-10 07:44:34 +00:00
|
|
|
| text [width=4]
|
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-24 02:09:50 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn right_chain_ends() -> io::Result<()> {
|
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(2)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::missing()], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1\nmore\ntext")?;
|
2021-04-24 02:09:50 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 4 [width=2]
|
|
|
|
| o node 3 [width=4]
|
|
|
|
| o node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
| ~
|
|
|
|
o node 1
|
2022-05-10 04:03:59 +00:00
|
|
|
more
|
2023-03-10 07:44:34 +00:00
|
|
|
text [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-24 02:09:50 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-04-24 02:09:50 +00:00
|
|
|
#[test]
|
|
|
|
fn right_chain_ends_long_description() -> io::Result<()> {
|
|
|
|
let mut buffer = vec![];
|
|
|
|
let mut graph = AsciiGraphDrawer::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
2021-04-24 02:09:50 +00:00
|
|
|
graph.add_node(
|
|
|
|
&2,
|
|
|
|
&[Edge::missing()],
|
2023-01-16 09:08:48 +00:00
|
|
|
"o",
|
|
|
|
"node 2\nwith\nlong\ndescription",
|
2021-04-24 02:09:50 +00:00
|
|
|
)?;
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2021-04-24 02:09:50 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
|
|
|
o node 3
|
|
|
|
| o node 2
|
|
|
|
| ~ with
|
|
|
|
| long
|
|
|
|
| description
|
|
|
|
o node 1
|
|
|
|
"###);
|
2021-04-24 02:09:50 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-12-12 08:00:42 +00:00
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn fork_multiple() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&4, &[Edge::direct(1)], "@", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
@ node 4 [width=2]
|
|
|
|
| o node 3 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/
|
2023-03-10 07:44:34 +00:00
|
|
|
| o node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn fork_multiple_chains() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&10, &[Edge::direct(7)], "o", "node 10")?;
|
|
|
|
graph.add_node(&9, &[Edge::direct(6)], "o", "node 9")?;
|
|
|
|
graph.add_node(&8, &[Edge::direct(5)], "o", "node 8")?;
|
|
|
|
graph.add_node(&7, &[Edge::direct(4)], "o", "node 7")?;
|
|
|
|
graph.add_node(&6, &[Edge::direct(3)], "o", "node 6")?;
|
|
|
|
graph.add_node(&5, &[Edge::direct(2)], "o", "node 5")?;
|
|
|
|
graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 10 [width=2]
|
|
|
|
| o node 9 [width=4]
|
|
|
|
| | o node 8 [width=6]
|
|
|
|
o | | node 7 [width=6]
|
|
|
|
| o | node 6 [width=6]
|
|
|
|
| | o node 5 [width=6]
|
|
|
|
o | | node 4 [width=6]
|
|
|
|
| o | node 3 [width=6]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/ /
|
2023-03-10 07:44:34 +00:00
|
|
|
| o node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn cross_over() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&5, &[Edge::direct(1)], "o", "node 5")?;
|
|
|
|
graph.add_node(&4, &[Edge::direct(2)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 5 [width=2]
|
|
|
|
| o node 4 [width=4]
|
|
|
|
| | o node 3 [width=6]
|
2022-05-10 04:24:58 +00:00
|
|
|
| |/
|
|
|
|
|/|
|
2023-03-10 07:44:34 +00:00
|
|
|
| o node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn cross_over_multiple() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&7, &[Edge::direct(1)], "o", "node 7")?;
|
|
|
|
graph.add_node(&6, &[Edge::direct(3)], "o", "node 6")?;
|
|
|
|
graph.add_node(&5, &[Edge::direct(2)], "o", "node 5")?;
|
|
|
|
graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 7 [width=2]
|
|
|
|
| o node 6 [width=4]
|
|
|
|
| | o node 5 [width=6]
|
|
|
|
| | | o node 4 [width=8]
|
2022-05-10 04:24:58 +00:00
|
|
|
| |_|/
|
|
|
|
|/| |
|
2023-03-10 07:44:34 +00:00
|
|
|
| o | node 3 [width=6]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/ /
|
2023-03-10 07:44:34 +00:00
|
|
|
| o node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn cross_over_new_on_left() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&6, &[Edge::direct(3)], "o", "node 6")?;
|
|
|
|
graph.add_node(&5, &[Edge::direct(2)], "o", "node 5")?;
|
|
|
|
graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 6 [width=2]
|
|
|
|
| o node 5 [width=4]
|
|
|
|
| | o node 4 [width=6]
|
|
|
|
o | | node 3 [width=6]
|
2022-05-10 04:24:58 +00:00
|
|
|
| |/
|
|
|
|
|/|
|
2023-03-10 07:44:34 +00:00
|
|
|
| o node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn merge_multiple() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2020-12-12 08:00:42 +00:00
|
|
|
graph.add_node(
|
|
|
|
&5,
|
|
|
|
&[
|
|
|
|
Edge::direct(1),
|
|
|
|
Edge::direct(2),
|
|
|
|
Edge::direct(3),
|
|
|
|
Edge::direct(4),
|
|
|
|
],
|
2023-01-16 09:08:48 +00:00
|
|
|
"@",
|
|
|
|
"node 5\nmore\ntext",
|
2021-04-07 05:27:34 +00:00
|
|
|
)?;
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&4, &[Edge::missing()], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::missing()], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::missing()], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[Edge::missing()], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
|
|
|
@---. node 5
|
|
|
|
|\ \ \ more
|
2023-03-10 07:44:34 +00:00
|
|
|
| | | | text [width=8]
|
|
|
|
| | | o node 4 [width=8]
|
2022-05-10 04:24:58 +00:00
|
|
|
| | | ~
|
2023-03-10 07:44:34 +00:00
|
|
|
| | o node 3 [width=6]
|
2022-05-10 04:24:58 +00:00
|
|
|
| | ~
|
2023-03-10 07:44:34 +00:00
|
|
|
| o node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
| ~
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
~
|
|
|
|
"###);
|
|
|
|
|
2021-04-07 05:27:34 +00:00
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn fork_merge_in_central_edge() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&8, &[Edge::direct(1)], "o", "node 8")?;
|
|
|
|
graph.add_node(&7, &[Edge::direct(5)], "o", "node 7")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
graph.add_node(
|
|
|
|
&6,
|
|
|
|
&[Edge::direct(2)],
|
2023-01-16 09:08:48 +00:00
|
|
|
"o",
|
|
|
|
"node 6\nwith\nsome\nmore\nlines",
|
2021-04-07 05:27:34 +00:00
|
|
|
)?;
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&5, &[Edge::direct(4), Edge::direct(3)], "o", "node 5")?;
|
|
|
|
graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 8 [width=2]
|
|
|
|
| o node 7 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
| | o node 6
|
|
|
|
| | | with
|
|
|
|
| | | some
|
|
|
|
| | | more
|
2023-03-10 07:44:34 +00:00
|
|
|
| | | lines [width=6]
|
|
|
|
| o | node 5 [width=8]
|
2022-05-10 04:24:58 +00:00
|
|
|
| |\ \
|
2023-03-10 07:44:34 +00:00
|
|
|
| o | | node 4 [width=8]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/ / /
|
2023-03-10 07:44:34 +00:00
|
|
|
| o | node 3 [width=6]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/ /
|
2023-03-10 07:44:34 +00:00
|
|
|
| o node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn fork_merge_multiple() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&6, &[Edge::direct(5)], "o", "node 6")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
graph.add_node(
|
|
|
|
&5,
|
|
|
|
&[Edge::direct(2), Edge::direct(3), Edge::direct(4)],
|
2023-01-16 09:08:48 +00:00
|
|
|
"o",
|
|
|
|
"node 5",
|
2021-04-07 05:27:34 +00:00
|
|
|
)?;
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 6 [width=2]
|
|
|
|
o-. node 5 [width=6]
|
2022-05-10 04:24:58 +00:00
|
|
|
|\ \
|
2023-03-10 07:44:34 +00:00
|
|
|
| | o node 4 [width=6]
|
|
|
|
| o | node 3 [width=6]
|
2022-05-10 04:24:58 +00:00
|
|
|
| |/
|
2023-03-10 07:44:34 +00:00
|
|
|
o | node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn fork_merge_multiple_in_central_edge() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&10, &[Edge::direct(1)], "o", "node 10")?;
|
|
|
|
graph.add_node(&9, &[Edge::direct(7)], "o", "node 9")?;
|
|
|
|
graph.add_node(&8, &[Edge::direct(2)], "o", "node 8")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
graph.add_node(
|
|
|
|
&7,
|
|
|
|
&[
|
|
|
|
Edge::direct(6),
|
|
|
|
Edge::direct(5),
|
|
|
|
Edge::direct(4),
|
|
|
|
Edge::direct(3),
|
|
|
|
],
|
2023-01-16 09:08:48 +00:00
|
|
|
"o",
|
|
|
|
"node 7",
|
2021-04-07 05:27:34 +00:00
|
|
|
)?;
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&6, &[Edge::direct(1)], "o", "node 6")?;
|
|
|
|
graph.add_node(&5, &[Edge::direct(1)], "o", "node 5")?;
|
|
|
|
graph.add_node(&4, &[Edge::direct(1)], "o", "node 4")?;
|
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
|
|
|
graph.add_node(&2, &[Edge::direct(1)], "o", "node 2")?;
|
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 10 [width=2]
|
|
|
|
| o node 9 [width=4]
|
|
|
|
| | o node 8 [width=6]
|
2022-05-10 04:24:58 +00:00
|
|
|
| | \
|
|
|
|
| | \
|
2023-03-10 07:44:34 +00:00
|
|
|
| o---. | node 7 [width=12]
|
2022-05-10 04:24:58 +00:00
|
|
|
| |\ \ \ \
|
2023-03-10 07:44:34 +00:00
|
|
|
| o | | | | node 6 [width=12]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/ / / / /
|
2023-03-10 07:44:34 +00:00
|
|
|
| o | | | node 5 [width=10]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/ / / /
|
2023-03-10 07:44:34 +00:00
|
|
|
| o | | node 4 [width=8]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/ / /
|
2023-03-10 07:44:34 +00:00
|
|
|
| o | node 3 [width=6]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/ /
|
2023-03-10 07:44:34 +00:00
|
|
|
| o node 2 [width=4]
|
2022-05-10 04:24:58 +00:00
|
|
|
|/
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn merge_multiple_missing_edges() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2020-12-12 08:00:42 +00:00
|
|
|
graph.add_node(
|
|
|
|
&1,
|
|
|
|
&[
|
|
|
|
Edge::missing(),
|
|
|
|
Edge::missing(),
|
|
|
|
Edge::missing(),
|
|
|
|
Edge::missing(),
|
|
|
|
],
|
2023-01-16 09:08:48 +00:00
|
|
|
"@",
|
|
|
|
"node 1\nwith\nmany\nlines\nof\ntext",
|
2021-04-07 05:27:34 +00:00
|
|
|
)?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
|
|
|
@---. node 1
|
|
|
|
|\ \ \ with
|
|
|
|
| | | ~ many
|
|
|
|
| | ~ lines
|
|
|
|
| ~ of
|
2023-03-10 07:44:34 +00:00
|
|
|
~ text [width=8]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-04-07 05:27:34 +00:00
|
|
|
fn merge_missing_edges_and_fork() -> io::Result<()> {
|
2020-12-12 08:00:42 +00:00
|
|
|
let mut buffer = vec![];
|
2023-03-10 07:44:34 +00:00
|
|
|
let mut graph = AsciiGraphDrawerWithWidths::new(&mut buffer);
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&3, &[Edge::direct(1)], "o", "node 3")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
graph.add_node(
|
|
|
|
&2,
|
|
|
|
&[
|
|
|
|
Edge::missing(),
|
|
|
|
Edge::indirect(1),
|
|
|
|
Edge::missing(),
|
|
|
|
Edge::indirect(1),
|
|
|
|
],
|
2023-01-16 09:08:48 +00:00
|
|
|
"o",
|
|
|
|
"node 2\nwith\nmany\nlines\nof\ntext",
|
2021-04-07 05:27:34 +00:00
|
|
|
)?;
|
2023-01-16 09:08:48 +00:00
|
|
|
graph.add_node(&1, &[], "o", "node 1")?;
|
2020-12-12 08:00:42 +00:00
|
|
|
|
2022-05-10 04:24:58 +00:00
|
|
|
insta::assert_snapshot!(String::from_utf8_lossy(&buffer), @r###"
|
2023-03-10 07:44:34 +00:00
|
|
|
o node 3 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
| o---. node 2
|
|
|
|
| |\ \ \ with
|
|
|
|
| | : ~/ many
|
|
|
|
| ~/ / lines
|
|
|
|
|/ / of
|
2023-03-10 07:44:34 +00:00
|
|
|
|/ text [width=10]
|
|
|
|
o node 1 [width=2]
|
2022-05-10 04:24:58 +00:00
|
|
|
"###);
|
2021-04-07 05:27:34 +00:00
|
|
|
|
|
|
|
Ok(())
|
2020-12-12 08:00:42 +00:00
|
|
|
}
|
|
|
|
}
|