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}