From aea35fa7e1a3261e5e7a3e8daa999bed3dcd193f Mon Sep 17 00:00:00 2001 From: Till Höppner Date: Thu, 18 Feb 2016 18:53:30 +0100 Subject: Restructure, add API, add compression --- codegen/Cargo.toml | 14 +++++ codegen/src/lib.rs | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 codegen/Cargo.toml create mode 100644 codegen/src/lib.rs (limited to 'codegen') diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml new file mode 100644 index 0000000..1fb1cad --- /dev/null +++ b/codegen/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "includedir_codegen" +description = "Include a whole directory tree at compile time! - Compile time part" +keywords = ["include", "tree", "directory", "build", "static"] +version = "0.1.1" +authors = ["Till Höppner "] +license = "MIT" +homepage = "https://github.com/tilpner/includedir" +repository = "https://github.com/tilpner/includedir" + +[dependencies] +walkdir = "0.1.5" +phf_codegen = "0.7.12" +flate2 = "0.2.13" diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs new file mode 100644 index 0000000..e8a6475 --- /dev/null +++ b/codegen/src/lib.rs @@ -0,0 +1,147 @@ +extern crate walkdir; +extern crate phf_codegen; +extern crate flate2; + +use std::{fmt, env, 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::FlateWriteExt; + +#[derive(Copy, Clone, 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, + name: String, +} + +pub fn start(static_name: &str) -> IncludeDir { + IncludeDir { + files: HashMap::new(), + name: static_name.to_owned(), + } +} + +#[cfg(windows)] +fn as_key(path: &str) -> Cow { + Cow::Owned(path.replace("\\", "/")) +} + +#[cfg(not(windows))] +fn as_key(path: &str) -> Cow { + Cow::Borrowed(path) +} + +impl IncludeDir { + /// Add a single file to the binary. + /// With Gzip compression, the file will be encoded to OUT_DIR first. + /// For chaining, it's not sensible to return a Result. If any to-be-included + /// files can't be found, or encoded, this function will panic!. + pub fn file>(&mut self, path: P, comp: Compression) -> &mut IncludeDir { + self.add_file(path, comp).unwrap(); + self + } + + /// ## Panics + /// + /// This function panics when CARGO_MANIFEST_DIR or OUT_DIR are not defined. + pub fn add_file>(&mut self, path: P, comp: Compression) -> io::Result<()> { + let key = path.as_ref().to_string_lossy(); + match comp { + Compression::None => { + self.files.insert(as_key(key.borrow()).into_owned(), + (comp, path.as_ref().clone().to_owned())); + } + Compression::Gzip => { + // gzip encode file to OUT_DIR + let in_path = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).join(&path); + let mut in_file = BufReader::new(try!(File::open(&in_path))); + + let out_path = Path::new(&env::var("OUT_DIR").unwrap()).join(&path); + try!(fs::create_dir_all(&out_path.parent().unwrap())); + let out_file = BufWriter::new(try!(File::create(&out_path))); + let mut encoder = out_file.gz_encode(flate2::Compression::Default); + + try!(io::copy(&mut in_file, &mut encoder)); + + self.files.insert(as_key(key.borrow()).into_owned(), + (comp, out_path.to_owned())); + } + } + Ok(()) + } + + /// Add a whole directory recursively to the binary. + /// This function calls `file`, and therefore will panic! on missing files. + pub fn dir>(&mut self, path: P, comp: Compression) -> &mut IncludeDir { + self.add_dir(path, comp).unwrap(); + self + } + + /// ## Panics + /// + /// This function panics when CARGO_MANIFEST_DIR or OUT_DIR are not defined. + pub fn add_dir>(&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() => { + try!(self.add_file(e.path(), comp)); + } + _ => (), + } + } + Ok(()) + } + + pub fn build(&self, out_name: &str) -> io::Result<()> { + let base_path = Path::new(&env::var("CARGO_MANIFEST_DIR").unwrap()).to_owned(); + let out_path = Path::new(&env::var("OUT_DIR").unwrap()).join(out_name); + let mut out_file = BufWriter::new(try!(File::create(&out_path))); + + try!(write!(&mut out_file, + "use includedir::*;\n\ + pub static {}: Files = Files {{\n\ + \tfiles: ", + self.name)); + + let entries: Vec<_> = self.files //accumulate_entries() + .iter() + .map(|(name, &(ref comp, ref path))| { + let include_path = format!("{}", + base_path.join(path).display()); + let code = format!("(Compression::{}, \ + include_bytes!(\"{}\") as &'static \ + [u8])", + comp, + as_key(&include_path)); + (as_key(&name).into_owned(), code) + }) + .collect(); + let mut map: phf_codegen::Map<&str> = phf_codegen::Map::new(); + for &(ref name, ref code) in &entries { + map.entry(&name, &code); + } + try!(map.build(&mut out_file)); + + try!(write!(&mut out_file, "\n}};\n")); + Ok(()) + } +} -- cgit v1.2.3