1use std::ffi::OsString;
4
5use tui_treelistview::{TreeChildren, TreeModel, TreeRevision};
6
7pub type NodeId = usize;
8
9#[derive(Clone, Debug, PartialEq, Eq)]
11pub struct ActionValues {
12 pub output: OsString,
14 pub alternate_output: OsString,
16 pub path: OsString,
18 pub relpath: OsString,
20}
21
22impl ActionValues {
23 pub fn new(
24 output: impl Into<OsString>,
25 path: impl Into<OsString>,
26 relpath: impl Into<OsString>,
27 ) -> Self {
28 let output = output.into();
29 Self {
30 alternate_output: output.clone(),
31 output,
32 path: path.into(),
33 relpath: relpath.into(),
34 }
35 }
36
37 pub fn with_alternate_output(mut self, output: impl Into<OsString>) -> Self {
38 self.alternate_output = output.into();
39 self
40 }
41}
42
43#[derive(Debug)]
44pub struct Node {
45 pub name: String,
46 pub detail: Option<String>,
48 pub parent: Option<NodeId>,
49 pub children: Vec<NodeId>,
50 pub is_container: bool,
52 pub depth: usize,
54 pub action: ActionValues,
55}
56
57#[derive(Debug, Default)]
58pub struct Tree {
59 pub(crate) nodes: Vec<Node>,
60 pub(crate) roots: Vec<NodeId>,
61}
62
63impl Tree {
64 pub fn new() -> Self {
65 Self::default()
66 }
67
68 pub fn push(
69 &mut self,
70 parent: Option<NodeId>,
71 name: impl Into<String>,
72 is_container: bool,
73 action: ActionValues,
74 ) -> NodeId {
75 self.push_with_detail(parent, name, None, is_container, action)
76 }
77
78 pub fn push_with_detail(
79 &mut self,
80 parent: Option<NodeId>,
81 name: impl Into<String>,
82 detail: Option<String>,
83 is_container: bool,
84 action: ActionValues,
85 ) -> NodeId {
86 let id = self.nodes.len();
87 let depth = parent.map_or(0, |id| self.nodes[id].depth + 1);
88 self.nodes.push(Node {
89 name: name.into(),
90 detail,
91 parent,
92 children: Vec::new(),
93 is_container,
94 depth,
95 action,
96 });
97 match parent {
98 Some(parent) => self.nodes[parent].children.push(id),
99 None => self.roots.push(id),
100 }
101 id
102 }
103
104 pub fn node(&self, id: NodeId) -> &Node {
105 &self.nodes[id]
106 }
107
108 pub fn len(&self) -> usize {
109 self.nodes.len()
110 }
111
112 pub fn is_empty(&self) -> bool {
113 self.nodes.is_empty()
114 }
115
116 pub fn root_ids(&self) -> &[NodeId] {
117 &self.roots
118 }
119
120 pub fn is_leaf(&self, id: NodeId) -> bool {
122 self.nodes[id].children.is_empty()
123 }
124
125 pub fn branches(&self) -> impl Iterator<Item = (NodeId, Option<NodeId>)> + '_ {
127 self.nodes
128 .iter()
129 .enumerate()
130 .filter(|(id, _)| !self.is_leaf(*id))
131 .map(|(id, node)| (id, node.parent))
132 }
133}
134
135impl TreeModel for Tree {
136 type Id = NodeId;
137
138 fn roots(&self) -> impl Iterator<Item = NodeId> + '_ {
139 self.roots.iter().copied()
140 }
141
142 fn children(&self, id: NodeId) -> TreeChildren<'_, NodeId> {
143 TreeChildren::loaded(&self.nodes[id].children)
144 }
145
146 fn revision(&self) -> TreeRevision {
147 TreeRevision::INITIAL
148 }
149
150 fn size_hint(&self) -> usize {
151 self.nodes.len()
152 }
153}