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 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
)
}
}
}

View File

@ -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();

View File

@ -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,
&current_path,
input_folder,
output_folder,
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::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,
);
}

View File

@ -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<Node>) -> 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();