just playing with tangled
1// Copyright 2020 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::hash::Hash;
16use std::io;
17use std::io::Write;
18
19use itertools::Itertools;
20use jj_lib::settings::UserSettings;
21use renderdag::{Ancestor, GraphRowRenderer, Renderer};
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24// An edge to another node in the graph
25pub enum Edge<T> {
26 Direct(T),
27 Indirect(T),
28 Missing,
29}
30
31pub trait GraphLog<K: Clone + Eq + Hash> {
32 fn add_node(
33 &mut self,
34 id: &K,
35 edges: &[Edge<K>],
36 node_symbol: &str,
37 text: &str,
38 ) -> io::Result<()>;
39
40 fn width(&self, id: &K, edges: &[Edge<K>]) -> usize;
41}
42
43pub struct SaplingGraphLog<'writer, R> {
44 renderer: R,
45 writer: &'writer mut dyn Write,
46}
47
48impl<K: Clone> From<&Edge<K>> for Ancestor<K> {
49 fn from(e: &Edge<K>) -> Self {
50 match e {
51 Edge::Direct(target) => Ancestor::Parent(target.clone()),
52 Edge::Indirect(target) => Ancestor::Ancestor(target.clone()),
53 Edge::Missing => Ancestor::Anonymous,
54 }
55 }
56}
57
58impl<'writer, K, R> GraphLog<K> for SaplingGraphLog<'writer, R>
59where
60 K: Clone + Eq + Hash,
61 R: Renderer<K, Output = String>,
62{
63 fn add_node(
64 &mut self,
65 id: &K,
66 edges: &[Edge<K>],
67 node_symbol: &str,
68 text: &str,
69 ) -> io::Result<()> {
70 let row = self.renderer.next_row(
71 id.clone(),
72 edges.iter().map_into().collect(),
73 node_symbol.into(),
74 text.into(),
75 );
76
77 write!(self.writer, "{row}")
78 }
79
80 fn width(&self, id: &K, edges: &[Edge<K>]) -> usize {
81 let parents = edges.iter().map_into().collect();
82 let w: u64 = self.renderer.width(Some(id), Some(&parents));
83 w.try_into().unwrap()
84 }
85}
86
87impl<'writer, R> SaplingGraphLog<'writer, R> {
88 pub fn create<K>(
89 renderer: R,
90 formatter: &'writer mut dyn Write,
91 ) -> Box<dyn GraphLog<K> + 'writer>
92 where
93 K: Clone + Eq + Hash + 'writer,
94 R: Renderer<K, Output = String> + 'writer,
95 {
96 Box::new(SaplingGraphLog {
97 renderer,
98 writer: formatter,
99 })
100 }
101}
102
103pub fn get_graphlog<'a, K: Clone + Eq + Hash + 'a>(
104 settings: &UserSettings,
105 formatter: &'a mut dyn Write,
106) -> Box<dyn GraphLog<K> + 'a> {
107 let builder = GraphRowRenderer::new().output().with_min_row_height(0);
108 match settings.graph_style().as_str() {
109 "square" => {
110 SaplingGraphLog::create(builder.build_box_drawing().with_square_glyphs(), formatter)
111 }
112 "ascii" => SaplingGraphLog::create(builder.build_ascii(), formatter),
113 "ascii-large" => SaplingGraphLog::create(builder.build_ascii_large(), formatter),
114 // "curved"
115 _ => SaplingGraphLog::create(builder.build_box_drawing(), formatter),
116 }
117}