1use std::collections::BTreeMap;
9use std::ops::Range;
10
11use bitflags::bitflags;
12#[cfg(feature = "serialize")]
13use serde::Serialize;
14
15use super::column::Column;
16use super::column::ColumnsExt;
17use super::output::OutputRendererBuilder;
18
19pub trait Renderer<N> {
20 type Output;
21
22 fn width(&self, new_node: Option<&N>, new_parents: Option<&Vec<Ancestor<N>>>) -> u64;
24
25 fn reserve(&mut self, node: N);
27
28 fn next_row(
30 &mut self,
31 node: N,
32 parents: Vec<Ancestor<N>>,
33 glyph: String,
34 message: String,
35 ) -> Self::Output;
36}
37
38pub struct GraphRowRenderer<N> {
42 columns: Vec<Column<N>>,
43}
44
45pub enum Ancestor<N> {
47 Ancestor(N),
49
50 Parent(N),
52
53 Anonymous,
55}
56
57impl<N> Ancestor<N> {
58 fn to_column(&self) -> Column<N>
59 where
60 N: Clone,
61 {
62 match self {
63 Ancestor::Ancestor(n) => Column::Ancestor(n.clone()),
64 Ancestor::Parent(n) => Column::Parent(n.clone()),
65 Ancestor::Anonymous => Column::Blocked,
66 }
67 }
68
69 fn id(&self) -> Option<&N> {
70 match self {
71 Ancestor::Ancestor(n) => Some(n),
72 Ancestor::Parent(n) => Some(n),
73 Ancestor::Anonymous => None,
74 }
75 }
76
77 fn is_direct(&self) -> bool {
78 match self {
79 Ancestor::Ancestor(_) => false,
80 Ancestor::Parent(_) => true,
81 Ancestor::Anonymous => true,
82 }
83 }
84
85 fn to_link_line(&self, direct: LinkLine, indirect: LinkLine) -> LinkLine {
86 if self.is_direct() { direct } else { indirect }
87 }
88}
89
90struct AncestorColumnBounds {
91 target: usize,
92 min_ancestor: usize,
93 min_parent: usize,
94 max_parent: usize,
95 max_ancestor: usize,
96}
97
98impl AncestorColumnBounds {
99 fn new<N>(columns: &BTreeMap<usize, &Ancestor<N>>, target: usize) -> Option<Self> {
100 if columns.is_empty() {
101 return None;
102 }
103 let min_ancestor = columns
104 .iter()
105 .next()
106 .map_or(target, |(index, _)| *index)
107 .min(target);
108 let max_ancestor = columns
109 .iter()
110 .next_back()
111 .map_or(target, |(index, _)| *index)
112 .max(target);
113 let min_parent = columns
114 .iter()
115 .find(|(_, ancestor)| ancestor.is_direct())
116 .map_or(target, |(index, _)| *index)
117 .min(target);
118 let max_parent = columns
119 .iter()
120 .rev()
121 .find(|(_, ancestor)| ancestor.is_direct())
122 .map_or(target, |(index, _)| *index)
123 .max(target);
124 Some(Self {
125 target,
126 min_ancestor,
127 min_parent,
128 max_parent,
129 max_ancestor,
130 })
131 }
132
133 fn range(&self) -> Range<usize> {
134 if self.min_ancestor < self.max_ancestor {
135 self.min_ancestor + 1..self.max_ancestor
136 } else {
137 Default::default()
138 }
139 }
140
141 fn horizontal_line(&self, index: usize) -> LinkLine {
142 if index == self.target {
143 LinkLine::empty()
144 } else if index > self.min_parent && index < self.max_parent {
145 LinkLine::HORIZ_PARENT
146 } else if index > self.min_ancestor && index < self.max_ancestor {
147 LinkLine::HORIZ_ANCESTOR
148 } else {
149 LinkLine::empty()
150 }
151 }
152}
153
154impl<N> Column<N> {
155 fn to_node_line(&self) -> NodeLine {
156 match self {
157 Column::Ancestor(_) => NodeLine::Ancestor,
158 Column::Parent(_) => NodeLine::Parent,
159 _ => NodeLine::Blank,
160 }
161 }
162
163 fn to_link_line(&self) -> LinkLine {
164 match self {
165 Column::Ancestor(_) => LinkLine::VERT_ANCESTOR,
166 Column::Parent(_) => LinkLine::VERT_PARENT,
167 _ => LinkLine::empty(),
168 }
169 }
170
171 fn to_pad_line(&self) -> PadLine {
172 match self {
173 Column::Ancestor(_) => PadLine::Ancestor,
174 Column::Parent(_) => PadLine::Parent,
175 _ => PadLine::Blank,
176 }
177 }
178}
179
180#[derive(Debug, Copy, Clone, PartialEq, Eq)]
182#[cfg_attr(feature = "serialize", derive(Serialize))]
183pub enum NodeLine {
184 Blank,
186
187 Ancestor,
189
190 Parent,
192
193 Node,
195}
196
197#[derive(Debug, Copy, Clone, PartialEq, Eq)]
199#[cfg_attr(feature = "serialize", derive(Serialize))]
200pub enum PadLine {
201 Blank,
203
204 Ancestor,
206
207 Parent,
209}
210
211bitflags! {
212 #[cfg_attr(feature = "serialize", derive(Serialize))]
214 #[derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
215 pub struct LinkLine: u16 {
216 const HORIZ_PARENT = 0b0_0000_0000_0001;
218
219 const HORIZ_ANCESTOR = 0b0_0000_0000_0010;
221
222 const VERT_PARENT = 0b0_0000_0000_0100;
224
225 const VERT_ANCESTOR = 0b0_0000_0000_1000;
227
228 const LEFT_FORK_PARENT = 0b0_0000_0001_0000;
231
232 const LEFT_FORK_ANCESTOR = 0b0_0000_0010_0000;
235
236 const RIGHT_FORK_PARENT = 0b0_0000_0100_0000;
239
240 const RIGHT_FORK_ANCESTOR = 0b0_0000_1000_0000;
243
244 const LEFT_MERGE_PARENT = 0b0_0001_0000_0000;
246
247 const LEFT_MERGE_ANCESTOR = 0b0_0010_0000_0000;
249
250 const RIGHT_MERGE_PARENT = 0b0_0100_0000_0000;
252
253 const RIGHT_MERGE_ANCESTOR = 0b0_1000_0000_0000;
255
256 const CHILD = 0b1_0000_0000_0000;
260
261 const HORIZONTAL = Self::HORIZ_PARENT.bits() | Self::HORIZ_ANCESTOR.bits();
262 const VERTICAL = Self::VERT_PARENT.bits() | Self::VERT_ANCESTOR.bits();
263 const LEFT_FORK = Self::LEFT_FORK_PARENT.bits() | Self::LEFT_FORK_ANCESTOR.bits();
264 const RIGHT_FORK = Self::RIGHT_FORK_PARENT.bits() | Self::RIGHT_FORK_ANCESTOR.bits();
265 const LEFT_MERGE = Self::LEFT_MERGE_PARENT.bits() | Self::LEFT_MERGE_ANCESTOR.bits();
266 const RIGHT_MERGE = Self::RIGHT_MERGE_PARENT.bits() | Self::RIGHT_MERGE_ANCESTOR.bits();
267 const ANY_MERGE = Self::LEFT_MERGE.bits() | Self::RIGHT_MERGE.bits();
268 const ANY_FORK = Self::LEFT_FORK.bits() | Self::RIGHT_FORK.bits();
269 const ANY_FORK_OR_MERGE = Self::ANY_MERGE.bits() | Self::ANY_FORK.bits();
270 }
271}
272
273#[derive(Debug)]
275#[cfg_attr(feature = "serialize", derive(Serialize))]
276pub struct GraphRow<N> {
277 pub node: N,
279
280 pub glyph: String,
282
283 pub message: String,
285
286 pub merge: bool,
288
289 pub node_line: Vec<NodeLine>,
291
292 pub link_line: Option<Vec<LinkLine>>,
294
295 pub term_line: Option<Vec<bool>>,
298
299 pub pad_lines: Vec<PadLine>,
301}
302
303impl<N> GraphRowRenderer<N>
304where
305 N: Clone + Eq,
306{
307 pub fn new() -> Self {
309 GraphRowRenderer {
310 columns: Vec::new(),
311 }
312 }
313
314 pub fn output(self) -> OutputRendererBuilder<N, Self> {
316 OutputRendererBuilder::new(self)
317 }
318}
319
320impl<N> Renderer<N> for GraphRowRenderer<N>
321where
322 N: Clone + Eq,
323{
324 type Output = GraphRow<N>;
325
326 fn width(&self, node: Option<&N>, parents: Option<&Vec<Ancestor<N>>>) -> u64 {
327 let mut width = self.columns.len();
328 let mut empty_columns = self
329 .columns
330 .iter()
331 .filter(|&column| column == &Column::Empty)
332 .count();
333 if let Some(node) = node {
334 if self.columns.find(node).is_none() {
338 if empty_columns == 0 {
339 width += 1;
340 } else {
341 empty_columns = empty_columns.saturating_sub(1);
342 }
343 }
344 }
345 if let Some(parents) = parents {
346 let unallocated_parents = parents
350 .iter()
351 .filter(|parent| {
352 parent
353 .id()
354 .map_or(true, |parent| self.columns.find(parent).is_none())
355 })
356 .count()
357 .saturating_sub(empty_columns);
358 width += unallocated_parents.saturating_sub(1);
359 }
360 width as u64
361 }
362
363 fn reserve(&mut self, node: N) {
364 if self.columns.find(&node).is_none() {
365 if let Some(index) = self.columns.first_empty() {
366 self.columns[index] = Column::Reserved(node);
367 } else {
368 self.columns.push(Column::Reserved(node));
369 }
370 }
371 }
372
373 fn next_row(
374 &mut self,
375 node: N,
376 parents: Vec<Ancestor<N>>,
377 glyph: String,
378 message: String,
379 ) -> GraphRow<N> {
380 let column = self.columns.find(&node).unwrap_or_else(|| {
382 self.columns
383 .first_empty()
384 .unwrap_or_else(|| self.columns.new_empty())
385 });
386 self.columns[column] = Column::Empty;
387
388 let merge = parents.len() > 1;
390
391 let mut node_line: Vec<_> = self.columns.iter().map(|c| c.to_node_line()).collect();
393 node_line[column] = NodeLine::Node;
394
395 let mut link_line: Vec<_> = self.columns.iter().map(|c| c.to_link_line()).collect();
397 let mut need_link_line = false;
398
399 let mut term_line: Vec<_> = self.columns.iter().map(|_| false).collect();
401 let mut need_term_line = false;
402
403 let mut pad_lines: Vec<_> = self.columns.iter().map(|c| c.to_pad_line()).collect();
405
406 let mut parent_columns = BTreeMap::new();
408 for p in parents.iter() {
409 if let Some(parent_id) = p.id() {
411 if let Some(index) = self.columns.find(parent_id) {
412 self.columns[index].merge(&p.to_column());
413 parent_columns.insert(index, p);
414 continue;
415 }
416 }
417 if let Some(index) = self.columns.find_empty(column) {
420 self.columns[index].merge(&p.to_column());
421 parent_columns.insert(index, p);
422 continue;
423 }
424 parent_columns.insert(self.columns.len(), p);
426 node_line.push(NodeLine::Blank);
427 pad_lines.push(PadLine::Blank);
428 link_line.push(LinkLine::default());
429 term_line.push(false);
430 self.columns.push(p.to_column());
431 }
432
433 for (i, p) in parent_columns.iter() {
435 if p.id().is_none() {
436 term_line[*i] = true;
437 need_term_line = true;
438 }
439 }
440
441 if parents.len() == 1 {
443 if let Some((&parent_column, _)) = parent_columns.iter().next() {
444 if parent_column > column {
445 self.columns.swap(column, parent_column);
449 let parent = parent_columns
450 .remove(&parent_column)
451 .expect("parent should exist");
452 parent_columns.insert(column, parent);
453
454 let was_direct = link_line[parent_column].contains(LinkLine::VERT_PARENT);
458 link_line[column] |= if was_direct {
459 LinkLine::RIGHT_FORK_PARENT
460 } else {
461 LinkLine::RIGHT_FORK_ANCESTOR
462 };
463 #[allow(clippy::needless_range_loop)]
464 for i in column + 1..parent_column {
465 link_line[i] |= if was_direct {
466 LinkLine::HORIZ_PARENT
467 } else {
468 LinkLine::HORIZ_ANCESTOR
469 };
470 }
471 link_line[parent_column] = if was_direct {
472 LinkLine::LEFT_MERGE_PARENT
473 } else {
474 LinkLine::LEFT_MERGE_ANCESTOR
475 };
476 need_link_line = true;
477 pad_lines[parent_column] = PadLine::Blank;
479 }
480 }
481 }
482
483 if let Some(bounds) = AncestorColumnBounds::new(&parent_columns, column) {
485 for i in bounds.range() {
488 link_line[i] |= bounds.horizontal_line(i);
489 need_link_line = true;
490 }
491
492 if bounds.max_parent > column {
495 link_line[column] |= LinkLine::RIGHT_MERGE_PARENT;
496 need_link_line = true;
497 } else if bounds.max_ancestor > column {
498 link_line[column] |= LinkLine::RIGHT_MERGE_ANCESTOR;
499 need_link_line = true;
500 }
501
502 if bounds.min_parent < column {
504 link_line[column] |= LinkLine::LEFT_MERGE_PARENT;
505 need_link_line = true;
506 } else if bounds.min_ancestor < column {
507 link_line[column] |= LinkLine::LEFT_MERGE_ANCESTOR;
508 need_link_line = true;
509 }
510
511 #[allow(clippy::comparison_chain)]
513 for (&i, p) in parent_columns.iter() {
514 pad_lines[i] = self.columns[i].to_pad_line();
515 if i < column {
516 link_line[i] |=
517 p.to_link_line(LinkLine::RIGHT_FORK_PARENT, LinkLine::RIGHT_FORK_ANCESTOR);
518 } else if i == column {
519 link_line[i] |= LinkLine::CHILD
520 | p.to_link_line(LinkLine::VERT_PARENT, LinkLine::VERT_ANCESTOR);
521 } else {
522 link_line[i] |=
523 p.to_link_line(LinkLine::LEFT_FORK_PARENT, LinkLine::LEFT_FORK_ANCESTOR);
524 }
525 }
526 }
527
528 self.columns.reset();
530
531 let link_line = Some(link_line).filter(|_| need_link_line);
533 let term_line = Some(term_line).filter(|_| need_term_line);
534
535 GraphRow {
536 node,
537 glyph,
538 message,
539 merge,
540 node_line,
541 link_line,
542 term_line,
543 pad_lines,
544 }
545 }
546}