pelite/resources/
art.rs

1use std::fmt;
2use super::{Resources, Directory, Entry};
3
4/// Art used to format a directory tree.
5#[derive(Debug)]
6struct Art {
7	margin_draw: &'static str,
8	margin_open: &'static str,
9	dir_entry: &'static str,
10	dir_tail: &'static str,
11	file_entry: &'static str,
12	file_tail: &'static str,
13}
14/// Uses [box-drawing characters](https://en.wikipedia.org/wiki/Box-drawing_character) to draw the tree art.
15static U: Art = Art {
16	margin_draw: "│   ",
17	margin_open: "    ",
18	dir_entry:   "├── ",
19	dir_tail:    "└── ",
20	file_entry:  "├── ",
21	file_tail:   "└── ",
22};
23/// Uses ascii to draw the tree art.
24static A: Art = Art {
25	margin_draw: "|   ",
26	margin_open: "    ",
27	dir_entry:   "+-- ",
28	dir_tail:    "`-- ",
29	file_entry:  "+-- ",
30	file_tail:   "`-- ",
31};
32
33#[derive(Copy, Clone, Debug, Eq, PartialEq)]
34#[allow(dead_code)]
35enum TreeArt {
36	Ascii,
37	Unicode,
38}
39
40#[derive(Clone, Debug)]
41struct TreeFmt<'a: 'd, 'd> {
42	dir: &'d Directory<'a>,
43	art: &'static Art,
44	depth: u32,
45	margin: u32,
46}
47impl<'a, 'd> TreeFmt<'a, 'd> {
48	fn root(root: &'d Directory<'a>, art: TreeArt) -> TreeFmt<'a, 'd> {
49		let art = match art {
50			TreeArt::Ascii => &A,
51			TreeArt::Unicode => &U,
52		};
53		TreeFmt { dir: root, art, depth: !0, margin: 0 }
54	}
55	fn dir(dir: &'d Directory<'a>, art: TreeArt) -> TreeFmt<'a, 'd> {
56		let art = match art {
57			TreeArt::Ascii => &A,
58			TreeArt::Unicode => &U,
59		};
60		TreeFmt { dir, art, depth: 0, margin: 0 }
61	}
62
63	fn draw<F: fmt::Write>(&self, f: &mut F) -> fmt::Result {
64		// Encode if root in depth
65		let (root, depth) = if self.depth == !0 { (true, 0) } else { (false, self.depth) };
66
67		// Quiet failsafe, unlikely to happen
68		if depth >= 32 {
69			return Ok(());
70		}
71
72		let mut entries = self.dir.entries();
73		while let Some(e) = entries.next() {
74			// Print the margin
75			for open in (0..depth).map(|i| self.margin & (1 << i) != 0) {
76				f.write_str(if open { self.art.margin_open } else { self.art.margin_draw })?;
77			}
78			// Write the prefix
79			let tail = entries.len() == 0;
80			let prefix = match (tail, e.is_dir()) {
81				(false, false) => self.art.file_entry,
82				(true, false) => self.art.file_tail,
83				(false, true) => self.art.dir_entry,
84				(true, true) => self.art.dir_tail,
85			};
86			f.write_str(prefix)?;
87			// Print the file_name
88			match e.name() {
89				Ok(name) => write!(f, "{}", name.rename_id(if root { &super::RSRC_TYPES } else { &[] })),
90				Err(err) => write!(f, "{}", err),
91			}.and_then(|_| {
92				f.write_str(if e.is_dir() { "/\n" } else { "\n" })
93			})?;
94			// If it's a directory, print it recursively
95			if let Ok(Entry::Directory(dir)) = e.entry() {
96				TreeFmt {
97					dir: &dir,
98					art: self.art,
99					depth: depth + 1,
100					margin: self.margin | (tail as u32) << depth,
101				}.draw(f)?;
102			}
103		}
104		Ok(())
105	}
106}
107
108impl<'a> fmt::Display for Resources<'a> {
109	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110		f.write_str("Resources/\n")?;
111		match self.root() {
112			Ok(root) => TreeFmt::root(&root, TreeArt::Ascii).draw(f),
113			Err(err) => err.fmt(f),
114		}
115	}
116}
117
118impl<'a> fmt::Display for Directory<'a> {
119	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120		f.write_str("Directory/\n")?;
121		TreeFmt::dir(self, TreeArt::Ascii).draw(f)
122	}
123}