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
//! [`FsTree`] is a path [Trie] with an API focused on filesystem operations.
//!
//! # Clarifications:
//!
//! 1. _Unix_ only.
//! 2. This crate was transfered after `0.1.3`, and changed its purpose.
//! 3. This crate refers to _"filesystem tree"_ as the result you get from recursively traversing files:
//!     - If you try traversing a single file, you get a single node.
//!     - If you try traversing a directories, you might get a large subtree (of nodes).
//!     - This is agnostic to the underlying filesystem (nothing to do with `ext4` or `btrfs`).
//! 4. Check [Trie] if you both haven't met yet.
//!
//! # Tree Layout:
//!
//! A `FsTree` is a node with three possible file types:
//!
//! ```
//! use std::{collections::BTreeMap, path::PathBuf};
//!
//! pub enum FsTree {
//!     Regular,
//!     Directory(TrieMap), // Recursive part
//!     Symlink(PathBuf),
//! }
//! //                                     ↓↓
//! pub type TrieMap = BTreeMap<PathBuf, FsTree>; // Recursive part
//! ```
//!
//! The root of the `FsTree` is **unnamed** (no filename/path), the "edges" to children are the
//! relative paths.
//!
//! # Pitfall warning:
//!
//! Like `std` functions, functions in this crate follow symlinks (and symlink chains), so you'll
//! never get a [`FsTree::Symlink(_)`] in your tree! If you want symlink-awareness, use the function
//! version with the `symlink` prefix ([`FsTree::read_at`] vs [`FsTree::symlink_read_at`]).
//!
//! # Ways to construct a [`FsTree`]:
//!
//! 1. Read node/tree from path. ([`FsTree::symlink_read_at`])
//! 2. Declare a `FsTree` literal. ([`tree!`])
//! 3. Insert each node in an empty folder. ([`FsTree::new_dir`] + [`FsTree::insert`])
//! 4. Parse from path text segments. ([`FsTree::from_path_text`])
//!
//! # What you can do with a [`FsTree`]:
//!
//! 1. Traverse, query, and modify it.
//! 2. Merge with another tree. ([`FsTree::try_merge`])
//! 3. Write it to disk. ([`FsTree::write_at`])
//! 4. Try loading a structural copy of it from a path. ([`FsTree::read_copy_at`])
//! 5. (TODO) Compare with another `FsTree`, generating a DiffTree.
//! 6. (TODO) Add entry API.
//!
//! ## Iterators:
//!
//! See docs in the [`iter` module].
//!
//! # Alternatives:
//! - Crate [`walkdir`](https://docs.rs/walkdir) - Better if you just need to iterate on
//! filesystem trees.
//! - Crate [`file_type_enum`](https://docs.rs/file_type_enum) - If you want a shallow type enum.
//! - Crate [`build-fs-tree`](https://crates.io/crates/build-fs-tree) - If you need to create a
//! filesystem tree from a YAML file.
//!     - The closest we got is creating a tree literal with [`tree!`](crate::tree), and writing
//! with [`FsTree::write_at`].
//!
//! [Trie]: https://en.wikipedia.org/wiki/Trie
//! [`FsTree::Symlink(_)`]: https://docs.rs/fs-tree/latest/fs_tree/enum.FsTree.html#variant.Symlink
//! [`iter` module]: crate::iter

#![warn(missing_docs)]
// Emits false-positives on macros.
#![allow(clippy::vec_init_then_push)]

/// [`FsTree`] iterators.
pub mod iter;

pub use self::{
    error::{Error, Result},
    fs_tree::{FsTree, TrieMap},
};

mod error;
mod fs_tree;
mod macros;
pub(crate) mod utils;

#[cfg(not(feature = "fs-err"))]
pub(crate) use std::fs;

#[cfg(feature = "fs-err")]
pub(crate) use fs_err as fs;