2023-10-16 23:35:41 +05:30
|
|
|
// Copyright (C) 2022 Aravinth Manivannan <realaravinth@batsense.net>
|
|
|
|
// SPDX-FileCopyrightText: 2023 Aravinth Manivannan <realaravinth@batsense.net>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// SPDX-License-Identifier: MIT
|
2021-04-08 20:05:11 +05:30
|
|
|
|
2021-04-10 17:45:00 +05:30
|
|
|
//! Module describing file processor that changes filenames to setup cache-busting
|
|
|
|
//!
|
|
|
|
//! Run the following during build using `build.rs`:
|
|
|
|
//!
|
|
|
|
//! ```rust
|
|
|
|
//! use cache_buster::BusterBuilder;
|
|
|
|
//!
|
2021-07-05 14:46:56 +05:30
|
|
|
//! // note: add error checking yourself.
|
|
|
|
//! // println!("cargo:rustc-env=GIT_process={}", git_process);
|
|
|
|
//! let types = vec![
|
|
|
|
//! mime::IMAGE_PNG,
|
|
|
|
//! mime::IMAGE_SVG,
|
|
|
|
//! mime::IMAGE_JPEG,
|
|
|
|
//! mime::IMAGE_GIF,
|
|
|
|
//! ];
|
2021-04-10 17:45:00 +05:30
|
|
|
//!
|
2021-07-05 14:46:56 +05:30
|
|
|
//! let config = BusterBuilder::default()
|
|
|
|
//! .source("./dist")
|
|
|
|
//! .result("./prod")
|
|
|
|
//! .mime_types(types)
|
|
|
|
//! .follow_links(true)
|
|
|
|
//! .build()
|
|
|
|
//! .unwrap();
|
2021-04-10 17:45:00 +05:30
|
|
|
//!
|
2021-07-05 14:46:56 +05:30
|
|
|
//! config.process().unwrap();
|
2021-04-10 17:45:00 +05:30
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! There's a runtime component to this library which will let you read modified
|
|
|
|
//! filenames from within your program. See [Files]
|
|
|
|
|
2021-04-11 22:53:16 +05:30
|
|
|
use std::collections::HashMap;
|
2021-04-08 20:05:11 +05:30
|
|
|
use std::io::Error;
|
|
|
|
use std::path::Path;
|
|
|
|
use std::{fs, path::PathBuf};
|
|
|
|
|
|
|
|
use derive_builder::Builder;
|
2021-04-11 22:53:16 +05:30
|
|
|
use serde::{Deserialize, Serialize};
|
2021-04-08 20:05:11 +05:30
|
|
|
use walkdir::WalkDir;
|
|
|
|
|
2021-04-30 20:37:56 +05:30
|
|
|
use crate::*;
|
2021-04-08 20:45:38 +05:30
|
|
|
|
2021-07-07 17:55:57 +05:30
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
/// Items to avoid hash calculation.
|
|
|
|
///
|
|
|
|
/// This is useful when serving vendor static files which are interlinked, where chaing
|
|
|
|
/// file names should mean changing how the vendor files pulls its dependencies --- which are
|
|
|
|
/// beyond the abilities of `cache_buster`.
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use cache_buster::NoHashCategory;
|
|
|
|
///
|
|
|
|
/// let extensions = NoHashCategory::FileExtentions(vec!["wasm"]);
|
|
|
|
/// let files = NoHashCategory::FileExtentions(vec!["swagger-ui-bundle.js", "favicon-16x16.png"]);
|
|
|
|
/// ```
|
|
|
|
pub enum NoHashCategory<'a> {
|
|
|
|
/// vector of file extensions that should be avoided for hash processing
|
|
|
|
FileExtentions(Vec<&'a str>),
|
|
|
|
/// list of file paths that should be avoided for file processing
|
|
|
|
FilePaths(Vec<&'a str>),
|
|
|
|
}
|
|
|
|
|
2021-04-10 17:45:00 +05:30
|
|
|
/// Configuration for setting up cache-busting
|
2021-04-08 20:05:11 +05:30
|
|
|
#[derive(Debug, Clone, Builder)]
|
2021-07-05 14:39:35 +05:30
|
|
|
#[builder(build_fn(validate = "Self::validate"))]
|
|
|
|
pub struct Buster<'a> {
|
2021-04-10 17:45:00 +05:30
|
|
|
/// source directory
|
2021-04-08 20:05:11 +05:30
|
|
|
#[builder(setter(into))]
|
|
|
|
source: String,
|
2021-04-10 17:45:00 +05:30
|
|
|
/// mime_types for hashing
|
2021-07-05 15:24:56 +05:30
|
|
|
#[builder(setter(into, strip_option), default)]
|
|
|
|
mime_types: Option<Vec<mime::Mime>>,
|
2021-04-10 17:45:00 +05:30
|
|
|
/// directory for writing results
|
2021-04-08 20:05:11 +05:30
|
|
|
#[builder(setter(into))]
|
|
|
|
result: String,
|
2021-04-12 18:23:56 +05:30
|
|
|
#[builder(setter(into, strip_option), default)]
|
|
|
|
/// route prefixes
|
|
|
|
prefix: Option<String>,
|
2021-04-10 17:45:00 +05:30
|
|
|
/// follow symlinks?
|
2021-04-08 20:05:11 +05:30
|
|
|
follow_links: bool,
|
2021-07-05 14:39:35 +05:30
|
|
|
/// exclude these files for hashing.
|
|
|
|
/// They will be copied over without including a hash in the filename
|
|
|
|
/// Path should be relative to [self.source]
|
|
|
|
#[builder(default)]
|
2021-07-07 17:55:57 +05:30
|
|
|
no_hash: Vec<NoHashCategory<'a>>,
|
2021-04-08 20:05:11 +05:30
|
|
|
}
|
|
|
|
|
2021-07-05 14:39:35 +05:30
|
|
|
impl<'a> BusterBuilder<'a> {
|
|
|
|
fn validate(&self) -> Result<(), String> {
|
2021-07-07 17:55:57 +05:30
|
|
|
for no_hash_configs in self.no_hash.iter() {
|
|
|
|
for no_hash in no_hash_configs.iter() {
|
|
|
|
if let NoHashCategory::FilePaths(files) = no_hash {
|
|
|
|
for file in files.iter() {
|
|
|
|
if !Path::new(&self.source.as_ref().unwrap())
|
|
|
|
.join(file)
|
|
|
|
.exists()
|
|
|
|
{
|
|
|
|
return Err(format!("File {} doesn't exist", file));
|
|
|
|
}
|
|
|
|
}
|
2021-07-05 14:39:35 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Buster<'a> {
|
2021-04-10 17:45:00 +05:30
|
|
|
// creates base_dir to output files to
|
|
|
|
fn init(&self) -> Result<(), Error> {
|
2021-04-08 20:05:11 +05:30
|
|
|
let res = Path::new(&self.result);
|
2021-07-11 21:48:30 +05:30
|
|
|
println!("cargo:rerun-if-changed={}", self.source);
|
2021-04-08 20:05:11 +05:30
|
|
|
if res.exists() {
|
|
|
|
fs::remove_dir_all(&self.result).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
fs::create_dir(&self.result).unwrap();
|
|
|
|
self.create_dir_structure(Path::new(&self.source))?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-04-09 12:20:27 +05:30
|
|
|
fn hasher(payload: &[u8]) -> String {
|
2021-04-08 20:05:11 +05:30
|
|
|
use data_encoding::HEXUPPER;
|
|
|
|
use sha2::{Digest, Sha256};
|
|
|
|
let mut hasher = Sha256::new();
|
|
|
|
hasher.update(payload);
|
|
|
|
HEXUPPER.encode(&hasher.finalize())
|
|
|
|
}
|
|
|
|
|
2021-04-10 17:45:00 +05:30
|
|
|
/// Processes files.
|
|
|
|
///
|
2021-04-30 20:37:56 +05:30
|
|
|
/// Panics when a weird MIME is encountered.
|
2021-04-11 22:53:16 +05:30
|
|
|
pub fn process(&self) -> Result<(), Error> {
|
2021-04-10 17:45:00 +05:30
|
|
|
// panics when mimetypes are detected. This way you'll know which files are ignored
|
|
|
|
// from processing
|
|
|
|
|
|
|
|
self.init()?;
|
2021-04-09 14:05:27 +05:30
|
|
|
let mut file_map: Files = Files::new(&self.result);
|
2021-04-08 20:05:11 +05:30
|
|
|
|
2021-07-05 15:44:34 +05:30
|
|
|
let mut process_worker = |path: &Path| {
|
2021-12-17 11:54:34 +05:30
|
|
|
let contents = Self::read_to_string(path).unwrap();
|
2021-07-05 15:44:34 +05:30
|
|
|
let hash = Self::hasher(&contents);
|
2021-07-07 17:55:57 +05:30
|
|
|
|
|
|
|
let get_name = |no_hash: bool| -> String {
|
|
|
|
if no_hash {
|
|
|
|
format!(
|
|
|
|
"{}.{}",
|
|
|
|
path.file_stem().unwrap().to_str().unwrap(),
|
|
|
|
path.extension().unwrap().to_str().unwrap()
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
format!(
|
|
|
|
"{}.{}.{}",
|
|
|
|
path.file_stem().unwrap().to_str().unwrap(),
|
|
|
|
hash,
|
|
|
|
path.extension().unwrap().to_str().unwrap()
|
|
|
|
)
|
|
|
|
}
|
2021-07-05 15:44:34 +05:30
|
|
|
};
|
2021-07-07 17:55:57 +05:30
|
|
|
|
|
|
|
let no_hash_status = self.no_hash.iter().any(|no_hash| {
|
|
|
|
match no_hash {
|
|
|
|
NoHashCategory::FilePaths(paths) => {
|
|
|
|
let no_hash_status = paths
|
|
|
|
.iter()
|
|
|
|
.any(|file_path| Path::new(&self.source).join(&file_path) == path);
|
|
|
|
no_hash_status
|
|
|
|
}
|
|
|
|
NoHashCategory::FileExtentions(extensions) => {
|
|
|
|
let mut no_hash_status = false;
|
|
|
|
if let Some(cur_extention) = path.extension() {
|
|
|
|
// .unwrap().to_str().unwrap();
|
|
|
|
if let Some(cur_extention) = cur_extention.to_str() {
|
|
|
|
no_hash_status = extensions.iter().any(|ext| &cur_extention == ext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
no_hash_status
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let new_name = get_name(no_hash_status);
|
|
|
|
|
2021-07-05 15:44:34 +05:30
|
|
|
self.copy(path, &new_name);
|
2021-12-17 11:54:34 +05:30
|
|
|
let (source, destination) = self.gen_map(path, &new_name);
|
2021-07-05 15:44:34 +05:30
|
|
|
let _ = file_map.add(
|
|
|
|
source.to_str().unwrap().into(),
|
|
|
|
destination.to_str().unwrap().into(),
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2021-04-08 20:05:11 +05:30
|
|
|
for entry in WalkDir::new(&self.source)
|
|
|
|
.follow_links(self.follow_links)
|
|
|
|
.into_iter()
|
|
|
|
{
|
|
|
|
let entry = entry?;
|
|
|
|
|
|
|
|
let path = entry.path();
|
2021-07-05 15:24:56 +05:30
|
|
|
if !path.is_dir() {
|
2021-04-08 20:05:11 +05:30
|
|
|
let path = Path::new(&path);
|
|
|
|
|
2021-07-05 15:24:56 +05:30
|
|
|
match self.mime_types.as_ref() {
|
|
|
|
Some(mime_types) => {
|
|
|
|
for mime_type in mime_types.iter() {
|
|
|
|
let file_mime =
|
|
|
|
mime_guess::from_path(path).first().unwrap_or_else(|| {
|
|
|
|
panic!("couldn't resolve MIME for file: {:?}", &path)
|
|
|
|
});
|
|
|
|
if &file_mime == mime_type {
|
2021-12-17 11:54:34 +05:30
|
|
|
process_worker(path);
|
2021-07-05 15:24:56 +05:30
|
|
|
}
|
|
|
|
}
|
2021-04-08 20:05:11 +05:30
|
|
|
}
|
2021-12-17 11:54:34 +05:30
|
|
|
None => process_worker(path),
|
2021-04-08 20:05:11 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-11 22:53:16 +05:30
|
|
|
file_map.to_env();
|
|
|
|
Ok(())
|
2021-04-08 20:05:11 +05:30
|
|
|
}
|
|
|
|
|
2021-04-10 17:45:00 +05:30
|
|
|
// helper fn to read file to string
|
2021-04-09 12:20:27 +05:30
|
|
|
fn read_to_string(path: &Path) -> Result<Vec<u8>, Error> {
|
2021-04-08 20:05:11 +05:30
|
|
|
use std::fs::File;
|
2021-04-09 12:20:27 +05:30
|
|
|
use std::io::Read;
|
2021-04-08 20:05:11 +05:30
|
|
|
|
2021-04-09 12:20:27 +05:30
|
|
|
let mut file_content = Vec::new();
|
|
|
|
let mut file = File::open(path)?;
|
|
|
|
file.read_to_end(&mut file_content).expect("Unable to read");
|
|
|
|
Ok(file_content)
|
2021-04-08 20:05:11 +05:30
|
|
|
}
|
|
|
|
|
2021-04-10 17:45:00 +05:30
|
|
|
// helper fn to generate filemap
|
2021-07-05 14:39:35 +05:30
|
|
|
fn gen_map<'b>(&self, source: &'b Path, name: &str) -> (&'b Path, PathBuf) {
|
2021-04-08 20:05:11 +05:30
|
|
|
let rel_location = source.strip_prefix(&self.source).unwrap().parent().unwrap();
|
2021-04-12 18:23:56 +05:30
|
|
|
if let Some(prefix) = &self.prefix {
|
|
|
|
//panic!("{}", &prefix);
|
|
|
|
let mut result = self.result.as_str();
|
2021-07-05 14:46:56 +05:30
|
|
|
if result.starts_with('/') {
|
2021-04-12 18:23:56 +05:30
|
|
|
result = &self.result[1..];
|
|
|
|
}
|
|
|
|
let destination = Path::new(prefix)
|
2021-04-30 20:37:56 +05:30
|
|
|
.join(&result)
|
2021-04-12 18:23:56 +05:30
|
|
|
.join(rel_location)
|
|
|
|
.join(name);
|
|
|
|
|
2021-07-05 14:46:56 +05:30
|
|
|
(source, destination)
|
2021-04-12 18:23:56 +05:30
|
|
|
} else {
|
|
|
|
let destination = Path::new(&self.result).join(rel_location).join(name);
|
2021-07-05 14:46:56 +05:30
|
|
|
(source, destination)
|
2021-04-12 18:23:56 +05:30
|
|
|
}
|
2021-04-08 20:05:11 +05:30
|
|
|
}
|
|
|
|
|
2021-04-10 17:45:00 +05:30
|
|
|
// helper fn to copy files
|
2021-04-08 20:05:11 +05:30
|
|
|
fn copy(&self, source: &Path, name: &str) {
|
|
|
|
let rel_location = source.strip_prefix(&self.source).unwrap().parent().unwrap();
|
|
|
|
let destination = Path::new(&self.result).join(rel_location).join(name);
|
|
|
|
fs::copy(source, &destination).unwrap();
|
|
|
|
}
|
|
|
|
|
2021-04-10 17:45:00 +05:30
|
|
|
// helper fn to create directory structure in self.base_dir
|
2021-04-08 20:05:11 +05:30
|
|
|
fn create_dir_structure(&self, path: &Path) -> Result<(), Error> {
|
|
|
|
for entry in WalkDir::new(&path)
|
|
|
|
.follow_links(self.follow_links)
|
|
|
|
.into_iter()
|
|
|
|
{
|
|
|
|
let entry = entry?;
|
|
|
|
let entry_path = entry.path();
|
|
|
|
let entry_path = Path::new(&entry_path);
|
|
|
|
|
|
|
|
if entry_path.is_dir() && path != entry_path {
|
2021-12-17 11:54:34 +05:30
|
|
|
Self::create_dir_structure(self, entry_path)?;
|
2021-07-05 14:46:56 +05:30
|
|
|
} else if entry_path.is_dir() {
|
|
|
|
let rel_location = entry_path.strip_prefix(&self.source).unwrap();
|
|
|
|
let destination = Path::new(&self.result).join(rel_location);
|
|
|
|
if !destination.exists() {
|
|
|
|
fs::create_dir(destination)?
|
2021-04-08 20:05:11 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2021-04-11 22:53:16 +05:30
|
|
|
/// Filemap struct
|
|
|
|
///
|
|
|
|
/// maps original names to generated names
|
|
|
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
|
|
|
struct Files {
|
|
|
|
/// filemap<original-path, modified-path>
|
|
|
|
pub map: HashMap<String, String>,
|
|
|
|
base_dir: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Files {
|
|
|
|
/// Initialize map
|
|
|
|
fn new(base_dir: &str) -> Self {
|
|
|
|
Files {
|
|
|
|
map: HashMap::default(),
|
|
|
|
base_dir: base_dir.into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create file map: map original path to modified paths
|
|
|
|
fn add(&mut self, k: String, v: String) -> Result<(), &'static str> {
|
2021-07-05 14:46:56 +05:30
|
|
|
if let std::collections::hash_map::Entry::Vacant(e) = self.map.entry(k) {
|
|
|
|
e.insert(v);
|
2021-04-11 22:53:16 +05:30
|
|
|
Ok(())
|
2021-07-05 14:46:56 +05:30
|
|
|
} else {
|
|
|
|
Err("key exists")
|
2021-04-11 22:53:16 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This crate uses compile-time environment variables to transfer
|
|
|
|
/// data to the main program. This funtction sets that variable
|
|
|
|
fn to_env(&self) {
|
2021-04-30 20:37:56 +05:30
|
|
|
let json = serde_json::to_string(&self).unwrap();
|
|
|
|
let res = Path::new(CACHE_BUSTER_DATA_FILE);
|
|
|
|
if res.exists() {
|
|
|
|
fs::remove_file(&res).unwrap();
|
|
|
|
}
|
|
|
|
fs::write(CACHE_BUSTER_DATA_FILE, &json).unwrap();
|
2021-04-11 22:53:16 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
/// Load filemap in main program. Should be called from main program
|
|
|
|
fn load() -> Self {
|
2021-04-30 20:37:56 +05:30
|
|
|
let map = fs::read_to_string(CACHE_BUSTER_DATA_FILE).unwrap();
|
|
|
|
let res: Files = serde_json::from_str(&map).unwrap();
|
2021-04-11 22:53:16 +05:30
|
|
|
res
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-08 20:05:11 +05:30
|
|
|
#[cfg(test)]
|
2021-04-08 22:00:41 +05:30
|
|
|
pub mod tests {
|
2021-04-08 20:05:11 +05:30
|
|
|
use super::*;
|
|
|
|
|
2021-12-17 11:54:34 +05:30
|
|
|
pub fn cleanup(config: &Buster<'_>) {
|
2021-07-05 16:59:53 +05:30
|
|
|
let _ = fs::remove_dir_all(&config.result);
|
2021-04-30 20:37:56 +05:30
|
|
|
delete_file();
|
2021-07-05 16:59:53 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
pub fn delete_file() {
|
|
|
|
let _ = fs::remove_file(&CACHE_BUSTER_DATA_FILE);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn no_hash_validation_works() {
|
2021-04-08 20:05:11 +05:30
|
|
|
let types = vec![
|
|
|
|
mime::IMAGE_PNG,
|
|
|
|
mime::IMAGE_SVG,
|
|
|
|
mime::IMAGE_JPEG,
|
|
|
|
mime::IMAGE_GIF,
|
|
|
|
];
|
|
|
|
|
2021-07-07 17:55:57 +05:30
|
|
|
let no_hash =
|
|
|
|
NoHashCategory::FilePaths(vec!["bbell.svg", "eye.svg", "a/b/c/d/s/d/svg/10.svg"]);
|
|
|
|
|
2021-07-05 16:59:53 +05:30
|
|
|
assert!(BusterBuilder::default()
|
2021-04-08 20:05:11 +05:30
|
|
|
.source("./dist")
|
2021-07-05 16:59:53 +05:30
|
|
|
.result("/tmp/prod2i")
|
2021-04-08 20:05:11 +05:30
|
|
|
.mime_types(types)
|
|
|
|
.follow_links(true)
|
2021-07-05 16:59:53 +05:30
|
|
|
.prefix("/test")
|
2021-07-07 17:55:57 +05:30
|
|
|
.no_hash(vec![no_hash.clone()])
|
2021-04-08 20:05:11 +05:30
|
|
|
.build()
|
2021-07-05 16:59:53 +05:30
|
|
|
.is_err())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn no_specific_mime() {
|
|
|
|
delete_file();
|
2021-07-07 17:55:57 +05:30
|
|
|
//use std::{thread, time};
|
2021-07-05 16:59:53 +05:30
|
|
|
|
2021-07-07 17:55:57 +05:30
|
|
|
//let sleep = time::Duration::from_secs(4);
|
2021-07-05 16:59:53 +05:30
|
|
|
|
2021-07-07 17:55:57 +05:30
|
|
|
//thread::sleep(sleep);
|
2021-04-08 20:05:11 +05:30
|
|
|
|
2021-07-05 16:59:53 +05:30
|
|
|
const WASM: &str = "858fd6c482cc75111d54.module.wasm";
|
2021-07-07 17:55:57 +05:30
|
|
|
let no_hash_files = vec![WASM, "bell.svg", "eye.svg", "a/b/c/d/s/d/svg/10.svg"];
|
|
|
|
let no_hash = NoHashCategory::FilePaths(no_hash_files.clone());
|
2021-07-05 16:59:53 +05:30
|
|
|
let config = BusterBuilder::default()
|
|
|
|
.source("./dist")
|
|
|
|
.result("/tmp/prod2ii")
|
|
|
|
.follow_links(true)
|
2021-07-07 17:55:57 +05:30
|
|
|
.no_hash(vec![no_hash.clone()])
|
2021-07-05 16:59:53 +05:30
|
|
|
.build()
|
|
|
|
.unwrap();
|
2021-04-11 22:53:16 +05:30
|
|
|
config.process().unwrap();
|
2021-07-05 16:59:53 +05:30
|
|
|
let files = Files::load();
|
2021-04-08 20:05:11 +05:30
|
|
|
|
2021-07-05 16:59:53 +05:30
|
|
|
let no_hash_file = Path::new(&config.result).join(WASM);
|
|
|
|
assert!(files.map.iter().any(|(k, v)| {
|
|
|
|
let source = Path::new(&config.source).join(k);
|
|
|
|
let dest = Path::new(&v);
|
|
|
|
dest.file_name() == no_hash_file.file_name()
|
|
|
|
&& dest.exists()
|
|
|
|
&& source.file_name() == dest.file_name()
|
|
|
|
}));
|
|
|
|
|
2021-07-07 17:55:57 +05:30
|
|
|
no_hash_files.iter().for_each(|file| {
|
2021-07-05 16:59:53 +05:30
|
|
|
assert!(files.map.iter().any(|(k, v)| {
|
|
|
|
let source = Path::new(k);
|
|
|
|
let dest = Path::new(&v);
|
|
|
|
let no_hash = Path::new(file);
|
2021-07-11 21:48:30 +05:30
|
|
|
source == Path::new(&config.source).join(file)
|
2021-07-05 16:59:53 +05:30
|
|
|
&& dest.exists()
|
|
|
|
&& no_hash.file_name() == dest.file_name()
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
|
|
|
for (k, v) in files.map.iter() {
|
2021-04-08 20:05:11 +05:30
|
|
|
let src = Path::new(&k);
|
|
|
|
let dest = Path::new(&v);
|
|
|
|
|
|
|
|
assert_eq!(src.exists(), dest.exists());
|
|
|
|
}
|
2021-04-12 18:23:56 +05:30
|
|
|
|
2021-04-08 22:00:41 +05:30
|
|
|
cleanup(&config);
|
2021-04-08 20:05:11 +05:30
|
|
|
}
|
2021-04-08 20:14:53 +05:30
|
|
|
|
2021-04-12 18:23:56 +05:30
|
|
|
fn prefix_works() {
|
2021-04-30 20:37:56 +05:30
|
|
|
delete_file();
|
2021-04-12 18:23:56 +05:30
|
|
|
let types = vec![
|
|
|
|
mime::IMAGE_PNG,
|
|
|
|
mime::IMAGE_SVG,
|
|
|
|
mime::IMAGE_JPEG,
|
|
|
|
mime::IMAGE_GIF,
|
|
|
|
];
|
|
|
|
|
|
|
|
let config = BusterBuilder::default()
|
|
|
|
.source("./dist")
|
2021-07-05 16:59:53 +05:30
|
|
|
.result("/tmp/prod2i")
|
2021-04-12 18:23:56 +05:30
|
|
|
.mime_types(types)
|
|
|
|
.follow_links(true)
|
|
|
|
.prefix("/test")
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
config.process().unwrap();
|
|
|
|
let mut files = Files::load();
|
|
|
|
|
|
|
|
if let Some(prefix) = &config.prefix {
|
|
|
|
for (k, v) in files.map.drain() {
|
|
|
|
let src = Path::new(&k);
|
|
|
|
let dest = Path::new(&v[prefix.len()..]);
|
|
|
|
|
|
|
|
assert_eq!(src.exists(), dest.exists());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup(&config);
|
|
|
|
}
|
2021-04-30 20:37:56 +05:30
|
|
|
|
2021-07-07 17:55:57 +05:30
|
|
|
fn no_hash_extension_works() {
|
|
|
|
delete_file();
|
|
|
|
use std::{thread, time};
|
|
|
|
|
|
|
|
let sleep = time::Duration::from_secs(4);
|
|
|
|
const APPLICATION_WASM: &str = "wasm";
|
|
|
|
const WASM: &str = "858fd6c482cc75111d54.module.wasm";
|
|
|
|
|
|
|
|
thread::sleep(sleep);
|
|
|
|
|
|
|
|
let no_hash_extensions = vec![APPLICATION_WASM];
|
|
|
|
let no_hash_ext = NoHashCategory::FileExtentions(no_hash_extensions.clone());
|
|
|
|
|
|
|
|
let no_hash_paths = vec!["bell.svg", "eye.svg", "a/b/c/d/s/d/svg/10.svg"];
|
|
|
|
let no_hash_cat = NoHashCategory::FilePaths(no_hash_paths.clone());
|
|
|
|
let no_hash = vec![no_hash_cat, no_hash_ext];
|
|
|
|
|
|
|
|
let config = BusterBuilder::default()
|
|
|
|
.source("./dist")
|
|
|
|
.result("/tmp/prodnohashextension")
|
|
|
|
.follow_links(true)
|
|
|
|
.no_hash(no_hash.clone())
|
|
|
|
.build()
|
|
|
|
.unwrap();
|
|
|
|
config.process().unwrap();
|
|
|
|
let files = Files::load();
|
|
|
|
|
2021-07-11 21:48:30 +05:30
|
|
|
assert!(files.map.iter().any(|(_k, v)| {
|
2021-07-07 17:55:57 +05:30
|
|
|
let dest = Path::new(&v);
|
|
|
|
dest.extension().unwrap().to_str().unwrap() == APPLICATION_WASM && dest.exists()
|
|
|
|
}));
|
|
|
|
|
|
|
|
let no_hash_file = Path::new(&config.result).join(WASM);
|
|
|
|
assert!(files.map.iter().any(|(k, v)| {
|
|
|
|
let source = Path::new(&config.source).join(k);
|
|
|
|
let dest = Path::new(&v);
|
|
|
|
dest.file_name() == no_hash_file.file_name()
|
|
|
|
&& dest.exists()
|
|
|
|
&& source.file_name() == dest.file_name()
|
|
|
|
}));
|
|
|
|
|
|
|
|
no_hash_paths.iter().for_each(|file| {
|
|
|
|
assert!(files.map.iter().any(|(k, v)| {
|
|
|
|
let source = Path::new(k);
|
|
|
|
let dest = Path::new(&v);
|
|
|
|
let no_hash = Path::new(file);
|
2021-07-11 21:48:30 +05:30
|
|
|
source == Path::new(&config.source).join(file)
|
2021-07-07 17:55:57 +05:30
|
|
|
&& dest.exists()
|
|
|
|
&& no_hash.file_name() == dest.file_name()
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
|
|
|
for (k, v) in files.map.iter() {
|
|
|
|
let src = Path::new(&k);
|
|
|
|
let dest = Path::new(&v);
|
|
|
|
|
|
|
|
assert_eq!(src.exists(), dest.exists());
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup(&config);
|
|
|
|
}
|
|
|
|
|
2021-04-30 20:37:56 +05:30
|
|
|
pub fn runner() {
|
|
|
|
prefix_works();
|
2021-07-05 16:59:53 +05:30
|
|
|
no_specific_mime();
|
2021-07-07 17:55:57 +05:30
|
|
|
no_hash_extension_works();
|
2021-04-30 20:37:56 +05:30
|
|
|
}
|
2021-04-08 20:05:11 +05:30
|
|
|
}
|