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 view_root: Option<NodeId>,
62 revision: TreeRevision,
63}
64
65impl Tree {
66 pub fn new() -> Self {
67 Self::default()
68 }
69
70 pub fn push(
71 &mut self,
72 parent: Option<NodeId>,
73 name: impl Into<String>,
74 is_container: bool,
75 action: ActionValues,
76 ) -> NodeId {
77 self.push_with_detail(parent, name, None, is_container, action)
78 }
79
80 pub fn push_with_detail(
81 &mut self,
82 parent: Option<NodeId>,
83 name: impl Into<String>,
84 detail: Option<String>,
85 is_container: bool,
86 action: ActionValues,
87 ) -> NodeId {
88 let id = self.nodes.len();
89 let depth = parent.map_or(0, |id| self.nodes[id].depth + 1);
90 self.nodes.push(Node {
91 name: name.into(),
92 detail,
93 parent,
94 children: Vec::new(),
95 is_container,
96 depth,
97 action,
98 });
99 match parent {
100 Some(parent) => self.nodes[parent].children.push(id),
101 None => self.roots.push(id),
102 }
103 id
104 }
105
106 pub fn node(&self, id: NodeId) -> &Node {
107 &self.nodes[id]
108 }
109
110 pub fn len(&self) -> usize {
111 self.nodes.len()
112 }
113
114 pub fn is_empty(&self) -> bool {
115 self.nodes.is_empty()
116 }
117
118 pub fn root_ids(&self) -> &[NodeId] {
119 &self.roots
120 }
121
122 pub(crate) fn view_root(&self) -> Option<NodeId> {
124 self.view_root
125 }
126
127 pub(crate) fn set_view_root(&mut self, root: Option<NodeId>) {
129 if self.view_root != root {
130 self.view_root = root;
131 self.revision.advance();
132 }
133 }
134
135 pub(crate) fn view_parent(&self, id: NodeId) -> Option<NodeId> {
137 if self.view_root == Some(id) {
138 None
139 } else {
140 self.nodes[id].parent
141 }
142 }
143
144 pub(crate) fn is_in_view(&self, id: NodeId) -> bool {
146 let Some(root) = self.view_root else {
147 return true;
148 };
149 let mut cursor = Some(id);
150 while let Some(current) = cursor {
151 if current == root {
152 return true;
153 }
154 cursor = self.nodes[current].parent;
155 }
156 false
157 }
158
159 pub fn is_leaf(&self, id: NodeId) -> bool {
161 self.nodes[id].children.is_empty()
162 }
163
164 pub fn branches(&self) -> impl Iterator<Item = (NodeId, Option<NodeId>)> + '_ {
166 self.nodes
167 .iter()
168 .enumerate()
169 .filter(|(id, _)| !self.is_leaf(*id))
170 .map(|(id, node)| (id, node.parent))
171 }
172}
173
174impl TreeModel for Tree {
175 type Id = NodeId;
176
177 fn roots(&self) -> impl Iterator<Item = NodeId> + '_ {
178 self.roots
179 .iter()
180 .copied()
181 .filter(|_| self.view_root.is_none())
182 .chain(self.view_root)
183 }
184
185 fn children(&self, id: NodeId) -> TreeChildren<'_, NodeId> {
186 TreeChildren::loaded(&self.nodes[id].children)
187 }
188
189 fn revision(&self) -> TreeRevision {
190 self.revision
191 }
192
193 fn size_hint(&self) -> usize {
194 self.nodes.len()
195 }
196}