diff --git a/Cargo.lock b/Cargo.lock index aa82390..5ef5622 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,6 +71,11 @@ dependencies = [ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pathdiff" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.3.6" @@ -193,6 +198,7 @@ name = "teascade-generator" version = "0.1.0" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pathdiff 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", @@ -304,6 +310,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" "checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" +"checksum pathdiff 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a3bf70094d203e07844da868b634207e71bfab254fe713171fae9a6e751ccf31" "checksum proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "49b6a521dc81b643e9a51e0d1cf05df46d5a2f3c0280ea72bcb68276ba64a118" "checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" "checksum quote 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0ff51282f28dc1b53fd154298feaa2e77c5ea0dba68e1fd8b03b72fbe13d2a" diff --git a/Cargo.toml b/Cargo.toml index 5bc88d6..4c2d295 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ structopt = "*" serde = "*" serde_derive = "*" regex = "*" -ansi_term = "*" \ No newline at end of file +ansi_term = "*" +pathdiff = "*" \ No newline at end of file diff --git a/src/config_toml.rs b/src/config_toml.rs index a5a17dd..d0b0485 100644 --- a/src/config_toml.rs +++ b/src/config_toml.rs @@ -9,13 +9,13 @@ use std::collections::HashMap; use logger::LogLevel; use error::Error; -#[derive(Deserialize, Clone, Debug)] +#[derive(Deserialize, Serialize, Clone, Debug)] pub struct GlobalConfigToml { pub website: WebsiteConfig, pub navbar: Option, } -#[derive(Deserialize, Clone, Debug)] +#[derive(Deserialize, Serialize, Clone, Debug)] pub struct WebsiteConfig { pub website_name: String, pub built_pages: Vec, @@ -31,14 +31,14 @@ pub struct WebsiteConfig { pub google_site_verification: Option, } -#[derive(Deserialize, Clone, Debug)] +#[derive(Deserialize, Serialize, Clone, Debug)] pub struct NavbarConfig { pub items: Vec, #[serde(rename = "item")] pub item_map: HashMap, } -#[derive(Deserialize, Clone, Debug)] +#[derive(Deserialize, Serialize, Clone, Debug)] pub struct NavbarItem { pub title: String, pub link: String, @@ -69,7 +69,7 @@ impl GlobalConfigToml { } } -#[derive(Deserialize, Clone, Debug)] +#[derive(Deserialize, Serialize, Clone, Debug)] pub struct PageConfigToml { pub page: PageConfig, } @@ -118,7 +118,7 @@ impl PageConfigToml { } } -#[derive(Deserialize, Clone, Debug)] +#[derive(Deserialize, Serialize, Clone, Debug)] pub struct PageConfig { pub html_path: String, pub title: String, diff --git a/src/file_writer.rs b/src/file_writer.rs index 114e149..add6305 100644 --- a/src/file_writer.rs +++ b/src/file_writer.rs @@ -8,7 +8,6 @@ use logger::LogLevel; pub fn add_to_beginning>(path: PathBuf, to_add: T) -> Result { if let (Some(parent), Some(file_name)) = (path.clone().parent(), path.clone().file_name()) { let parent = Path::new(&to_add.into()).join(parent); - create_dir_all(parent.clone())?; Ok(parent.join(file_name)) } else { return Err(Error::new( @@ -23,6 +22,15 @@ pub fn write_file>( content: T, overwrite: bool, ) -> Result<(), Error> { + if let Some(parent) = path.clone().parent() { + create_dir_all(parent.clone())?; + } else { + return Err(Error::new( + LogLevel::SEVERE, + format!("Could not find parent folder for {:?}", path), + )); + } + let content = content.into(); if path.exists() && !overwrite { Err(Error::new( diff --git a/src/main.rs b/src/main.rs index 697ea5a..a9d7f9a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ extern crate ansi_term; +extern crate pathdiff; extern crate pulldown_cmark; extern crate regex; #[macro_use] @@ -15,6 +16,7 @@ mod config; mod template; mod renderer; mod builder; +mod new_page; mod file_writer; use structopt::StructOpt; @@ -36,16 +38,15 @@ fn main() { logger.log(LogLevel::SEVERE, "Aborting building due to error."); } }, - Subcommands::New(ops) => { - logger.log( - LogLevel::DETAIL, - format!("Creating a new .toml file at {:?}", ops.toml_path), - ); - - match file_writer::write_file(ops.toml_path, "Hello!", ops.overwrite) { - Ok(_) => logger.log(LogLevel::INFO, "Done."), - Err(err) => logger.log(err.severity(), err.description()), + Subcommands::New(ops) => match new_page::generate_new_page(ops, &logger) { + Ok(_) => logger.log( + LogLevel::DETAILER, + "Generating the new page finished successfully.", + ), + Err(err) => { + logger.log(err.severity(), err.description()); + logger.log(LogLevel::SEVERE, "Aborting building due to error."); } - } + }, } } diff --git a/src/new_page.rs b/src/new_page.rs new file mode 100644 index 0000000..8735e52 --- /dev/null +++ b/src/new_page.rs @@ -0,0 +1,133 @@ +use std::path::PathBuf; +use std::error::Error as STDError; + +use options::NewOps; +use logger::{LogLevel, Logger}; +use file_writer; +use error::Error; +use config_toml::{PageConfig, PageConfigToml}; + +use toml; +use pathdiff; + +const PLACEHOLDER_MARKDOWN: &'static str = +r#"# Placeholder title +This is the markdown file, where you will insert this page's contents. +- This is an example list +- And in lists + - You can even make inner lists, as well as **bold**, and _italics_ + +See [CommonMark](http://commonmark.org/help/) for reference."#; + +pub fn generate_new_page(ops: NewOps, logger: &Logger) -> Result<(), Error> { + logger.log( + LogLevel::DETAILER, + format!("Starting to create paths for given files"), + ); + + let toml_path = ensure_extension(ops.toml_path, "toml", false)?; + + let markdown_path; + if let Some(markdown) = ops.markdown_path { + markdown_path = ensure_extension(markdown, "md", false)?; + } else { + markdown_path = ensure_extension(toml_path.clone(), "md", true)?; + } + + logger.log( + LogLevel::DETAIL, + format!("Creating a new .toml file at {:?}", toml_path), + ); + + let html_path; + if let Some(path) = ops.html_path { + html_path = ensure_extension(path, "html", false)?; + } else { + html_path = ensure_extension(toml_path.clone(), "html", true)?; + } + + let title: String; + if let Some(t) = ops.title { + title = t; + } else { + let mut parts = toml_path.file_name().unwrap().to_str().unwrap().split("."); + title = parts.next().unwrap().to_owned(); + } + + let mut relative_markdown_path = markdown_path.clone(); + if let Some(toml_parent) = toml_path.parent() { + if let Some(relative) = pathdiff::diff_paths(markdown_path.as_path(), toml_parent) { + relative_markdown_path = relative; + } + } + + let page_config = PageConfigToml { + page: PageConfig { + html_path: html_path.to_str().unwrap().to_owned(), + title: title, + description: String::new(), + content_path: relative_markdown_path.to_str().unwrap().to_owned(), + favicon: None, + before_navbar_url: None, + before_content_url: None, + after_content_url: None, + javascript: None, + css: None, + }, + }; + + match toml::ser::to_string_pretty(&page_config) { + Ok(text) => file_writer::write_file(toml_path.clone(), text, ops.overwrite)?, + Err(err) => { + return Err(Error::new( + LogLevel::SEVERE, + format!( + "Failed to serialize page config: {}", + err.description().to_owned() + ), + )) + } + } + + logger.log( + LogLevel::DETAIL, + format!("Creating a new .md file at {:?}", markdown_path), + ); + + file_writer::write_file(markdown_path, PLACEHOLDER_MARKDOWN, ops.overwrite)?; + + Ok(()) +} + +fn ensure_extension>( + mut path: PathBuf, + extension: T, + replace_extension: bool, +) -> Result { + let extension = extension.into(); + + if let (Some(parent), Some(file_name)) = (path.clone().parent(), path.clone().file_name()) { + let mut filename = file_name.to_str().unwrap().to_owned(); + let clone = filename.clone(); + let mut split = clone.split("."); + if split.clone().count() == 1 as usize { + path = parent.join(format!("{}.{}", filename, extension)); + } else if replace_extension { + let amount = split.clone().count() - 1; + filename = split + .clone() + .take(amount) + .map(|i| format!("{}.", i)) + .collect::(); + + path = parent.join(format!("{}{}", filename, extension)); + } + + Ok(path) + } else { + Err(Error::new( + LogLevel::SEVERE, + format!("Could not find parent/file_name for {:?}", path), + )) + } +} diff --git a/src/options.rs b/src/options.rs index 520e1d6..1c9b5e4 100644 --- a/src/options.rs +++ b/src/options.rs @@ -41,6 +41,12 @@ pub struct NewOps { /// File path for the markdown file created #[structopt(short = "m", long = "markdown", parse(from_os_str))] pub markdown_path: Option, + /// Outpuh html file path of the generated page + #[structopt(short = "h", long = "html", parse(from_os_str))] + pub html_path: Option, + /// Sets the title of the generated page + #[structopt(short = "t", long = "title")] + pub title: Option, /// Overwrites existing .toml / .md files #[structopt(short = "o", long = "overwrite")] pub overwrite: bool, diff --git a/test_page/hello.toml b/test_page/hello.toml deleted file mode 100644 index 05a682b..0000000 --- a/test_page/hello.toml +++ /dev/null @@ -1 +0,0 @@ -Hello! \ No newline at end of file