use super::parser::{parse_lino, split_pipe_list};
use super::SUMMARY_TOPICS_LINO;
#[derive(Debug, Clone, Default)]
pub struct SummaryTopic {
pub display_name: String,
pub detection_keywords: Vec<String>,
pub body: String,
}
#[derive(Debug, Clone, Default)]
pub struct SummaryTopicSeeds {
pub triggers: Vec<String>,
pub reject_substrings: Vec<String>,
pub reject_exact: Vec<String>,
pub constraint_markers: Vec<String>,
pub constraint_label: String,
pub fallback_body: String,
pub topics: Vec<SummaryTopic>,
}
impl SummaryTopicSeeds {
#[must_use]
pub fn matches_trigger(&self, normalized: &str) -> bool {
let hit = self
.triggers
.iter()
.any(|t| !t.is_empty() && normalized.contains(t.as_str()));
if !hit {
return false;
}
if self
.reject_exact
.iter()
.any(|r| !r.is_empty() && normalized == r.as_str())
{
return false;
}
if self
.reject_substrings
.iter()
.any(|r| !r.is_empty() && normalized.contains(r.as_str()))
{
return false;
}
true
}
#[must_use]
pub fn pick_topic(&self, normalized: &str) -> Option<&SummaryTopic> {
self.topics.iter().find(|topic| {
topic
.detection_keywords
.iter()
.any(|keyword| !keyword.is_empty() && normalized.contains(keyword.as_str()))
})
}
#[must_use]
pub fn constraint_for(&self, normalized: &str) -> Option<&str> {
let hit = self
.constraint_markers
.iter()
.any(|m| !m.is_empty() && normalized.contains(m.as_str()));
hit.then_some(self.constraint_label.as_str())
}
#[must_use]
pub fn render_fallback(&self, topic: &str) -> String {
if self.fallback_body.is_empty() {
return format!("{topic} summary recorded.");
}
self.fallback_body.replace("<topic>", topic)
}
}
#[must_use]
pub fn summary_topic_seeds() -> SummaryTopicSeeds {
let tree = parse_lino(SUMMARY_TOPICS_LINO);
let mut seeds = SummaryTopicSeeds::default();
let Some(root) = tree.children.iter().find(|c| c.name == "summary_topics") else {
return seeds;
};
seeds.triggers = split_pipe_list(root.find_child_value("trigger"))
.into_iter()
.map(|t| t.to_lowercase())
.collect();
for child in &root.children {
match child.name.as_str() {
"reject_substring" => {
for value in split_pipe_list(&child.id) {
seeds.reject_substrings.push(value.to_lowercase());
}
}
"reject_exact" => seeds.reject_exact.push(child.id.to_lowercase()),
"constraint_marker" => seeds.constraint_markers.push(child.id.to_lowercase()),
"constraint_label" => seeds.constraint_label.clone_from(&child.id),
"fallback_body" => seeds.fallback_body.clone_from(&child.id),
"topic" => {
if child.id.is_empty() {
continue;
}
let body = child.find_child_value("body").to_owned();
if body.is_empty() {
continue;
}
seeds.topics.push(SummaryTopic {
display_name: child.id.clone(),
detection_keywords: split_pipe_list(
child.find_child_value("detection_keywords"),
)
.into_iter()
.map(|k| k.to_lowercase())
.collect(),
body,
});
}
_ => {}
}
}
seeds
}