#![allow(private_interfaces)]
pub(super) use crate::index::archive::{extract_body, extract_date_from_archive_path};
pub(super) fn archive_source_names(config: &crate::config::ArchiveConfig) -> Vec<String> {
config.sources.iter().map(|s| s.name.clone()).collect()
}
pub(super) fn archive_source_paths(config: &crate::config::ArchiveConfig) -> Vec<(String, String)> {
config
.sources
.iter()
.filter(|s| !s.path.is_empty())
.map(|s| (s.name.clone(), s.path.clone()))
.collect()
}
pub(super) fn find_record_by_id(root: &std::path::Path, id: &str) -> Option<(String, String)> {
find_record_recursive(root, root, id)
}
pub(super) fn find_record_recursive(
root: &std::path::Path,
dir: &std::path::Path,
id: &str,
) -> Option<(String, String)> {
let entries = std::fs::read_dir(dir).ok()?;
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
if let Some(found) = find_record_recursive(root, &path, id) {
return Some(found);
}
} else if path
.file_stem()
.is_some_and(|s| s.to_string_lossy().contains(id))
{
let content = std::fs::read_to_string(&path).ok()?;
let rel = path.strip_prefix(root).ok()?;
return Some((content, rel.to_string_lossy().to_string()));
}
}
None
}
pub(super) fn extract_bead_field(content: &str, prefix: &str) -> String {
content
.lines()
.find(|line| line.contains(prefix))
.and_then(|line| {
let start = line.find(prefix)? + prefix.len();
let rest = &line[start..];
let end = rest.find(" | ").unwrap_or(rest.len());
Some(rest[..end].trim().to_string())
})
.unwrap_or_else(|| "unknown".to_string())
}
pub(super) fn clean_bead_snippet(content: &str, max_len: usize) -> String {
let cleaned: String = content
.lines()
.filter(|line| {
let trimmed = line.trim();
!(trimmed.starts_with("Status: ") && trimmed.contains(" | "))
&& !trimmed.starts_with("Priority: P")
&& !trimmed.starts_with("Assignee: ")
&& !trimmed.starts_with("Comments:")
&& !trimmed.starts_with("--- ")
&& !trimmed.starts_with("Notes:")
})
.collect::<Vec<_>>()
.join("\n");
let trimmed = cleaned.trim();
if trimmed.len() <= max_len {
trimmed.to_string()
} else {
let mut end = max_len;
while end > 0 && !trimmed.is_char_boundary(end) {
end -= 1;
}
format!("{}...", &trimmed[..end])
}
}