extern crate walkdir;
extern crate phf_codegen;
extern crate flate2;
use std::{env, fmt, io};
use std::borrow::{Borrow, Cow};
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{BufReader, BufWriter, Write};
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
use flate2::write::GzEncoder;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Compression {
None,
Gzip
}
impl fmt::Display for Compression {
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Compression::None => fmt.write_str("None"),
Compression::Gzip => fmt.write_str("Gzip"),
}
}
}
pub struct IncludeDir {
files: HashMap<String, (Compression, PathBuf)>,
name: String,
manifest_dir: PathBuf
}
pub fn start(static_name: &str) -> IncludeDir {
IncludeDir {
files: HashMap::new(),
name: static_name.to_owned(),
manifest_dir: Path::new(&env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is not set")).to_owned()
}
}
#[cfg(windows)]
fn as_key(path: &str) -> Cow<str> {
Cow::Owned(path.replace("\\", "/"))
}
#[cfg(not(windows))]
fn as_key(path: &str) -> Cow<str> {
Cow::Borrowed(path)
}
impl IncludeDir {
pub fn file<P: AsRef<Path>>(mut self, path: P, comp: Compression) -> IncludeDir {
self.add_file(path, comp).unwrap();
self
}
pub fn add_file<P: AsRef<Path>>(&mut self, path: P, comp: Compression) -> io::Result<()> {
let key = path.as_ref().to_string_lossy();
println!("cargo:rerun-if-changed={}", path.as_ref().display());
match comp {
Compression::None => {
self.files.insert(as_key(key.borrow()).into_owned(),
(comp, path.as_ref().to_owned()));
}
Compression::Gzip => {
let in_path = self.manifest_dir.join(&path);
let mut in_file = BufReader::new(File::open(&in_path)?);
let out_path = Path::new(&env::var("OUT_DIR").unwrap()).join(&path);
fs::create_dir_all(&out_path.parent().unwrap())?;
let out_file = BufWriter::new(File::create(&out_path)?);
let mut encoder = GzEncoder::new(out_file, flate2::Compression::best());
io::copy(&mut in_file, &mut encoder)?;
self.files.insert(as_key(key.borrow()).into_owned(),
(comp, out_path));
}
}
Ok(())
}
pub fn dir<P: AsRef<Path>>(mut self, path: P, comp: Compression) -> IncludeDir {
self.add_dir(path, comp).unwrap();
self
}
pub fn add_dir<P: AsRef<Path>>(&mut self, path: P, comp: Compression) -> io::Result<()> {
for entry in WalkDir::new(path).follow_links(true).into_iter() {
match entry {
Ok(ref e) if !e.file_type().is_dir() => {
self.add_file(e.path(), comp)?;
}
_ => (),
}
}
Ok(())
}
pub fn build(self, out_name: &str) -> io::Result<()> {
let out_path = Path::new(&env::var("OUT_DIR").unwrap()).join(out_name);
let mut out_file = BufWriter::new(File::create(&out_path)?);
writeln!(&mut out_file, "#[allow(clippy::unreadable_literal)]")?;
writeln!(&mut out_file,
"pub static {}: ::includedir::Files = ::includedir::Files {{\n\
files: ",
self.name)?;
let mut map: phf_codegen::Map<&str> = phf_codegen::Map::new();
let entries: Vec<_> = self.files.iter()
.map(|(name, (compression, path))| {
let include_path = format!("{}", self.manifest_dir.join(path).display());
(as_key(&name).to_string(), (compression, as_key(&include_path).to_string()))
})
.collect();
for (name, (compression, include_path)) in &entries {
map.entry(name,
&format!("(::includedir::Compression::{}, \
include_bytes!(\"{}\") as &'static [u8])",
compression, include_path));
}
writeln!(&mut out_file, "{}", map.build())?;
writeln!(&mut out_file, ", passthrough: ::std::sync::atomic::AtomicBool::new(false)")?;
writeln!(&mut out_file, "}};")?;
Ok(())
}
}