1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::fmt;
use super::{Resources, Directory, Entry};

/// Art used to format a directory tree.
#[derive(Debug)]
struct Art {
	margin_draw: &'static str,
	margin_open: &'static str,
	dir_entry: &'static str,
	dir_tail: &'static str,
	file_entry: &'static str,
	file_tail: &'static str,
}
/// Uses [box-drawing characters](https://en.wikipedia.org/wiki/Box-drawing_character) to draw the tree art.
static U: Art = Art {
	margin_draw: "│   ",
	margin_open: "    ",
	dir_entry:   "├── ",
	dir_tail:    "└── ",
	file_entry:  "├── ",
	file_tail:   "└── ",
};
/// Uses ascii to draw the tree art.
static A: Art = Art {
	margin_draw: "|   ",
	margin_open: "    ",
	dir_entry:   "+-- ",
	dir_tail:    "`-- ",
	file_entry:  "+-- ",
	file_tail:   "`-- ",
};

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[allow(dead_code)]
enum TreeArt {
	Ascii,
	Unicode,
}

#[derive(Clone, Debug)]
struct TreeFmt<'a: 'd, 'd> {
	dir: &'d Directory<'a>,
	art: &'static Art,
	depth: u32,
	margin: u32,
}
impl<'a, 'd> TreeFmt<'a, 'd> {
	fn root(root: &'d Directory<'a>, art: TreeArt) -> TreeFmt<'a, 'd> {
		let art = match art {
			TreeArt::Ascii => &A,
			TreeArt::Unicode => &U,
		};
		TreeFmt { dir: root, art, depth: !0, margin: 0 }
	}
	fn dir(dir: &'d Directory<'a>, art: TreeArt) -> TreeFmt<'a, 'd> {
		let art = match art {
			TreeArt::Ascii => &A,
			TreeArt::Unicode => &U,
		};
		TreeFmt { dir, art, depth: 0, margin: 0 }
	}

	fn draw<F: fmt::Write>(&self, f: &mut F) -> fmt::Result {
		// Encode if root in depth
		let (root, depth) = if self.depth == !0 { (true, 0) } else { (false, self.depth) };

		// Quiet failsafe, unlikely to happen
		if depth >= 32 {
			return Ok(());
		}

		let mut entries = self.dir.entries();
		while let Some(e) = entries.next() {
			// Print the margin
			for open in (0..depth).map(|i| self.margin & (1 << i) != 0) {
				f.write_str(if open { self.art.margin_open } else { self.art.margin_draw })?;
			}
			// Write the prefix
			let tail = entries.len() == 0;
			let prefix = match (tail, e.is_dir()) {
				(false, false) => self.art.file_entry,
				(true, false) => self.art.file_tail,
				(false, true) => self.art.dir_entry,
				(true, true) => self.art.dir_tail,
			};
			f.write_str(prefix)?;
			// Print the file_name
			match e.name() {
				Ok(name) => write!(f, "{}", name.rename_id(if root { &super::RSRC_TYPES } else { &[] })),
				Err(err) => write!(f, "{}", err),
			}.and_then(|_| {
				f.write_str(if e.is_dir() { "/\n" } else { "\n" })
			})?;
			// If it's a directory, print it recursively
			if let Ok(Entry::Directory(dir)) = e.entry() {
				TreeFmt {
					dir: &dir,
					art: self.art,
					depth: depth + 1,
					margin: self.margin | (tail as u32) << depth,
				}.draw(f)?;
			}
		}
		Ok(())
	}
}

impl<'a> fmt::Display for Resources<'a> {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		f.write_str("Resources/\n")?;
		match self.root() {
			Ok(root) => TreeFmt::root(&root, TreeArt::Ascii).draw(f),
			Err(err) => err.fmt(f),
		}
	}
}

impl<'a> fmt::Display for Directory<'a> {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		f.write_str("Directory/\n")?;
		TreeFmt::dir(self, TreeArt::Ascii).draw(f)
	}
}