Мой ввод - это простой список путей файловой системы, которые являются всеми подкаталогами (или файлами в нем) одного каталога верхнего уровня.
Мой конечный вывод должен быть:
- Текстовое иерархическое отображение путей, например, unix tree команда.
- Иерархическая JSON сериализация путей с соответствующей логической структурой to (1)
Я создал промежуточную структуру данных, которая представляет собой самоссылающуюся struct Dir
с именем и вектором Box
'ed child struct Dir
.
Я могу успешно использовать Dir
для представления произвольного дерева каталогов, как показано в выходных данных ниже.
Я думал использовать стек для обработки списка, добавив Dir
для каждого подпрограммы -директория и всплывающее окно при подъеме, но я не могу заставить его работать с Rust так же, как с C или другими языками. Я получаю ошибки компилятора независимо от того, что я пытаюсь.
Как я могу превратить плоский список в Dir
и поддерживать компилятор счастливым? Или, в качестве альтернативы, как добиться (1) и (2) по-разному?
Код:
// A type to represent a path, split into its component parts
#[derive(Debug)]
struct Path {
parts: Vec<String>,
}
impl Path {
pub fn new(path: &str) -> Path {
Path {
parts: path.to_string().split("/").map(|s| s.to_string()).collect(),
}
}
}
// A recursive type to represent a directory tree.
// Simplification: If it has children, it is considered
// a directory, else considered a file.
#[derive(Debug)]
struct Dir {
name: String,
children: Vec<Box<Dir>>,
}
impl Dir {
fn new(name: &str) -> Dir {
Dir {
name: name.to_string(),
children: Vec::<Box<Dir>>::new(),
}
}
fn has_child(&self, name: &str) -> bool {
for c in self.children.iter() {
if c.name == name {
return true;
}
}
false
}
fn add_child<T>(mut self, leaf: T) -> Self
where
T: Into<Dir>,
{
self.children.push(Box::new(leaf.into()));
self
}
}
fn dir(val: &str) -> Dir {
Dir::new(val)
}
fn main() {
// Form our INPUT: a list of paths.
let paths = vec![
Path::new("root/child1/grandchild1.txt"),
Path::new("root/child1/grandchild2.json"),
Path::new("root/child2/grandchild3.pdf"),
Path::new("root/child3"),
];
println!("Input Paths:\n{:#?}\n", paths);
// Transformation:
// I need an algorithm here that converts the list of paths
// above to a recursive struct (tree) below.
// ie: paths --> dir
let top = dir("root");
let mut cwd = ⊤
for p in paths.iter() {
for part in p.parts.iter() {
if !cwd.has_child(part) {
// cwd.add_child(dir(part));
// cwd = &cwd.children[cwd.children.len() - 1];
}
}
}
// Intermediate Representation:
// The above transformation should result in the following
// hierarchical structure.
let top = dir("root")
.add_child(
dir("child1")
.add_child(dir("grandchild1.txt"))
.add_child(dir("grandchild2.json")),
)
.add_child(dir("child2").add_child(dir("grandchild3.pdf")))
.add_child(dir("child3"));
println!("Intermediate Representation of Dirs:\n{:#?}\n\nOutput Tree Format:\n", top);
// Output: textual `tree` format
print_dir(&top, 0);
}
// A function to print a Dir in format similar to unix `tree` command.
fn print_dir(dir: &Dir, depth: u32) {
if depth == 0 {
println!("{}", dir.name);
} else {
println!(
"{:indent$}{} {}",
"",
"└──",
dir.name,
indent = ((depth as usize) - 1) * 4
);
}
for child in dir.children.iter() {
print_dir(child, depth + 1)
}
}
Вывод:
$ ./target/debug/rust-tree
Input Paths:
[
Path {
parts: [
"root",
"child1",
"grandchild1.txt",
],
},
Path {
parts: [
"root",
"child1",
"grandchild2.json",
],
},
Path {
parts: [
"root",
"child2",
"grandchild3.pdf",
],
},
Path {
parts: [
"root",
"child3",
],
},
]
Intermediate Representation of Dirs:
Dir {
name: "root",
children: [
Dir {
name: "child1",
children: [
Dir {
name: "grandchild1.txt",
children: [],
},
Dir {
name: "grandchild2.json",
children: [],
},
],
},
Dir {
name: "child2",
children: [
Dir {
name: "grandchild3.pdf",
children: [],
},
],
},
Dir {
name: "child3",
children: [],
},
],
}
Output Tree Format:
root
└── child1
└── grandchild1.txt
└── grandchild2.json
└── child2
└── grandchild3.pdf
└── child3