tree.rs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. //! Tree structures, such as `├──` or `└──`, used in a tree view.
  2. //!
  3. //! ## Constructing Tree Views
  4. //!
  5. //! When using the `--tree` argument, instead of a vector of cells, each row
  6. //! has a `depth` field that indicates how far deep in the tree it is: the top
  7. //! level has depth 0, its children have depth 1, and *their* children have
  8. //! depth 2, and so on.
  9. //!
  10. //! On top of this, it also has a `last` field that specifies whether this is
  11. //! the last row of this particular consecutive set of rows. This doesn’t
  12. //! affect the file’s information; it’s just used to display a different set of
  13. //! Unicode tree characters! The resulting table looks like this:
  14. //!
  15. //! ```text
  16. //! ┌───────┬───────┬───────────────────────┐
  17. //! │ Depth │ Last │ Output │
  18. //! ├───────┼───────┼───────────────────────┤
  19. //! │ 0 │ │ documents │
  20. //! │ 1 │ false │ ├── this_file.txt │
  21. //! │ 1 │ false │ ├── that_file.txt │
  22. //! │ 1 │ false │ ├── features │
  23. //! │ 2 │ false │ │ ├── feature_1.rs │
  24. //! │ 2 │ false │ │ ├── feature_2.rs │
  25. //! │ 2 │ true │ │ └── feature_3.rs │
  26. //! │ 1 │ true │ └── pictures │
  27. //! │ 2 │ false │ ├── garden.jpg │
  28. //! │ 2 │ false │ ├── flowers.jpg │
  29. //! │ 2 │ false │ ├── library.png │
  30. //! │ 2 │ true │ └── space.tiff │
  31. //! └───────┴───────┴───────────────────────┘
  32. //! ```
  33. //!
  34. //! Creating the table like this means that each file has to be tested to see
  35. //! if it’s the last one in the group. This is usually done by putting all the
  36. //! files in a vector beforehand, getting its length, then comparing the index
  37. //! of each file to see if it’s the last one. (As some files may not be
  38. //! successfully `stat`ted, we don’t know how many files are going to exist in
  39. //! each directory)
  40. #[derive(PartialEq, Eq, Debug, Copy, Clone)]
  41. pub enum TreePart {
  42. /// Rightmost column, *not* the last in the directory.
  43. Edge,
  44. /// Not the rightmost column, and the directory has not finished yet.
  45. Line,
  46. /// Rightmost column, and the last in the directory.
  47. Corner,
  48. /// Not the rightmost column, and the directory *has* finished.
  49. Blank,
  50. }
  51. impl TreePart {
  52. /// Turn this tree part into ASCII-licious box drawing characters!
  53. /// (Warning: not actually ASCII)
  54. pub fn ascii_art(self) -> &'static str {
  55. #[rustfmt::skip]
  56. return match self {
  57. Self::Edge => "├──",
  58. Self::Line => "│ ",
  59. Self::Corner => "└──",
  60. Self::Blank => " ",
  61. };
  62. }
  63. }
  64. /// A **tree trunk** builds up arrays of tree parts over multiple depths.
  65. #[derive(Debug, Default)]
  66. pub struct TreeTrunk {
  67. /// A stack tracks which tree characters should be printed. It’s
  68. /// necessary to maintain information about the previously-printed
  69. /// lines, as the output will change based on any previous entries.
  70. stack: Vec<TreePart>,
  71. /// A tuple for the last ‘depth’ and ‘last’ parameters that are passed in.
  72. last_params: Option<TreeParams>,
  73. }
  74. #[derive(Debug, Copy, Clone)]
  75. pub struct TreeParams {
  76. /// How many directories deep into the tree structure this is. Directories
  77. /// on top have depth 0.
  78. pub depth: TreeDepth,
  79. /// Whether this is the last entry in the directory.
  80. pub last: bool,
  81. }
  82. #[derive(Debug, Copy, Clone, PartialEq, Eq)]
  83. pub struct TreeDepth(pub usize);
  84. impl TreeTrunk {
  85. /// Calculates the tree parts for an entry at the given depth and
  86. /// last-ness. The depth is used to determine where in the stack the tree
  87. /// part should be inserted, and the last-ness is used to determine which
  88. /// type of tree part to insert.
  89. ///
  90. /// This takes a `&mut self` because the results of each file are stored
  91. /// and used in future rows.
  92. pub fn new_row(&mut self, params: TreeParams) -> &[TreePart] {
  93. // If this isn’t our first iteration, then update the tree parts thus
  94. // far to account for there being another row after it.
  95. if let Some(last) = self.last_params {
  96. self.stack[last.depth.0] = if last.last {
  97. TreePart::Blank
  98. } else {
  99. TreePart::Line
  100. };
  101. }
  102. // Make sure the stack has enough space, then add or modify another
  103. // part into it.
  104. self.stack.resize(params.depth.0 + 1, TreePart::Edge);
  105. self.stack[params.depth.0] = if params.last {
  106. TreePart::Corner
  107. } else {
  108. TreePart::Edge
  109. };
  110. self.last_params = Some(params);
  111. // Return the tree parts as a slice of the stack.
  112. //
  113. // Ignore the first element here to prevent a ‘zeroth level’ from
  114. // appearing before the very first directory. This level would
  115. // join unrelated directories without connecting to anything:
  116. //
  117. // with [0..] with [1..]
  118. // ========== ==========
  119. // ├── folder folder
  120. // │ └── file └── file
  121. // └── folder folder
  122. // └── file └──file
  123. //
  124. &self.stack[1..]
  125. }
  126. }
  127. impl TreeParams {
  128. pub fn new(depth: TreeDepth, last: bool) -> Self {
  129. Self { depth, last }
  130. }
  131. pub fn is_at_root(&self) -> bool {
  132. self.depth.0 == 0
  133. }
  134. }
  135. impl TreeDepth {
  136. pub fn root() -> Self {
  137. Self(0)
  138. }
  139. pub fn deeper(self) -> Self {
  140. Self(self.0 + 1)
  141. }
  142. /// Creates an iterator that, as well as yielding each value, yields a
  143. /// `TreeParams` with the current depth and last flag filled in.
  144. pub fn iterate_over<I, T>(self, inner: I) -> Iter<I>
  145. where
  146. I: ExactSizeIterator + Iterator<Item = T>,
  147. {
  148. Iter {
  149. current_depth: self,
  150. inner,
  151. }
  152. }
  153. }
  154. pub struct Iter<I> {
  155. current_depth: TreeDepth,
  156. inner: I,
  157. }
  158. impl<I, T> Iterator for Iter<I>
  159. where
  160. I: ExactSizeIterator + Iterator<Item = T>,
  161. {
  162. type Item = (TreeParams, T);
  163. fn next(&mut self) -> Option<Self::Item> {
  164. let t = self.inner.next()?;
  165. // TODO: use exact_size_is_empty API soon
  166. let params = TreeParams::new(self.current_depth, self.inner.len() == 0);
  167. Some((params, t))
  168. }
  169. }
  170. #[cfg(test)]
  171. mod trunk_test {
  172. use super::*;
  173. fn params(depth: usize, last: bool) -> TreeParams {
  174. TreeParams::new(TreeDepth(depth), last)
  175. }
  176. #[rustfmt::skip]
  177. #[test]
  178. fn empty_at_first() {
  179. let mut tt = TreeTrunk::default();
  180. assert_eq!(tt.new_row(params(0, true)), &[ ]);
  181. }
  182. #[rustfmt::skip]
  183. #[test]
  184. fn one_child() {
  185. let mut tt = TreeTrunk::default();
  186. assert_eq!(tt.new_row(params(0, true)), &[ ]);
  187. assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]);
  188. }
  189. #[rustfmt::skip]
  190. #[test]
  191. fn two_children() {
  192. let mut tt = TreeTrunk::default();
  193. assert_eq!(tt.new_row(params(0, true)), &[ ]);
  194. assert_eq!(tt.new_row(params(1, false)), &[ TreePart::Edge ]);
  195. assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]);
  196. }
  197. #[rustfmt::skip]
  198. #[test]
  199. fn two_times_two_children() {
  200. let mut tt = TreeTrunk::default();
  201. assert_eq!(tt.new_row(params(0, false)), &[ ]);
  202. assert_eq!(tt.new_row(params(1, false)), &[ TreePart::Edge ]);
  203. assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]);
  204. assert_eq!(tt.new_row(params(0, true)), &[ ]);
  205. assert_eq!(tt.new_row(params(1, false)), &[ TreePart::Edge ]);
  206. assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]);
  207. }
  208. #[rustfmt::skip]
  209. #[test]
  210. fn two_times_two_nested_children() {
  211. let mut tt = TreeTrunk::default();
  212. assert_eq!(tt.new_row(params(0, true)), &[ ]);
  213. assert_eq!(tt.new_row(params(1, false)), &[ TreePart::Edge ]);
  214. assert_eq!(tt.new_row(params(2, false)), &[ TreePart::Line, TreePart::Edge ]);
  215. assert_eq!(tt.new_row(params(2, true)), &[ TreePart::Line, TreePart::Corner ]);
  216. assert_eq!(tt.new_row(params(1, true)), &[ TreePart::Corner ]);
  217. assert_eq!(tt.new_row(params(2, false)), &[ TreePart::Blank, TreePart::Edge ]);
  218. assert_eq!(tt.new_row(params(2, true)), &[ TreePart::Blank, TreePart::Corner ]);
  219. }
  220. }
  221. #[cfg(test)]
  222. mod iter_test {
  223. use super::*;
  224. #[test]
  225. fn test_iteration() {
  226. let foos = &["first", "middle", "last"];
  227. let mut iter = TreeDepth::root().iterate_over(foos.iter());
  228. let next = iter.next().unwrap();
  229. assert_eq!(&"first", next.1);
  230. assert!(!next.0.last);
  231. let next = iter.next().unwrap();
  232. assert_eq!(&"middle", next.1);
  233. assert!(!next.0.last);
  234. let next = iter.next().unwrap();
  235. assert_eq!(&"last", next.1);
  236. assert!(next.0.last);
  237. assert!(iter.next().is_none());
  238. }
  239. #[test]
  240. fn test_empty() {
  241. let nothing: &[usize] = &[];
  242. let mut iter = TreeDepth::root().iterate_over(nothing.iter());
  243. assert!(iter.next().is_none());
  244. }
  245. }