use std::convert::AsRef;
use std::error;
use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use crc::crc64::checksum_iso as checksum;
use walkdir::WalkDir;
use super::{Error, Result};
pub fn get<P: AsRef<Path>>(base_path: P) -> Result<FileData> {
if !base_path.as_ref().is_dir() {
return Err(Error::FileData(FileDataError::BasePathNotDirectory));
}
let full_base_path = base_path.as_ref().canonicalize()?;
let mut file_data = Vec::<FileDatum>::new();
for entry in WalkDir::new(&full_base_path) {
let ent = entry?;
if ent.file_type().is_file() {
let full_path = ent.path().to_path_buf();
let file_path = full_path.strip_prefix(&full_base_path)
.unwrap().to_path_buf();
let metadata = ent.metadata()?;
let length = metadata.len();
if let Some(p) = file_path.to_str() {
let mut in_file = File::open(full_path)?;
let mut contents = Vec::<u8>::with_capacity(length as usize);
in_file.read_to_end(&mut contents)?;
let contents_checksum = checksum(&contents);
file_data.push(FileDatum {
name: String::from(p),
length: length,
checksum: contents_checksum,
});
}
else {
return Err(Error::FileData(FileDataError::NonUtf8Filepath(
String::from(file_path.to_string_lossy())
)));
}
}
}
Ok(FileData {
base_path: full_base_path,
data: file_data,
})
}
#[derive(Clone)]
pub struct FileData {
base_path: PathBuf,
data: Vec<FileDatum>,
}
impl FileData {
#[cfg(test)]
pub fn new(base_path: PathBuf, data: Vec<FileDatum>) -> Self {
FileData {
base_path: base_path,
data: data,
}
}
pub fn path(&self) -> PathBuf {
self.base_path.clone()
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn into_vec(self) -> Vec<FileDatum> {
self.data
}
}
#[derive(Debug)]
pub enum FileDataError {
BasePathNotDirectory,
NonUtf8Filepath(String),
}
impl fmt::Display for FileDataError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
FileDataError::BasePathNotDirectory => {
write!(fmt, "Base path is not a directory")
},
FileDataError::NonUtf8Filepath(ref file_path) => {
write!(fmt, "{}", file_path)
},
}
}
}
impl error::Error for FileDataError {
fn description(&self) -> &str {
static BASE_PATH_NOT_DIRECTORY: &'static str = "Base path is not a directory";
static NON_UTF8_FILE_PATH: &'static str = "Non-Utf8 file path detected";
match *self {
FileDataError::BasePathNotDirectory => {
BASE_PATH_NOT_DIRECTORY
},
FileDataError::NonUtf8Filepath(_) => {
NON_UTF8_FILE_PATH
},
}
}
fn cause(&self) -> Option<&error::Error> { None }
}
#[derive(Clone)]
pub struct FileDatum {
name: String,
length: u64,
checksum: u64,
}
impl FileDatum {
#[cfg(test)]
pub fn new(name: String, length: u64, checksum: u64) -> Self {
FileDatum {
name: name,
length: length,
checksum: checksum,
}
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn len(&self) -> u64 {
self.length
}
pub fn checksum(&self) -> u64 {
self.checksum
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(windows)]
fn get_reqchan_docs() -> Vec<String> {
let mut v = Vec::<String>::new();
v.push(String::from("implementors\\core\\clone\\trait.Clone.js"));
v.push(String::from("implementors\\core\\fmt\\trait.Debug.js"));
v.push(String::from("implementors\\core\\ops\\trait.Drop.js"));
v.push(String::from("reqchan\\RequestContract.t.html"));
v.push(String::from("reqchan\\Requester.t.html"));
v.push(String::from("reqchan\\Responder.t.html"));
v.push(String::from("reqchan\\ResponseContract.t.html"));
v.push(String::from("reqchan\\TryReceiveError.t.html"));
v.push(String::from("reqchan\\TryRequestError.t.html"));
v.push(String::from("reqchan\\TryRespondError.t.html"));
v.push(String::from("reqchan\\channel.v.html"));
v.push(String::from("reqchan\\enum.TryReceiveError.html"));
v.push(String::from("reqchan\\enum.TryRequestError.html"));
v.push(String::from("reqchan\\enum.TryRespondError.html"));
v.push(String::from("reqchan\\fn.channel.html"));
v.push(String::from("reqchan\\index.html"));
v.push(String::from("reqchan\\sidebar-items.js"));
v.push(String::from("reqchan\\struct.RequestContract.html"));
v.push(String::from("reqchan\\struct.Requester.html"));
v.push(String::from("reqchan\\struct.Responder.html"));
v.push(String::from("reqchan\\struct.ResponseContract.html"));
v.push(String::from("src\\reqchan\\lib.rs.html"));
v.push(String::from("COPYRIGHT.txt"));
v.push(String::from("FiraSans-LICENSE.txt"));
v.push(String::from("FiraSans-Medium.woff"));
v.push(String::from("FiraSans-Regular.woff"));
v.push(String::from("Heuristica-Italic.woff"));
v.push(String::from("Heuristica-LICENSE.txt"));
v.push(String::from("LICENSE-APACHE.txt"));
v.push(String::from("LICENSE-MIT.txt"));
v.push(String::from("SourceCodePro-LICENSE.txt"));
v.push(String::from("SourceCodePro-Regular.woff"));
v.push(String::from("SourceCodePro-Semibold.woff"));
v.push(String::from("SourceSerifPro-Bold.woff"));
v.push(String::from("SourceSerifPro-LICENSE.txt"));
v.push(String::from("SourceSerifPro-Regular.woff"));
v.push(String::from("main.css"));
v.push(String::from("main.js"));
v.push(String::from("normalize.css"));
v.push(String::from("rustdoc.css"));
v.push(String::from("search-index.js"));
v
}
#[cfg(not(windows))]
fn get_reqchan_docs() -> Vec<String> {
let mut v = Vec::<String>::new();
v.push(String::from("implementors/core/clone/trait.Clone.js"));
v.push(String::from("implementors/core/fmt/trait.Debug.js"));
v.push(String::from("implementors/core/ops/trait.Drop.js"));
v.push(String::from("reqchan/RequestContract.t.html"));
v.push(String::from("reqchan/Requester.t.html"));
v.push(String::from("reqchan/Responder.t.html"));
v.push(String::from("reqchan/ResponseContract.t.html"));
v.push(String::from("reqchan/TryReceiveError.t.html"));
v.push(String::from("reqchan/TryRequestError.t.html"));
v.push(String::from("reqchan/TryRespondError.t.html"));
v.push(String::from("reqchan/channel.v.html"));
v.push(String::from("reqchan/enum.TryReceiveError.html"));
v.push(String::from("reqchan/enum.TryRequestError.html"));
v.push(String::from("reqchan/enum.TryRespondError.html"));
v.push(String::from("reqchan/fn.channel.html"));
v.push(String::from("reqchan/index.html"));
v.push(String::from("reqchan/sidebar-items.js"));
v.push(String::from("reqchan/struct.RequestContract.html"));
v.push(String::from("reqchan/struct.Requester.html"));
v.push(String::from("reqchan/struct.Responder.html"));
v.push(String::from("reqchan/struct.ResponseContract.html"));
v.push(String::from("src/reqchan/lib.rs.html"));
v.push(String::from("COPYRIGHT.txt"));
v.push(String::from("FiraSans-LICENSE.txt"));
v.push(String::from("FiraSans-Medium.woff"));
v.push(String::from("FiraSans-Regular.woff"));
v.push(String::from("Heuristica-Italic.woff"));
v.push(String::from("Heuristica-LICENSE.txt"));
v.push(String::from("LICENSE-APACHE.txt"));
v.push(String::from("LICENSE-MIT.txt"));
v.push(String::from("SourceCodePro-LICENSE.txt"));
v.push(String::from("SourceCodePro-Regular.woff"));
v.push(String::from("SourceCodePro-Semibold.woff"));
v.push(String::from("SourceSerifPro-Bold.woff"));
v.push(String::from("SourceSerifPro-LICENSE.txt"));
v.push(String::from("SourceSerifPro-Regular.woff"));
v.push(String::from("main.css"));
v.push(String::from("main.js"));
v.push(String::from("normalize.css"));
v.push(String::from("rustdoc.css"));
v.push(String::from("search-index.js"));
v
}
#[test]
fn test_v1_get_file_data() {
let reqchan_docs = get_reqchan_docs();
let path = Path::new("testarchives/reqchandocs");
let file_data = get(path).ok().unwrap();
let full_path = path.canonicalize().ok().unwrap();
assert_eq!(full_path, file_data.path());
assert_eq!(file_data.len(), reqchan_docs.len());
let fdvec = file_data.into_vec();
for name in reqchan_docs.iter() {
let mut found = false;
for dname in fdvec.iter() {
if *name == *dname.name() {
found = true;
}
}
if !found {
println!("{} not found!", *name);
}
assert!(found);
}
}
}