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}