write filemap to file
This commit is contained in:
parent
d5593b2db6
commit
3c3b61aebf
9 changed files with 87 additions and 116 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,3 +3,4 @@ prod
|
||||||
tarpaulin-report.html
|
tarpaulin-report.html
|
||||||
actix-example/target
|
actix-example/target
|
||||||
actix-example/prod
|
actix-example/prod
|
||||||
|
prod56/
|
||||||
|
|
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -1,17 +1,32 @@
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
|
### Changed:
|
||||||
|
|
||||||
|
- `Files::new()` takes a `&str`: Earlier versions were using
|
||||||
|
environment variables to pass filemap information from `build.rs`
|
||||||
|
component to program code but this proved to be unreliable. Starting
|
||||||
|
with `0.2.0`, `cache_buster` will write filemap to
|
||||||
|
`CACHE_BUSTER_DATA_FILE`(`./src/cache_buster_data.json`) and the user
|
||||||
|
is requested to read and pass the value to `File::new()`
|
||||||
|
|
||||||
## 0.1.1
|
## 0.1.1
|
||||||
|
|
||||||
### Added:
|
### Added:
|
||||||
|
|
||||||
- Optional route prefix to `Processor`
|
- Optional route prefix to `Processor`
|
||||||
|
|
||||||
### Changed:
|
### Changed:
|
||||||
|
|
||||||
- `Files::load()` became `Files::new()`
|
- `Files::load()` became `Files::new()`
|
||||||
|
|
||||||
### Removed:
|
### Removed:
|
||||||
|
|
||||||
- Some methods on `Files` were for internal use only but they had a
|
- Some methods on `Files` were for internal use only but they had a
|
||||||
public API, they were modified to private.
|
public API, they were modified to private.
|
||||||
|
|
||||||
## 0.1.0
|
## 0.1.0
|
||||||
|
|
||||||
### Added:
|
### Added:
|
||||||
|
|
||||||
- `SHA-256`-based cache-buster
|
- `SHA-256`-based cache-buster
|
||||||
- runtime filemap loading
|
- runtime filemap loading
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -21,7 +21,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cache-buster"
|
name = "cache-buster"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"derive_builder",
|
"derive_builder",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "cache-buster"
|
name = "cache-buster"
|
||||||
version = "0.1.1"
|
version = "0.2.0"
|
||||||
authors = ["realaravinth <realaravinth@batsense.net>"]
|
authors = ["realaravinth <realaravinth@batsense.net>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
@ -22,7 +22,7 @@ mime = "0.3.16"
|
||||||
|
|
||||||
sha2 = "0.9.3"
|
sha2 = "0.9.3"
|
||||||
|
|
||||||
derive_builder = "0.10.0"
|
derive_builder = "0.10.2"
|
||||||
|
|
||||||
data-encoding = "2.3.2"
|
data-encoding = "2.3.2"
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
|
1
actix-example/src/cache_buster_data.json
Normal file
1
actix-example/src/cache_buster_data.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,8 @@
|
||||||
use cache_buster::Files;
|
use cache_buster::Files;
|
||||||
|
use cache_buster::CACHE_BUSTER_DATA_FILE;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let files = Files::new();
|
let files = Files::new(CACHE_BUSTER_DATA_FILE);
|
||||||
|
|
||||||
assert!(get_full_path_runner("../dist/log-out.svg", &files));
|
assert!(get_full_path_runner("../dist/log-out.svg", &files));
|
||||||
assert!(get_full_path_runner(
|
assert!(get_full_path_runner(
|
||||||
|
|
|
@ -10,19 +10,17 @@
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use cache_buster::Files;
|
//! use cache_buster::Files;
|
||||||
|
//! use cache_buster::CACHE_BUSTER_DATA_FILE;
|
||||||
//!
|
//!
|
||||||
//! fn main(){
|
//! fn main(){
|
||||||
//! let files = Files::new();
|
//! let files = Files::new(CACHE_BUSTER_DATA_FILE);
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
const ENV_VAR_NAME: &str = "CACHE_BUSTER_FILE_MAP";
|
|
||||||
|
|
||||||
/// Filemap struct
|
/// Filemap struct
|
||||||
///
|
///
|
||||||
/// maps original names to generated names
|
/// maps original names to generated names
|
||||||
|
@ -35,10 +33,8 @@ pub struct Files {
|
||||||
|
|
||||||
impl Files {
|
impl Files {
|
||||||
/// Load filemap in main program. Should be called from main program
|
/// Load filemap in main program. Should be called from main program
|
||||||
pub fn new() -> Self {
|
pub fn new(map: &str) -> Self {
|
||||||
let env = env::var(ENV_VAR_NAME)
|
let res: Files = serde_json::from_str(&map).unwrap();
|
||||||
.expect("unable to read env var, might be a bug in lib. Please report on GitHub");
|
|
||||||
let res: Files = serde_json::from_str(&env).unwrap();
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +44,8 @@ impl Files {
|
||||||
/// output `/test.randomhash.svg`. For full path, see [get_full_path][Self::get_full_path].
|
/// output `/test.randomhash.svg`. For full path, see [get_full_path][Self::get_full_path].
|
||||||
pub fn get<'a>(&'a self, path: &'a str) -> Option<&'a str> {
|
pub fn get<'a>(&'a self, path: &'a str) -> Option<&'a str> {
|
||||||
if let Some(path) = self.map.get(path) {
|
if let Some(path) = self.map.get(path) {
|
||||||
Some(&path[self.base_dir.len()..])
|
//Some(&path[self.base_dir.len()..])
|
||||||
|
Some(&path)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -65,14 +62,17 @@ impl Files {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::processor::tests::cleanup;
|
use std::fs;
|
||||||
|
|
||||||
|
use crate::processor::tests::{cleanup, delete_file, runner as processor_runner};
|
||||||
use crate::processor::*;
|
use crate::processor::*;
|
||||||
|
use crate::CACHE_BUSTER_DATA_FILE;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_full_path_works() {
|
fn get_full_path_works() {
|
||||||
|
delete_file();
|
||||||
let types = vec![
|
let types = vec![
|
||||||
mime::IMAGE_PNG,
|
mime::IMAGE_PNG,
|
||||||
mime::IMAGE_SVG,
|
mime::IMAGE_SVG,
|
||||||
|
@ -82,7 +82,7 @@ mod tests {
|
||||||
|
|
||||||
let config = BusterBuilder::default()
|
let config = BusterBuilder::default()
|
||||||
.source("./dist")
|
.source("./dist")
|
||||||
.result("/tmp/prod2")
|
.result("/tmp/prodsd2")
|
||||||
.mime_types(types)
|
.mime_types(types)
|
||||||
.copy(true)
|
.copy(true)
|
||||||
.follow_links(true)
|
.follow_links(true)
|
||||||
|
@ -91,7 +91,9 @@ mod tests {
|
||||||
|
|
||||||
config.process().unwrap();
|
config.process().unwrap();
|
||||||
|
|
||||||
let files = Files::new();
|
let map = fs::read_to_string(CACHE_BUSTER_DATA_FILE).unwrap();
|
||||||
|
let files = Files::new(&map);
|
||||||
|
|
||||||
assert!(get_full_path_runner("./dist/log-out.svg", &files));
|
assert!(get_full_path_runner("./dist/log-out.svg", &files));
|
||||||
assert!(get_full_path_runner(
|
assert!(get_full_path_runner(
|
||||||
"./dist/a/b/c/d/s/d/svg/credit-card.svg",
|
"./dist/a/b/c/d/s/d/svg/credit-card.svg",
|
||||||
|
@ -114,8 +116,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_works() {
|
fn get_works() {
|
||||||
|
delete_file();
|
||||||
let types = vec![
|
let types = vec![
|
||||||
mime::IMAGE_PNG,
|
mime::IMAGE_PNG,
|
||||||
mime::IMAGE_SVG,
|
mime::IMAGE_SVG,
|
||||||
|
@ -134,7 +136,8 @@ mod tests {
|
||||||
|
|
||||||
config.process().unwrap();
|
config.process().unwrap();
|
||||||
|
|
||||||
let files = Files::new();
|
let map = fs::read_to_string(CACHE_BUSTER_DATA_FILE).unwrap();
|
||||||
|
let files = Files::new(&map);
|
||||||
|
|
||||||
assert!(get_runner("./dist/log-out.svg", &files));
|
assert!(get_runner("./dist/log-out.svg", &files));
|
||||||
assert!(get_runner("./dist/a/b/c/d/s/d/svg/credit-card.svg", &files));
|
assert!(get_runner("./dist/a/b/c/d/s/d/svg/credit-card.svg", &files));
|
||||||
|
@ -146,10 +149,19 @@ mod tests {
|
||||||
|
|
||||||
fn get_runner(path: &str, files: &Files) -> bool {
|
fn get_runner(path: &str, files: &Files) -> bool {
|
||||||
if let Some(file) = files.get(path) {
|
if let Some(file) = files.get(path) {
|
||||||
let path = Path::new(&files.base_dir).join(&file[1..]);
|
// let path = Path::new(&files.base_dir).join(&file[1..]);
|
||||||
|
println!("{}", &file);
|
||||||
|
let path = Path::new(&file);
|
||||||
path.exists()
|
path.exists()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn runner() {
|
||||||
|
get_works();
|
||||||
|
get_full_path_works();
|
||||||
|
processor_runner();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,5 +36,6 @@ pub use processor::BusterBuilder;
|
||||||
pub mod filemap;
|
pub mod filemap;
|
||||||
pub use filemap::Files;
|
pub use filemap::Files;
|
||||||
|
|
||||||
/// env var to which filemap is written during compilation
|
/// file to which filemap is written during compilation
|
||||||
pub const ENV_VAR_NAME: &str = "CACHE_BUSTER_FILE_MAP";
|
/// include this to `.gitignore`
|
||||||
|
pub const CACHE_BUSTER_DATA_FILE: &str = "./src/cache_buster_data.json";
|
||||||
|
|
128
src/processor.rs
128
src/processor.rs
|
@ -47,7 +47,7 @@ use derive_builder::Builder;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::ENV_VAR_NAME;
|
use crate::*;
|
||||||
|
|
||||||
/// Configuration for setting up cache-busting
|
/// Configuration for setting up cache-busting
|
||||||
#[derive(Debug, Clone, Builder)]
|
#[derive(Debug, Clone, Builder)]
|
||||||
|
@ -77,7 +77,6 @@ impl Buster {
|
||||||
fs::remove_dir_all(&self.result).unwrap();
|
fs::remove_dir_all(&self.result).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{}", &self.result);
|
|
||||||
fs::create_dir(&self.result).unwrap();
|
fs::create_dir(&self.result).unwrap();
|
||||||
self.create_dir_structure(Path::new(&self.source))?;
|
self.create_dir_structure(Path::new(&self.source))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -93,53 +92,7 @@ impl Buster {
|
||||||
|
|
||||||
/// Processes files.
|
/// Processes files.
|
||||||
///
|
///
|
||||||
/// If MIME types are uncommon, then use this funtion
|
/// Panics when a weird MIME is encountered.
|
||||||
/// as it won't panic when a weird MIM is encountered.
|
|
||||||
///
|
|
||||||
/// Otherwise, use [process][Self::process]
|
|
||||||
///
|
|
||||||
/// Note: it omits processing uncommon MIME types
|
|
||||||
pub fn try_process(&self) -> Result<(), Error> {
|
|
||||||
self.init()?;
|
|
||||||
let mut file_map: Files = Files::new(&self.result);
|
|
||||||
for entry in WalkDir::new(&self.source)
|
|
||||||
.follow_links(self.follow_links)
|
|
||||||
.into_iter()
|
|
||||||
{
|
|
||||||
let entry = entry?;
|
|
||||||
let path = entry.path();
|
|
||||||
let path = Path::new(&path);
|
|
||||||
|
|
||||||
for mime_type in self.mime_types.iter() {
|
|
||||||
if let Some(file_mime) = mime_guess::from_path(path).first() {
|
|
||||||
if &file_mime == mime_type {
|
|
||||||
let contents = Self::read_to_string(&path).unwrap();
|
|
||||||
let hash = Self::hasher(&contents);
|
|
||||||
let new_name = format!(
|
|
||||||
"{}.{}.{}",
|
|
||||||
path.file_stem().unwrap().to_str().unwrap(),
|
|
||||||
hash,
|
|
||||||
path.extension().unwrap().to_str().unwrap()
|
|
||||||
);
|
|
||||||
self.copy(path, &new_name);
|
|
||||||
|
|
||||||
let (source, destination) = self.gen_map(path, &&new_name);
|
|
||||||
let _ = file_map.add(
|
|
||||||
source.to_str().unwrap().into(),
|
|
||||||
destination.to_str().unwrap().into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
file_map.to_env();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Processes files.
|
|
||||||
///
|
|
||||||
/// If MIME types are common, then use this funtion
|
|
||||||
/// as it will panic when a weird MIM is encountered.
|
|
||||||
pub fn process(&self) -> Result<(), Error> {
|
pub fn process(&self) -> Result<(), Error> {
|
||||||
// panics when mimetypes are detected. This way you'll know which files are ignored
|
// panics when mimetypes are detected. This way you'll know which files are ignored
|
||||||
// from processing
|
// from processing
|
||||||
|
@ -206,7 +159,7 @@ impl Buster {
|
||||||
result = &self.result[1..];
|
result = &self.result[1..];
|
||||||
}
|
}
|
||||||
let destination = Path::new(prefix)
|
let destination = Path::new(prefix)
|
||||||
.join(&self.result[1..])
|
.join(&result)
|
||||||
.join(rel_location)
|
.join(rel_location)
|
||||||
.join(name);
|
.join(name);
|
||||||
|
|
||||||
|
@ -281,25 +234,33 @@ impl Files {
|
||||||
/// This crate uses compile-time environment variables to transfer
|
/// This crate uses compile-time environment variables to transfer
|
||||||
/// data to the main program. This funtction sets that variable
|
/// data to the main program. This funtction sets that variable
|
||||||
fn to_env(&self) {
|
fn to_env(&self) {
|
||||||
println!(
|
let json = serde_json::to_string(&self).unwrap();
|
||||||
"cargo:rustc-env={}={}",
|
// println!("cargo:rustc-env={}={}", ENV_VAR_NAME, json);
|
||||||
ENV_VAR_NAME,
|
let res = Path::new(CACHE_BUSTER_DATA_FILE);
|
||||||
serde_json::to_string(&self).unwrap()
|
if res.exists() {
|
||||||
);
|
fs::remove_file(&res).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// const PREFIX: &str = r##"pub const FILE_MAP: &str = r#" "##;
|
||||||
|
// const POSTFIX: &str = r##""#;"##;
|
||||||
|
|
||||||
|
// let content = format!("#[allow(dead_code)]\n{}{}{}", &PREFIX, &json, &POSTFIX);
|
||||||
|
|
||||||
|
// fs::write(CACHE_BUSTER_DATA_FILE, content).unwrap();
|
||||||
|
fs::write(CACHE_BUSTER_DATA_FILE, &json).unwrap();
|
||||||
|
|
||||||
// needed for testing load()
|
// needed for testing load()
|
||||||
// if the above statement fails(println), then something's broken
|
// if the above statement fails(println), then something's broken
|
||||||
// with the rust compiler. So not really worried about that.
|
// with the rust compiler. So not really worried about that.
|
||||||
#[cfg(test)]
|
// #[cfg(test)]
|
||||||
std::env::set_var(ENV_VAR_NAME, serde_json::to_string(&self).unwrap());
|
// std::env::set_var(ENV_VAR_NAME, serde_json::to_string(&self).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
/// Load filemap in main program. Should be called from main program
|
/// Load filemap in main program. Should be called from main program
|
||||||
fn load() -> Self {
|
fn load() -> Self {
|
||||||
let env = std::env::var(ENV_VAR_NAME)
|
let map = fs::read_to_string(CACHE_BUSTER_DATA_FILE).unwrap();
|
||||||
.expect("unable to read env var, might be a bug in lib. Please report on GitHub");
|
let res: Files = serde_json::from_str(&map).unwrap();
|
||||||
let res: Files = serde_json::from_str(&env).unwrap();
|
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,8 +269,8 @@ impl Files {
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hasher_works() {
|
fn hasher_works() {
|
||||||
|
delete_file();
|
||||||
let types = vec![
|
let types = vec![
|
||||||
mime::IMAGE_PNG,
|
mime::IMAGE_PNG,
|
||||||
mime::IMAGE_SVG,
|
mime::IMAGE_SVG,
|
||||||
|
@ -339,42 +300,17 @@ pub mod tests {
|
||||||
cleanup(&config);
|
cleanup(&config);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn try_process_works() {
|
|
||||||
let types = vec![
|
|
||||||
mime::IMAGE_PNG,
|
|
||||||
mime::IMAGE_SVG,
|
|
||||||
mime::IMAGE_JPEG,
|
|
||||||
mime::IMAGE_GIF,
|
|
||||||
];
|
|
||||||
|
|
||||||
let config = BusterBuilder::default()
|
|
||||||
.source("./dist")
|
|
||||||
.result("/tmp/prod")
|
|
||||||
.mime_types(types)
|
|
||||||
.copy(true)
|
|
||||||
.follow_links(true)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
config.process().unwrap();
|
|
||||||
let mut files = Files::load();
|
|
||||||
|
|
||||||
for (k, v) in files.map.drain() {
|
|
||||||
let src = Path::new(&k);
|
|
||||||
let dest = Path::new(&v);
|
|
||||||
|
|
||||||
assert_eq!(src.exists(), dest.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup(&config);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cleanup(config: &Buster) {
|
pub fn cleanup(config: &Buster) {
|
||||||
let _ = fs::remove_dir_all(&config.result);
|
let _ = fs::remove_dir_all(&config.result);
|
||||||
|
delete_file();
|
||||||
}
|
}
|
||||||
#[test]
|
|
||||||
|
pub fn delete_file() {
|
||||||
|
let _ = fs::remove_file(&CACHE_BUSTER_DATA_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
fn prefix_works() {
|
fn prefix_works() {
|
||||||
|
delete_file();
|
||||||
let types = vec![
|
let types = vec![
|
||||||
mime::IMAGE_PNG,
|
mime::IMAGE_PNG,
|
||||||
mime::IMAGE_SVG,
|
mime::IMAGE_SVG,
|
||||||
|
@ -398,7 +334,6 @@ pub mod tests {
|
||||||
if let Some(prefix) = &config.prefix {
|
if let Some(prefix) = &config.prefix {
|
||||||
for (k, v) in files.map.drain() {
|
for (k, v) in files.map.drain() {
|
||||||
let src = Path::new(&k);
|
let src = Path::new(&k);
|
||||||
println!("{}", &v);
|
|
||||||
let dest = Path::new(&v[prefix.len()..]);
|
let dest = Path::new(&v[prefix.len()..]);
|
||||||
|
|
||||||
assert_eq!(src.exists(), dest.exists());
|
assert_eq!(src.exists(), dest.exists());
|
||||||
|
@ -407,4 +342,9 @@ pub mod tests {
|
||||||
|
|
||||||
cleanup(&config);
|
cleanup(&config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn runner() {
|
||||||
|
prefix_works();
|
||||||
|
hasher_works();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue