diff --git a/README.md b/README.md new file mode 100644 index 0000000..8968794 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# MD-DOCS + +All configuration is done via the `md-docs.config.yaml` file. + +## md-docs.config.yaml + +Values: + +```yaml +{ + String input # Path to the input folder + String output # Path to the output folder + + ?content: { + String? h1 # Classes to add to h1 elements + String? h2 # Classes to add to h2 elements + String? h3 # Classes to add to h3 elements + String? h4 # Classes to add to h4 elements + String? h5 # Classes to add to h5 elements + } +} +``` diff --git a/md-docs.md b/md-docs.md deleted file mode 100644 index bd62cb5..0000000 --- a/md-docs.md +++ /dev/null @@ -1,16 +0,0 @@ -# MD-DOCS - -All configuration is done via the `md-docs.config.yaml` file. - -## md-docs.config.yaml - -Values: - -```yaml -{ - String input # Path to the input folder - String output # Path to the output folder - - -} -``` diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..b01858e --- /dev/null +++ b/src/config.rs @@ -0,0 +1,124 @@ +use std::path::Path; + +use yaml_rust::Yaml; + +pub const CONFIG_NAME: &str = "md-docs.config.yaml"; +pub const INPUT_KEY: &str = "input"; +pub const OUTPUT_KEY: &str = "output"; +const TEMPLATE_KEY: &str = "template"; + +pub struct Config { + pub input: String, + pub output: String, + pub template: String, +} + +/// Creates a `YAML::String` from a `&str` +macro_rules! ystr { + ($str:literal) => { + &Yaml::String(String::from($str)) + }; +} + +pub fn parse(yaml_str: &String) -> Result { + let config_yaml = match yaml_rust::YamlLoader::load_from_str(yaml_str) { + Ok(y) => { + let document = &y[0]; + let Yaml::Hash(hash) = document + else { + return Err(format!("{} doesn't contain a hash as first value.", CONFIG_NAME)); + }; + + hash.clone() + } + Err(error) => { + return Err(format!( + "{} doesn't contain valid YAML.\n{:?}", + CONFIG_NAME, error + )); + } + }; + + let input_folder = match config_yaml.get(ystr!("input")) { + Some(Yaml::String(input)) => input, + Some(_) => { + return Err(format!( + "{}'s `{}` key MUST be a string", + CONFIG_NAME, INPUT_KEY + )); + } + None => { + return Err(format!("{} MUST have a `{}` key", CONFIG_NAME, INPUT_KEY)); + } + }; + + let output_folder = match config_yaml.get(ystr!("output")) { + Some(Yaml::String(input)) => input, + Some(_) => { + return Err(format!( + "{}'s `{}` key MUST be a string", + CONFIG_NAME, OUTPUT_KEY + )); + } + None => { + return Err(format!("{} MUST have a `{}` key", CONFIG_NAME, OUTPUT_KEY)); + } + }; + + // Check that input & output are valid folders + match ( + Path::new(input_folder).is_dir(), + Path::new(output_folder).is_dir(), + ) { + (true, true) => {} + (false, true) => { + return Err(format!( + "{}'s `{}` key is not a valid path to a folder", + CONFIG_NAME, INPUT_KEY + )) + } + (true, false) => { + return Err(format!( + "{}'s `{}` key is not a valid path to a folder", + CONFIG_NAME, OUTPUT_KEY + )) + } + (false, false) => { + return Err(format!( + "{}'s `{}` and `{}` keys are not valid paths to a folder", + CONFIG_NAME, INPUT_KEY, OUTPUT_KEY + )) + } + }; + + // Get template file + let template_file = match config_yaml.get(ystr!("template")) { + Some(Yaml::String(v)) => v, + Some(_) => { + return Err(format!( + "{}'s `{}` key MUST be a string", + CONFIG_NAME, TEMPLATE_KEY + )); + } + None => { + return Err(format!( + "{} MUST have a `{}` key", + CONFIG_NAME, TEMPLATE_KEY + )); + } + }; + + // Check template file exists + if !(Path::new(template_file).is_file()) { + return Err(format!( + "{}'s `{}` key MUST point to a file ({})", + CONFIG_NAME, TEMPLATE_KEY, template_file + )); + } + + Ok(Config { + input: input_folder.clone(), + output: output_folder.clone(), + template: template_file.clone(), + }) +} diff --git a/src/main.rs b/src/main.rs index e3496e1..cc4eab2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,16 @@ +use config::CONFIG_NAME; use std::{fs, path::Path}; use yaml_rust::Yaml; +use crate::config::{INPUT_KEY, OUTPUT_KEY}; + +mod config; mod generator; mod pages; mod processor; mod sidebar; mod utils; -const CONFIG_NAME: &str = "md-docs.config.yaml"; -const INPUT_KEY: &str = "input"; -const OUTPUT_KEY: &str = "output"; - /// Creates a `YAML::String` from a `&str` macro_rules! ystr { ($str:literal) => { @@ -33,71 +33,13 @@ fn main() { } }; - let config_yaml = match yaml_rust::YamlLoader::load_from_str(&config_str) { - Ok(y) => { - let document = &y[0]; - let Yaml::Hash(hash) = document - else { - eprintln!("{} doesn't contain a hash as first value.", CONFIG_NAME); - return; - }; - - hash.clone() + match config::parse(&config_str) { + Ok(config) => { + processor::search_config_file(&config); } - Err(error) => { - eprintln!("{} doesn't contain valid YAML.\n{:?}", CONFIG_NAME, error); + Err(reason) => { + eprintln!("{}", reason); return; } - }; - - let input_folder = match config_yaml.get(ystr!("input")) { - Some(Yaml::String(input)) => input, - Some(_) => { - eprintln!("{}'s `{}` key MUST be a string", CONFIG_NAME, INPUT_KEY); - return; - } - None => { - eprintln!("{} MUST have a `{}` key", CONFIG_NAME, INPUT_KEY); - return; - } - }; - - let output_folder = match config_yaml.get(ystr!("output")) { - Some(Yaml::String(input)) => input, - Some(_) => { - eprintln!("{}'s `{}` key MUST be a string", CONFIG_NAME, OUTPUT_KEY); - return; - } - None => { - eprintln!("{} MUST have a `{}` key", CONFIG_NAME, OUTPUT_KEY); - return; - } - }; - - let input_folder = Path::new(input_folder); - let output_folder = Path::new(output_folder); - - match (input_folder.is_dir(), output_folder.is_dir()) { - (true, true) => { - processor::search_config_file(&input_folder, input_folder, output_folder); - } - (false, true) => { - eprintln!( - "{}'s `{}` key is not a valid path to a folder", - CONFIG_NAME, INPUT_KEY - ) - } - (true, false) => { - eprintln!( - "{}'s `{}` key is not a valid path to a folder", - CONFIG_NAME, OUTPUT_KEY - ) - } - (false, false) => { - eprintln!( - "{}'s `{}` and `{}` keys are not valid paths to a folder", - CONFIG_NAME, INPUT_KEY, OUTPUT_KEY - ) - } } } diff --git a/src/pages/md_compiler.rs b/src/pages/md_compiler.rs index 5f1d4d6..80e99cf 100644 --- a/src/pages/md_compiler.rs +++ b/src/pages/md_compiler.rs @@ -4,15 +4,17 @@ use std::{ path::{Path, PathBuf}, }; -use crate::{generator::Printable, sidebar::SidebarGenerator}; +use crate::{generator::Printable, sidebar::SidebarGenerator, config::Config}; /// ## Parameters /// +/// - `config`: Config struct with the parsed values /// - `file`: Path to the MD file to compile -/// - `input_folder`: Path to the input folder passed as parameter of the program -/// - `output_folder`: Path to the output folder passed as parameter of the program /// - `file_tree_html`: HTML code of the file tree to be inserted into the generated HTML -pub fn compile(file: &PathBuf, input_folder: &Path, output_folder: &Path, file_tree_html: &String) { +pub fn compile(config: &Config,file: &PathBuf, file_tree_html: &String) { + let input_folder = Path::new(&config.input); + let output_folder = Path::new(&config.output); + // /home/fernando/misti/docs/markdown let input_folder = input_folder.canonicalize().unwrap(); diff --git a/src/pages/mod.rs b/src/pages/mod.rs index b8a6a77..dca7a9f 100644 --- a/src/pages/mod.rs +++ b/src/pages/mod.rs @@ -2,7 +2,7 @@ use std::path::Path; use yaml_rust::Yaml; -use crate::utils; +use crate::{utils, config::{self, Config}}; mod md_compiler; @@ -133,10 +133,9 @@ pub fn generate_pages_html(file_tree: &Node, current_path: &Path) -> String { } pub fn compile_md_to_html( + config: &Config, file_tree: &Node, current_path: &Path, - input_folder: &Path, - output_folder: &Path, file_tree_html: &String, ) { match file_tree { @@ -144,7 +143,7 @@ pub fn compile_md_to_html( let mut file_path = current_path.canonicalize().unwrap(); file_path.push(format!("{}.md", file.path)); - md_compiler::compile(&file_path, input_folder, output_folder, file_tree_html); + md_compiler::compile(config, &file_path, file_tree_html); } Node::File(_) => { panic!("YAML: A file cannot have an empty `path` key") @@ -152,20 +151,19 @@ pub fn compile_md_to_html( Node::Folder(folder) if folder.path != "" => { let mut new_path = current_path.canonicalize().unwrap(); new_path.push(folder.path); - utils::ensure_folder_exists(&new_path, input_folder, output_folder) + utils::ensure_folder_exists(config, &new_path) .expect("SHOULD be able to create folder"); for node in folder.children.iter() { - compile_md_to_html(node, &new_path, input_folder, output_folder, file_tree_html); + compile_md_to_html(config, node, &new_path, file_tree_html); } } Node::Folder(folder) => { for node in folder.children.iter() { compile_md_to_html( + config, node, ¤t_path, - input_folder, - output_folder, file_tree_html, ); } diff --git a/src/processor.rs b/src/processor.rs index d8d6e92..91b73be 100644 --- a/src/processor.rs +++ b/src/processor.rs @@ -1,3 +1,4 @@ +use crate::config::Config; use crate::pages::{compile_md_to_html, generate_pages_html, parse_yaml}; use crate::utils; use std::{fs, path::Path}; @@ -9,10 +10,16 @@ enum EntryFound { None, } -// Traverses the current path searching for a YAML file -pub fn search_config_file(current_path: &Path, input_folder: &Path, output_folder: &Path) { +pub fn search_config_file(config: &Config) { + let current_folder = Path::new(&config.input); + + search_config_file_impl(config, current_folder) +} + +// Recursively traverses the current folder searching a YAML file +pub fn search_config_file_impl(config: &Config, current_folder: &Path) { // Iterate over all the files searching for a YAML file - let result = current_path + let result = current_folder .read_dir() .unwrap() .fold(&EntryFound::None, |acc, next| { @@ -22,7 +29,11 @@ pub fn search_config_file(current_path: &Path, input_folder: &Path, output_folde match (acc, is_file, ext) { (EntryFound::YamlFile, true, Some(x)) if x == "yaml" => { - panic!("FOUND A SECOND YAML FILE!!!") + panic!( + "Inside the folder {} a second YAML file was found ({}).", + current_folder.to_str().unwrap(), + p.to_str().unwrap() + ); } (EntryFound::YamlFile, _, _) => acc, (EntryFound::OtherFile, true, Some(x)) if x == "yaml" => &EntryFound::YamlFile, @@ -35,26 +46,29 @@ pub fn search_config_file(current_path: &Path, input_folder: &Path, output_folde match result { // If a file other than a YAML file is found, panic EntryFound::OtherFile => panic!( - "Found an orphan file without a YAML parent at {:?}", - current_path + "Found an orphan file without a YAML parent inside folder {}", + current_folder.to_str().unwrap() ), // Process the YAML file - EntryFound::YamlFile => process_yaml(current_path, input_folder, output_folder), + EntryFound::YamlFile => process_yaml(config, current_folder), // No files found, recursively read children folders EntryFound::None => { - for entry in current_path.read_dir().unwrap() { - // Should always succeed, and countain a folder + for entry in current_folder.read_dir().unwrap() { + // Should always succeed, and contain a folder let x = entry.unwrap(); let path = x.path(); - utils::ensure_folder_exists(&path, input_folder, output_folder).unwrap(); - search_config_file(&path, input_folder, output_folder); + utils::ensure_folder_exists(config, &path).unwrap(); + search_config_file_impl(config, &path); } } }; } -fn process_yaml(current_path: &Path, input_folder: &Path, output_folder: &Path) { +fn process_yaml(config: &Config, current_path: &Path) { + let input_folder = Path::new(&config.input); + let output_folder = Path::new(&config.output); + // // Read YAML file // @@ -88,10 +102,9 @@ fn process_yaml(current_path: &Path, input_folder: &Path, output_folder: &Path) // Compile MD to HTML // compile_md_to_html( + config, &file_tree, current_path, - input_folder, - output_folder, &tree_html, ); } diff --git a/src/utils.rs b/src/utils.rs index ac2720e..f31763c 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,7 +2,7 @@ use std::{fs, path::Path}; use markdown::mdast::Node; -use crate::generator::Printable; +use crate::{generator::Printable, config::{self, Config}}; pub fn to_html_fragment(text: &String) -> String { text.clone().replace(" ", "-") @@ -29,10 +29,12 @@ pub fn collect_children_text(vec: &Vec) -> String { } pub fn ensure_folder_exists( + config: &Config, folder: &Path, - input_folder: &Path, - output_folder: &Path, ) -> Result<(), String> { + let input_folder = Path::new(&config.input); + let output_folder = Path::new(&config.output); + // /home/fernando/misti/docs/markdown let input_folder = input_folder.canonicalize().unwrap();