Use a Config struct across the code

master
Araozu 2023-06-25 19:43:14 -05:00
parent 5bcc37f32c
commit 50085e8ce2
8 changed files with 199 additions and 112 deletions

22
README.md Normal file
View File

@ -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
}
}
```

View File

@ -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
}
```

124
src/config.rs Normal file
View File

@ -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<Config, String> {
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(),
})
}

View File

@ -1,16 +1,16 @@
use config::CONFIG_NAME;
use std::{fs, path::Path}; use std::{fs, path::Path};
use yaml_rust::Yaml; use yaml_rust::Yaml;
use crate::config::{INPUT_KEY, OUTPUT_KEY};
mod config;
mod generator; mod generator;
mod pages; mod pages;
mod processor; mod processor;
mod sidebar; mod sidebar;
mod utils; 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` /// Creates a `YAML::String` from a `&str`
macro_rules! ystr { macro_rules! ystr {
($str:literal) => { ($str:literal) => {
@ -33,71 +33,13 @@ fn main() {
} }
}; };
let config_yaml = match yaml_rust::YamlLoader::load_from_str(&config_str) { match config::parse(&config_str) {
Ok(y) => { Ok(config) => {
let document = &y[0]; processor::search_config_file(&config);
let Yaml::Hash(hash) = document
else {
eprintln!("{} doesn't contain a hash as first value.", CONFIG_NAME);
return;
};
hash.clone()
} }
Err(error) => { Err(reason) => {
eprintln!("{} doesn't contain valid YAML.\n{:?}", CONFIG_NAME, error); eprintln!("{}", reason);
return; 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
)
}
} }
} }

View File

@ -4,15 +4,17 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use crate::{generator::Printable, sidebar::SidebarGenerator}; use crate::{generator::Printable, sidebar::SidebarGenerator, config::Config};
/// ## Parameters /// ## Parameters
/// ///
/// - `config`: Config struct with the parsed values
/// - `file`: Path to the MD file to compile /// - `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 /// - `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 // /home/fernando/misti/docs/markdown
let input_folder = input_folder.canonicalize().unwrap(); let input_folder = input_folder.canonicalize().unwrap();

View File

@ -2,7 +2,7 @@ use std::path::Path;
use yaml_rust::Yaml; use yaml_rust::Yaml;
use crate::utils; use crate::{utils, config::{self, Config}};
mod md_compiler; 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( pub fn compile_md_to_html(
config: &Config,
file_tree: &Node, file_tree: &Node,
current_path: &Path, current_path: &Path,
input_folder: &Path,
output_folder: &Path,
file_tree_html: &String, file_tree_html: &String,
) { ) {
match file_tree { match file_tree {
@ -144,7 +143,7 @@ pub fn compile_md_to_html(
let mut file_path = current_path.canonicalize().unwrap(); let mut file_path = current_path.canonicalize().unwrap();
file_path.push(format!("{}.md", file.path)); 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(_) => { Node::File(_) => {
panic!("YAML: A file cannot have an empty `path` key") 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 != "" => { Node::Folder(folder) if folder.path != "" => {
let mut new_path = current_path.canonicalize().unwrap(); let mut new_path = current_path.canonicalize().unwrap();
new_path.push(folder.path); 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"); .expect("SHOULD be able to create folder");
for node in folder.children.iter() { 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) => { Node::Folder(folder) => {
for node in folder.children.iter() { for node in folder.children.iter() {
compile_md_to_html( compile_md_to_html(
config,
node, node,
&current_path, &current_path,
input_folder,
output_folder,
file_tree_html, file_tree_html,
); );
} }

View File

@ -1,3 +1,4 @@
use crate::config::Config;
use crate::pages::{compile_md_to_html, generate_pages_html, parse_yaml}; use crate::pages::{compile_md_to_html, generate_pages_html, parse_yaml};
use crate::utils; use crate::utils;
use std::{fs, path::Path}; use std::{fs, path::Path};
@ -9,10 +10,16 @@ enum EntryFound {
None, None,
} }
// Traverses the current path searching for a YAML file pub fn search_config_file(config: &Config) {
pub fn search_config_file(current_path: &Path, input_folder: &Path, output_folder: &Path) { 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 // Iterate over all the files searching for a YAML file
let result = current_path let result = current_folder
.read_dir() .read_dir()
.unwrap() .unwrap()
.fold(&EntryFound::None, |acc, next| { .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) { match (acc, is_file, ext) {
(EntryFound::YamlFile, true, Some(x)) if x == "yaml" => { (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::YamlFile, _, _) => acc,
(EntryFound::OtherFile, true, Some(x)) if x == "yaml" => &EntryFound::YamlFile, (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 { match result {
// If a file other than a YAML file is found, panic // If a file other than a YAML file is found, panic
EntryFound::OtherFile => panic!( EntryFound::OtherFile => panic!(
"Found an orphan file without a YAML parent at {:?}", "Found an orphan file without a YAML parent inside folder {}",
current_path current_folder.to_str().unwrap()
), ),
// Process the YAML file // 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 // No files found, recursively read children folders
EntryFound::None => { EntryFound::None => {
for entry in current_path.read_dir().unwrap() { for entry in current_folder.read_dir().unwrap() {
// Should always succeed, and countain a folder // Should always succeed, and contain a folder
let x = entry.unwrap(); let x = entry.unwrap();
let path = x.path(); let path = x.path();
utils::ensure_folder_exists(&path, input_folder, output_folder).unwrap(); utils::ensure_folder_exists(config, &path).unwrap();
search_config_file(&path, input_folder, output_folder); 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 // 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
// //
compile_md_to_html( compile_md_to_html(
config,
&file_tree, &file_tree,
current_path, current_path,
input_folder,
output_folder,
&tree_html, &tree_html,
); );
} }

View File

@ -2,7 +2,7 @@ use std::{fs, path::Path};
use markdown::mdast::Node; use markdown::mdast::Node;
use crate::generator::Printable; use crate::{generator::Printable, config::{self, Config}};
pub fn to_html_fragment(text: &String) -> String { pub fn to_html_fragment(text: &String) -> String {
text.clone().replace(" ", "-") text.clone().replace(" ", "-")
@ -29,10 +29,12 @@ pub fn collect_children_text(vec: &Vec<Node>) -> String {
} }
pub fn ensure_folder_exists( pub fn ensure_folder_exists(
config: &Config,
folder: &Path, folder: &Path,
input_folder: &Path,
output_folder: &Path,
) -> Result<(), String> { ) -> Result<(), String> {
let input_folder = Path::new(&config.input);
let output_folder = Path::new(&config.output);
// /home/fernando/misti/docs/markdown // /home/fernando/misti/docs/markdown
let input_folder = input_folder.canonicalize().unwrap(); let input_folder = input_folder.canonicalize().unwrap();