write filemap to file

This commit is contained in:
Aravinth Manivannan 2021-04-30 20:37:56 +05:30
parent d5593b2db6
commit 3c3b61aebf
Signed by: realaravinth
GPG key ID: AD9F0F08E855ED88
9 changed files with 87 additions and 116 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@ prod
tarpaulin-report.html tarpaulin-report.html
actix-example/target actix-example/target
actix-example/prod actix-example/prod
prod56/

View file

@ -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
View file

@ -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",

View file

@ -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"

File diff suppressed because one or more lines are too long

View file

@ -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(

View file

@ -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();
}
} }

View file

@ -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";

View file

@ -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();
}
} }