use std::collections::{HashMap, VecDeque};
use std::fs::{DirEntry, File};
use std::io::Write;
use std::path::PathBuf;
use std::time::{Duration, Instant};
use anyhow::{Context, Result};
use crate::{ConfigParser, Doc, GenCommand, GenTypes, Generator, MarkdownGenerator, TomlParser};
pub fn generate_files(comm: GenCommand) -> Result<GenerationResults> {
log::debug!("Beginning single threaded recursive search");
let mut file_count: u128 = 0;
let mut dir_count: u128 = 0;
let start_time = Instant::now();
let mut nodes_processed = Vec::new();
let mut dir_queue = VecDeque::new();
dir_queue.push_back(PathBuf::from(comm.source_dir.clone()));
let parser: Box<dyn ConfigParser> = Box::new(TomlParser::default());
let generator: Box<dyn Generator> = match comm.doc_type {
GenTypes::Markdown => Box::new(MarkdownGenerator::default()),
};
while let Some(path) = dir_queue.pop_front() {
log::trace!("recurring new directory: {:?}", path);
dir_count += 1;
for file in std::fs::read_dir(path).context("Failure while opening directory")? {
process_file(
&comm,
&mut dir_queue,
&*parser,
&*generator,
file?,
&mut nodes_processed,
)?;
file_count += 1;
}
}
log::trace!("queue size: {}", dir_queue.len());
if comm.readme && !nodes_processed.is_empty() {
log::trace!("Creating README");
println!("Generating NODES_README.md");
generate_readme(&comm, &nodes_processed, &*generator)
.context("Error while writing NODES_README.md")?;
}
Ok(GenerationResults {
files_read: file_count,
dir_read: dir_count,
duration: Instant::now().duration_since(start_time),
nodes_processed,
})
}
pub struct GenerationResults {
pub files_read: u128,
pub dir_read: u128,
pub duration: Duration,
pub nodes_processed: Vec<Doc>,
}
fn process_file(
comm: &GenCommand,
dir_queue: &mut VecDeque<PathBuf>,
parser: &dyn ConfigParser,
generator: &dyn Generator,
file: DirEntry,
nodes_processed: &mut Vec<Doc>,
) -> Result<bool> {
if file.path().is_dir() && comm.recurse {
log::trace!("adding dir to recurse: {:?}", file.path());
dir_queue.push_back(file.path());
return Ok(false);
}
if parser.match_filename(
file.file_name()
.to_str()
.context("Filename contains invalid chars")?,
) {
let path = file.path();
log::debug!("Reading config: {path:?}");
let config = parser.parse_path(path)?;
nodes_processed.push(config.clone());
let docs = generator.generate_string(config)?;
log::debug!("Generated Doc");
for (name, doc) in docs {
let mut path = PathBuf::from(comm.dest_dir.clone());
path.push(generator.add_file_extension(&name));
log::debug!("Writing to: {path:?}");
let mut file = File::create(path)?;
file.write_all(doc.as_bytes())?;
println!("Created doc for {name}");
}
return Ok(true);
}
Ok(false)
}
fn generate_readme(comm: &GenCommand, docs: &[Doc], generator: &dyn Generator) -> Result<()> {
let mut path = PathBuf::from(comm.dest_dir.clone());
path.push("NODES_README.md");
let mut file = File::create(path)?;
let mut fused_docs: HashMap<&str, Vec<&Doc>> = HashMap::new();
docs.iter().for_each(|doc| {
fused_docs.entry(&doc.package_name).or_default().push(doc);
});
for (package, docs) in fused_docs.iter() {
file.write_all(format!("# Nodes in package {}\n", package).as_bytes())?;
for doc in docs {
for node in &doc.nodes {
let mut file_path = PathBuf::from(comm.dest_dir.clone());
file_path.push(generator.add_file_extension(&node.node_name));
file.write_all(format!("- [{}](", &node.node_name).as_bytes())?;
file.write_all(
file_path
.to_str()
.ok_or_else(|| std::io::Error::from(std::io::ErrorKind::Other))?
.as_bytes(),
)?;
file.write_all(&[b')', b'\n'])?;
}
}
file.write_all(&[b'\n'])?;
}
Ok(())
}