diff --git a/Cargo.lock b/Cargo.lock index 8cef2bc..6c0c33d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,7 @@ dependencies = [ "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "iron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "markdown 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "mowl 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 370df6e..efb6d94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,5 +23,6 @@ iron = "0" log = "0" markdown = "0" mowl = "1" +lazy_static = "*" rayon = "0" uuid = { version = "0.5", features = ["v5"] } diff --git a/src/cli.yml b/src/cli.yml index 62a6805..e82220f 100644 --- a/src/cli.yml +++ b/src/cli.yml @@ -9,20 +9,25 @@ global_settings: args: - input_directory: default_value: . - help: The directory containing the markdown files to use + help: The directory containing the markdown files to use. required: true value_name: INPUT - output_directory: default_value: output - help: The directory where the HTML output is generated + help: The directory where the HTML output is generated. long: output-directory short: o value_name: PATH - verbose: - help: Set the verbosity level (maximum 4x `v`) + help: Set the verbosity level (maximum 4x `v`). short: v multiple: true - www: - help: Enable integrated HTTP server to serve contents from output directory + help: Enable integrated HTTP server to serve contents from output directory. long: www short: w + - file_directory: + default_value: files + help: The directory where files are stored. + short: f + long: file-directory diff --git a/src/error.rs b/src/error.rs index b660153..63f62d4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use std::io; use glob; -use iron::error::HttpError; +use iron::error::{HttpError, IronError}; error_chain! { foreign_links { diff --git a/src/lib.rs b/src/lib.rs index 0cf29fd..df87ef8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,8 @@ extern crate markdown; extern crate mowl; #[macro_use] extern crate error_chain; +#[macro_use] +extern crate lazy_static; extern crate uuid; extern crate rayon; @@ -25,16 +27,16 @@ use markdown::to_html; use iron::prelude::*; use iron::status; use iron::headers::ContentType; +use iron::mime::Mime; + -use std::fs::{self, canonicalize, create_dir_all, File}; +use std::fs::{self, canonicalize, create_dir_all, File, OpenOptions}; use std::path::{Path, PathBuf, MAIN_SEPARATOR}; use std::io::prelude::*; use std::str; use filehash::Filehash; use rayon::iter::{ParallelIterator, IntoParallelRefMutIterator}; -static SHA_FILE: &str = ".files.sha"; - pub struct InputPaths { path: PathBuf, hash: String, @@ -114,6 +116,21 @@ impl InputPaths { } } +lazy_static! { + static ref PDF_MIME: Mime = "application/pdf".parse::().unwrap(); + static ref DOC_MIME: Mime = "application/msword".parse::().unwrap(); + static ref ODA_MIME: Mime = "application/oda".parse::().unwrap(); + static ref ZIP_MIME: Mime = "application/zip".parse::().unwrap(); + static ref WAV_MIME: Mime = "audio/x-wav".parse::().unwrap(); + static ref CSS_MIME: Mime = "text/css".parse::().unwrap(); + static ref GIF_MIME: Mime = "image/gif".parse::().unwrap(); + static ref MPG_MIME: Mime = "video/mpeg".parse::().unwrap(); + static ref AVI_MIME: Mime = "video/x-msvideo".parse::().unwrap(); + static ref PNG_MIME: Mime = "image/png".parse::().unwrap(); + static ref JPG_MIME: Mime = "image/jpeg".parse::().unwrap(); + static ref SHA_FILE: &'static str = ".files.sha"; +} + #[derive(Default)] /// Global processing structure pub struct Wiki { @@ -139,7 +156,6 @@ impl Wiki { Ok(()) } - /// Reads all markdown files recursively from a given directory. /// Clears the current available input_paths pub fn read_from_directory(&mut self, directory: &str) -> Result<()> { @@ -161,7 +177,6 @@ impl Wiki { Ok(()) } - /// Print absolute path of all added md files pub fn list_current_input_paths(&self) { info!("Found the following markdown files:"); @@ -184,7 +199,7 @@ impl Wiki { fs::create_dir(output_directory)?; } - let sha_file_path = PathBuf::from(output_directory).join(SHA_FILE); + let sha_file_path = PathBuf::from(output_directory).join(*SHA_FILE); let sha_file = sha_file_path.to_str() .ok_or_else(|| "Unable to stringify the sha file path.")?; @@ -225,10 +240,78 @@ impl Wiki { Ok(()) } + /// Add a directory for storing files to the generated html sites or read stored + /// files of existing filestorage. Afterwards file links will be added to the + /// generated html site. + pub fn read_files(&mut self, file_folder: &str, output: &str) -> Result<()> { + + //create the default file path + let output_path = PathBuf::from(output); + let file_directory = PathBuf::from(output).join(file_folder); + + if !Path::new(file_folder).exists(){ + info!("Creating directory for file input: {:?}.", file_directory.to_str()); + fs::create_dir(&file_directory); + } + + for path in &self.input_paths{ + let mut count = 0; + + //create the path for the html site or just find it + let page_files_path = file_directory.join(&path.path).to_str().ok_or_else(|| + "Couldn't create Path to html file!")?.replace(".md",""); + + if !PathBuf::from(page_files_path.as_str()).exists(){ + info!("Creating directory for {}'s files: {}.", path.path.to_str().ok_or_else(|| "Path not found!")?, + page_files_path.as_str()); + fs::create_dir_all(page_files_path.as_str()); + } + + //add the path + let file_path = Path::new(page_files_path.as_str()); + //read all the files + let files = file_path.read_dir()?; + + //now attach the files to the html sites folder + for entry in files { + let current_entry = match entry { + Ok(entry) => entry, + _ => panic!("Couldn't read the path!"), + }; + + let link = format!( + "\n{}
\n", + path.path.to_str().ok_or_else(|| "Path not found!")?.replace(".md", ""), + current_entry.file_name().to_str().ok_or_else(|| "Entry is corrupted!")?.replace("\n","."), + current_entry.file_name().to_str().ok_or_else(|| "Entry is corrupted!")? + ); + let mut html_file = OpenOptions::new().read(true).write(true).create(true). + open(output_path.join(&path.path).to_str().ok_or_else(|| "Entry is corrupted!")?. + replace(".md", ".html"))?; + + let mut buffer = String::new(); + + html_file.read_to_string(&mut buffer); + buffer += &link.as_str(); + info!("Creating link to {:?} for {:?}", current_entry.file_name(), path.path.to_str() + .ok_or_else(|| "Entry is corrupted!")?.replace(".md", ".html")); + html_file.write((&buffer).as_bytes()); + count += 1; + } + //check if there are no files attached + if count == 0 { + info!("No files found so far for {}. Simply add your files to the folders in 'files'.", + path.path.to_str().ok_or_else(|| "Entry is corrupted!")?) + } + } + Ok(()) + } + /// Create an HTTP server serving the generated files pub fn serve(&self, output_directory: &str) -> Result<()> { + // Create a default listening address - let addr = "localhost:5000"; + let addr = "localhost:30000"; info!("Listening on {}", addr); // Moving the data into the closure @@ -236,9 +319,13 @@ impl Wiki { // Create a new iron instance Iron::new(move |request: &mut Request| { - // The owned path needs to created from the cloned string - let mut path = PathBuf::from(output_directory_string.clone()); - + ///to load files in browser + fn get_file(mime_type: &Mime, body: File) -> iron::Response { + let mut resp = Response::with((status::Ok, body)); + resp.headers.set(ContentType(mime_type.to_owned())); + resp + } + let mut path = PathBuf::from(&output_directory_string); // Create the full path for part in request.url.path() { path.push(part); @@ -251,27 +338,48 @@ impl Wiki { path.push("index.html"); } - let mut f = match File::open(path) { + if !path.exists() { + return Ok(Response::with((ContentType::html().0, + status::NotFound, include_str!("html/404.html")))); + } + let mut f = match File::open(&path) { Ok(v) => v, _ => return Ok(Response::with((ContentType::html().0, status::NotFound, include_str!("html/404.html")))), - }; - - let mut buffer = String::new(); - match f.read_to_string(&mut buffer) { - Ok(v) => v, - _ => return Ok(Response::with((ContentType::html().0, - status::InternalServerError, - include_str!("html/500.html")))), - }; - - // Content type needs to be determined from the file rather - // than assuming html - Ok(Response::with((ContentType::html().0, status::Ok, buffer))) - + }; + + match path.to_str(){ + Some(name) => { + + if name.contains(".pdf") {return Ok(get_file(&(*PDF_MIME), f))}; + if name.contains(".doc") {return Ok(get_file(&(*DOC_MIME), f))}; + if name.contains(".oda") {return Ok(get_file(&(*ODA_MIME), f))}; + if name.contains(".zip") {return Ok(get_file(&(*ZIP_MIME), f))}; + if name.contains(".wav") {return Ok(get_file(&(*WAV_MIME), f))}; + if name.contains(".css") {return Ok(get_file(&(*CSS_MIME), f))}; + if name.contains(".mp") {return Ok(get_file(&(*MPG_MIME), f))}; + if name.contains(".avi") {return Ok(get_file(&(*AVI_MIME), f))}; + if name.contains(".png") {return Ok(get_file(&(*PNG_MIME), f))}; + if name.contains(".jp") {return Ok(get_file(&(*JPG_MIME), f))}; + if name.contains(".gif") {return Ok(get_file(&(*GIF_MIME), f))}; + + if name.contains(".html") { + let mut buffer = String::new(); + match f.read_to_string(&mut buffer) { + Ok(v) => v, + _ => return Ok(Response::with((ContentType::html().0, + status::InternalServerError, + include_str!("html/500.html")))), + }; + return Ok(Response::with((ContentType::html().0, status::Ok, buffer))) + } + + else {Ok(Response::with((status::Ok, f)))} + }, + _ => panic!("Invalid Path."), + } }).http(addr)?; - Ok(()) } } diff --git a/src/main.rs b/src/main.rs index bcd06c6..780b464 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,12 +52,16 @@ fn run() -> Result<()> { let enable_httpd = matches.is_present("www"); + let file_directory = matches.value_of("file_directory") + .ok_or_else(|| "CLI parameter 'file_directory' missing")?; + // Do first processing steps let mut wiki = Wiki::new(); wiki.init_logging(log_level)?; wiki.read_from_directory(input_directory)?; wiki.read_content_from_current_paths(input_directory, output_directory)?; + wiki.read_files(file_directory, output_directory); wiki.create_index_tree(output_directory)?; if enable_httpd { diff --git a/tests/example_md/real_md/example-image.jpg b/tests/example_md/real_md/example-image.jpg deleted file mode 100644 index 594c800..0000000 Binary files a/tests/example_md/real_md/example-image.jpg and /dev/null differ diff --git a/tests/example_md/real_md/test3.xml b/tests/example_md/real_md/test3.xml deleted file mode 100644 index e69de29..0000000