use std::path::Path;
use fs_err as fs;
use indicatif::ProgressBar;
use rustdoc_types::{Item, ItemEnum};
use crate::error::Error;
use crate::generator::breadcrumbs::BreadcrumbGenerator;
use crate::generator::context::GeneratorContext;
use crate::generator::module::ModuleRenderer;
pub struct NestedGenerator<'a> {
ctx: &'a GeneratorContext<'a>,
output_dir: &'a Path,
progress: &'a ProgressBar,
}
impl<'a> NestedGenerator<'a> {
pub const fn new(
ctx: &'a GeneratorContext<'a>,
output_dir: &'a Path,
progress: &'a ProgressBar,
) -> Self {
Self {
ctx,
output_dir,
progress,
}
}
pub fn generate(&self, root: &Item) -> Result<(), Error> {
let renderer = ModuleRenderer::new(self.ctx, "index.md", true);
let index_content = renderer.render(root);
let index_path = self.output_dir.join("index.md");
fs::write(&index_path, index_content).map_err(Error::FileWrite)?;
self.progress.inc(1);
if let ItemEnum::Module(module) = &root.inner {
for item_id in &module.items {
if let Some(item) = self.ctx.krate.index.get(item_id)
&& let ItemEnum::Module(_) = &item.inner
&& self.ctx.should_include_item(item)
{
self.generate_module(item, self.output_dir, Vec::new())?;
}
}
}
Ok(())
}
fn generate_module(
&self,
item: &Item,
parent_dir: &Path,
module_path: Vec<String>,
) -> Result<(), Error> {
let name = item.name.as_deref().unwrap_or("unnamed");
let module_dir = parent_dir.join(name);
fs::create_dir_all(&module_dir).map_err(Error::CreateDir)?;
let mut current_path = module_path;
current_path.push(name.to_string());
let current_file = format!("{}/index.md", current_path.join("/"));
let crate_name = self
.ctx
.krate
.index
.get(&self.ctx.krate.root)
.and_then(|r| r.name.as_deref())
.unwrap_or("crate");
let breadcrumb_gen = BreadcrumbGenerator::new(¤t_path, crate_name);
let breadcrumbs = breadcrumb_gen.generate();
let renderer = ModuleRenderer::new(self.ctx, ¤t_file, false);
let module_content = renderer.render(item);
let content = format!("{breadcrumbs}{module_content}");
let file_path = module_dir.join("index.md");
fs::write(&file_path, content).map_err(Error::FileWrite)?;
self.progress.inc(1);
if let ItemEnum::Module(module) = &item.inner {
for sub_id in &module.items {
if let Some(sub_item) = self.ctx.krate.index.get(sub_id)
&& let ItemEnum::Module(_) = &sub_item.inner
&& self.ctx.should_include_item(sub_item)
{
self.generate_module(sub_item, &module_dir, current_path.clone())?;
}
}
}
Ok(())
}
}