web_static_pack_packer/
directory.rs

1//! Directory helpers. Contains [search] function, used to gather files from
2//! directory recursively.
3
4use crate::{file, file_pack_path};
5use anyhow::{Context, Error};
6use std::path::Path;
7use walkdir::WalkDir;
8
9/// Settings for [search] function.
10///
11/// If not sure what to set here, use [Default].
12#[derive(Debug)]
13pub struct SearchOptions {
14    /// Whether to follow links while traversing directories.
15    pub follow_links: bool,
16}
17impl Default for SearchOptions {
18    fn default() -> Self {
19        Self { follow_links: true }
20    }
21}
22
23/// Searches fs recursively and builds [file_pack_path::FilePackPath] for each
24/// file.
25///
26/// Traverses directory specified in `path` using [SearchOptions]. Builds all
27/// found files as [file_pack_path::FilePackPath] using
28/// [file::BuildFromPathOptions]. Paths are created by stripping `path` from
29/// full file path.
30///
31/// # Examples
32///
33/// ```
34/// # use anyhow::Error;
35/// # use std::{collections::HashMap, path::PathBuf};
36/// # use web_static_pack_packer::{
37/// #     directory::{search, SearchOptions},
38/// #     file::BuildFromPathOptions,
39/// # };
40/// #
41/// # fn main() -> Result<(), Error> {
42/// #
43/// // traverse directory from tests
44/// let files = search(
45///     &PathBuf::from(env!("CARGO_MANIFEST_DIR"))
46///         .parent()
47///         .unwrap()
48///         .join("tests")
49///         .join("data")
50///         .join("vcard-personal-portfolio")
51///         .join("assets"),
52///     &SearchOptions::default(),
53///     &BuildFromPathOptions::default(),
54/// )?;
55///
56/// // group files into hashmap {pack_path: file}
57/// let files_by_path = files
58///     .into_vec()
59///     .into_iter()
60///     .map(|file_pack_path| (file_pack_path.pack_path, file_pack_path.file))
61///     .collect::<HashMap<_, _>>();
62///
63/// // verify necessary files were added
64/// assert!(files_by_path.contains_key("/css/style.css"));
65/// assert!(files_by_path.contains_key("/js/script.js"));
66/// assert!(!files_by_path.contains_key("/index.html"));
67/// #
68/// # Ok(())
69/// # }
70/// ```
71pub fn search(
72    path: &Path,
73    options: &SearchOptions,
74    file_build_options: &file::BuildFromPathOptions,
75) -> Result<Box<[file_pack_path::FilePackPath]>, Error> {
76    let file_paths = WalkDir::new(path)
77        .follow_links(options.follow_links)
78        .into_iter()
79        .map(|file_entry| {
80            // detect search errors
81            let file_entry = file_entry?;
82
83            // we are interested in files only
84            // if follow_links is true, this will be resolved as link target
85            if !file_entry.file_type().is_file() {
86                return Ok(None);
87            }
88
89            // build file
90            let file_pack_path = file_pack_path::FilePackPath::build_from_path(
91                file_entry.path(),
92                path,
93                file_build_options,
94            )
95            .with_context(|| file_entry.path().to_string_lossy().into_owned())?;
96
97            // yield for processing
98            Ok(Some(file_pack_path))
99        })
100        .filter_map(|entry_result| entry_result.transpose()) // strips Ok(None)
101        .collect::<Result<Box<[_]>, Error>>()?;
102
103    Ok(file_paths)
104}