From 3e4cd2468b3be17a6f5c18b9e43bcc37b003d728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Such=C3=A1nek?= Date: Thu, 27 Jun 2024 22:27:35 +0200 Subject: [PATCH] Generate top-level chapters even if empty; #1 --- src/lib.rs | 59 ++++++++++++++++++++++++++++++++++-------- src/templating.rs | 65 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 93 insertions(+), 31 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 59e510f..4b9f91a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -188,22 +188,61 @@ impl Document { // Make sure that the output directory exists. fs::create_dir_all(generated_dir)?; - for module in modules { - let out_file = generated_dir.join(&module.file_name); + Self::write_chapters(modules, summary, generated_dir)?; + + // Save the appendix. + let summary_file = generated_dir.join("ref_list-of-tickets-by-component.adoc"); + log::debug!("Writing file: {}", summary_file.display()); + fs::write(summary_file, summary).wrap_err("Failed to write generated summary appendix.")?; + + Ok(()) + } + + fn write_chapters(modules: &[Module], summary: &str, generated_dir: &Path) -> Result<()> { + for chapter in modules { + let out_file = generated_dir.join(&chapter.file_name()); log::debug!("Writing file: {}", out_file.display()); - fs::write(out_file, &module.text).wrap_err("Failed to write generated module.")?; + + let text = match chapter { + Module::WithContent { text, .. } => text, + Module::Blank { .. } => "", + }; + + fs::write(out_file, text).wrap_err("Failed to write generated module.")?; // If the currently processed module is an assembly, // recursively descend into the assembly and write its included modules. - if let Some(included_modules) = &module.included_modules { - Self::write_variant(included_modules, summary, generated_dir)?; + if let Module::WithContent { + included_modules, .. + } = chapter + { + if let Some(included_modules) = included_modules { + Self::write_modules(included_modules, summary, generated_dir)?; + } } + } - // Save the appendix. - let summary_file = generated_dir.join("ref_list-of-tickets-by-component.adoc"); - log::debug!("Writing file: {}", summary_file.display()); - fs::write(summary_file, summary) - .wrap_err("Failed to write generated summary appendix.")?; + Ok(()) + } + + fn write_modules(modules: &[Module], summary: &str, generated_dir: &Path) -> Result<()> { + for module in modules { + if let Module::WithContent { + file_name, + text, + included_modules, + } = module + { + let out_file = generated_dir.join(file_name); + log::debug!("Writing file: {}", out_file.display()); + fs::write(out_file, text).wrap_err("Failed to write generated module.")?; + + // If the currently processed module is an assembly, + // recursively descend into the assembly and write its included modules. + if let Some(included_modules) = included_modules { + Self::write_modules(included_modules, summary, generated_dir)?; + } + } } Ok(()) diff --git a/src/templating.rs b/src/templating.rs index 8955ad7..62c421b 100644 --- a/src/templating.rs +++ b/src/templating.rs @@ -58,16 +58,33 @@ pub enum DocumentVariant { /// The representation of a module, before being finally rendered. #[derive(Clone, Debug, PartialEq)] -pub struct Module { - pub file_name: String, - pub text: String, - pub included_modules: Option>, +pub enum Module { + WithContent { + file_name: String, + text: String, + included_modules: Option>, + }, + Blank { + file_name: String, + }, } impl Module { /// The AsciiDoc include statement to include this module elsewhere. pub fn include_statement(&self) -> String { - format!("include::{}[leveloffset=+1]", &self.file_name) + format!("include::{}[leveloffset=+1]", self.file_name()) + } + pub fn file_name(&self) -> &str { + match self { + Self::WithContent { file_name, .. } => file_name, + Self::Blank { file_name, .. } => file_name, + } + } + fn has_content(&self) -> bool { + match self { + Self::WithContent { .. } => true, + Self::Blank { .. } => false, + } } } @@ -201,7 +218,7 @@ impl config::Section { /// Convert the section into either a leaf module, or into an assembly and all /// the modules that it includes, recursively. /// - /// Returns `None` if the module or assembly captured no release notes at all. + /// Returns `Blank` if the module or assembly captured no release notes at all. fn modules( &self, tickets: &[&AbstractTicket], @@ -209,7 +226,7 @@ impl config::Section { variant: DocumentVariant, with_priv_footnote: bool, ticket_stats: &mut HashMap, u32>, - ) -> Option { + ) -> Module { let matching_tickets: Vec<&AbstractTicket> = tickets .iter() .filter(|&&t| self.matches_ticket(t)) @@ -228,7 +245,7 @@ impl config::Section { let file_name = format!("assembly_{module_id}.adoc"); let included_modules: Vec = sections .iter() - .filter_map(|s| { + .map(|s| { s.modules( &matching_tickets, Some(&module_id), @@ -237,10 +254,11 @@ impl config::Section { ticket_stats, ) }) + .filter(|module| module.has_content()) .collect(); - // If the assembly receives no modules, because all its modules are empty, return None. + // If the assembly receives no modules, because all its modules are empty, return Blank. if included_modules.is_empty() { - None + Module::Blank { file_name } } else { let include_statements: Vec = included_modules .iter() @@ -259,28 +277,33 @@ impl config::Section { .render() .expect("Failed to render an assembly template."); - Some(Module { + Module::WithContent { file_name, text, included_modules: Some(included_modules), - }) + } } // If the section includes no sections, treat it as a leaf, reference module. } else { - // If the module receives no release notes and its body is empty, return None. + // If the module receives no release notes and its body is empty, return Blank. // Otherwise, return the module formatted with its release notes. - self.render( + let text = self.render( &module_id, tickets, variant, with_priv_footnote, ticket_stats, - ) - .map(|text| Module { - file_name: format!("ref_{module_id}.adoc"), - text, - included_modules: None, - }) + ); + let file_name = format!("ref_{module_id}.adoc"); + if let Some(text) = text { + Module::WithContent { + file_name, + text, + included_modules: None, + } + } else { + Module::Blank { file_name } + } } } @@ -369,7 +392,7 @@ pub fn format_document( let chapters: Vec<_> = template .chapters .iter() - .filter_map(|section| { + .map(|section| { section.modules( tickets, None,