Skip to main content

gitstack/graph/
layout.rs

1//! グラフレイアウト定義
2
3use super::cell::GraphCell;
4use crate::event::GitEvent;
5
6/// グラフの1行分のデータ
7#[derive(Debug, Clone)]
8pub struct GraphRow {
9    /// このコミットのイベント情報(コネクタ行の場合はNone)
10    pub event: Option<GitEvent>,
11    /// このコミットの列位置
12    pub column: usize,
13    /// 色インデックス
14    pub color_idx: usize,
15    /// グラフセル配列(左から右への各列の描画内容)
16    pub cells: Vec<GraphCell>,
17    /// HEADかどうか
18    pub is_head: bool,
19    /// ブランチ名一覧
20    pub branch_names: Vec<String>,
21}
22
23impl GraphRow {
24    /// 新しいGraphRowを作成
25    pub fn new(
26        event: Option<GitEvent>,
27        column: usize,
28        color_idx: usize,
29        cells: Vec<GraphCell>,
30    ) -> Self {
31        Self {
32            event,
33            column,
34            color_idx,
35            cells,
36            is_head: false,
37            branch_names: Vec::new(),
38        }
39    }
40
41    /// HEADフラグを設定
42    pub fn with_head(mut self, is_head: bool) -> Self {
43        self.is_head = is_head;
44        self
45    }
46
47    /// ブランチ名を設定
48    pub fn with_branches(mut self, names: Vec<String>) -> Self {
49        self.branch_names = names;
50        self
51    }
52}
53
54/// グラフ全体のレイアウト
55#[derive(Debug, Clone)]
56pub struct GraphLayout {
57    /// 行データ
58    pub rows: Vec<GraphRow>,
59    /// 最大列数
60    pub max_column: usize,
61}
62
63impl GraphLayout {
64    /// 空のレイアウトを作成
65    pub fn empty() -> Self {
66        Self {
67            rows: Vec::new(),
68            max_column: 0,
69        }
70    }
71
72    /// 行数を取得
73    pub fn len(&self) -> usize {
74        self.rows.len()
75    }
76
77    /// 空かどうか
78    pub fn is_empty(&self) -> bool {
79        self.rows.is_empty()
80    }
81
82    /// 指定インデックスの行を取得
83    pub fn get(&self, idx: usize) -> Option<&GraphRow> {
84        self.rows.get(idx)
85    }
86
87    /// イテレータを取得
88    pub fn iter(&self) -> impl Iterator<Item = &GraphRow> {
89        self.rows.iter()
90    }
91}
92
93impl Default for GraphLayout {
94    fn default() -> Self {
95        Self::empty()
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_graph_row_new() {
105        let row = GraphRow::new(None, 0, 1, vec![GraphCell::Empty]);
106        assert!(row.event.is_none());
107        assert_eq!(row.column, 0);
108        assert_eq!(row.color_idx, 1);
109        assert!(!row.is_head);
110    }
111
112    #[test]
113    fn test_graph_row_with_head() {
114        let row = GraphRow::new(None, 0, 0, vec![]).with_head(true);
115        assert!(row.is_head);
116    }
117
118    #[test]
119    fn test_graph_row_with_branches() {
120        let row = GraphRow::new(None, 0, 0, vec![]).with_branches(vec!["main".to_string()]);
121        assert_eq!(row.branch_names, vec!["main"]);
122    }
123
124    #[test]
125    fn test_graph_layout_empty() {
126        let layout = GraphLayout::empty();
127        assert!(layout.is_empty());
128        assert_eq!(layout.len(), 0);
129        assert_eq!(layout.max_column, 0);
130    }
131
132    #[test]
133    fn test_graph_layout_get() {
134        let mut layout = GraphLayout::empty();
135        layout.rows.push(GraphRow::new(None, 0, 0, vec![]));
136        assert!(layout.get(0).is_some());
137        assert!(layout.get(1).is_none());
138    }
139}