use std::collections::HashMap;
use std::fmt;
use askama::Template;
use color_eyre::{eyre::Context, Result};
use crate::extra_fields::DocTextStatus;
use crate::templating::DocumentVariant;
use crate::AbstractTicket;
const THROWAWAY_COMPONENTS: [&str; 3] = ["releng", "(none)", "Documentation"];
const THROWAWAY_PREFIXES: [&str; 2] = ["doc-", "Red_Hat_Enterprise_Linux-Release_Notes"];
const COMPONENT_PLACEHOLDER: &str = "other";
#[derive(Eq, PartialEq, PartialOrd, Ord)]
struct TicketsByComponent<'a> {
component: PresentableComponent<'a>,
signatures: Vec<String>,
}
#[derive(Template)]
#[template(path = "summary-list.adoc", escape = "none")]
struct SummaryList<'a> {
tickets_by_components: &'a [TicketsByComponent<'a>],
}
#[derive(Eq, Hash, PartialEq, PartialOrd, Ord)]
enum PresentableComponent<'a> {
External(&'a str),
Internal,
}
impl<'a> PresentableComponent<'a> {
fn from(component: &'a str) -> Self {
if THROWAWAY_COMPONENTS.contains(&component)
|| THROWAWAY_PREFIXES
.iter()
.any(|prefix| component.starts_with(prefix))
{
Self::Internal
} else {
Self::External(component)
}
}
}
impl fmt::Display for PresentableComponent<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PresentableComponent::External(component) => write!(f, "`{component}`"),
PresentableComponent::Internal => write!(f, "{COMPONENT_PLACEHOLDER}"),
}
}
}
fn groups<'a>(
tickets: &[&'a AbstractTicket],
variant: DocumentVariant,
) -> Vec<TicketsByComponent<'a>> {
let mut components: HashMap<PresentableComponent, Vec<String>> = HashMap::new();
tickets
.iter()
.filter(|ticket| filter_doc_text(ticket, variant))
.for_each(|ticket| {
for component in &ticket.components {
let presentable = PresentableComponent::from(component);
components
.entry(presentable)
.and_modify(|c| c.push(ticket.xref()))
.or_insert_with(|| vec![ticket.xref()]);
}
});
components
.into_iter()
.map(|(component, signatures)| TicketsByComponent {
component,
signatures,
})
.collect()
}
fn filter_doc_text(ticket: &AbstractTicket, variant: DocumentVariant) -> bool {
match variant {
DocumentVariant::Internal => true,
DocumentVariant::External => ticket.doc_text_status == DocTextStatus::Approved,
}
}
pub fn appendix(tickets: &[&AbstractTicket], variant: DocumentVariant) -> Result<String> {
let mut groups = groups(tickets, variant);
groups.sort_unstable();
let template = SummaryList {
tickets_by_components: &groups,
};
template
.render()
.map(|content| {
format!(
":_mod-docs-content-type: REFERENCE\n[id=\"list_of_tickets_by_component\"]\n{content}"
)
})
.wrap_err("Failed to prepare the ticket appendix.")
}