use crate::config::BeadsConfig;
pub fn rig_of(db_name: &str) -> &str {
db_name.strip_prefix("beads_").unwrap_or(db_name)
}
pub fn bead_file_path(rig: &str, bead_id: &str) -> String {
format!("beads:{rig}:{bead_id}")
}
pub fn bead_file_paths(
config: &BeadsConfig,
bead_id: &str,
rig_filter: Option<&str>,
) -> Vec<String> {
config
.databases
.iter()
.map(|db| rig_of(db))
.filter(|rig| rig_filter.is_none_or(|want| want == *rig))
.map(|rig| bead_file_path(rig, bead_id))
.collect()
}
fn sql_literal(value: &str) -> String {
value.replace('\'', "''")
}
pub(crate) fn issues_where_clause(config: &BeadsConfig, only_id: Option<&str>) -> String {
let mut conditions = Vec::new();
if !config.include_closed {
conditions.push("status NOT IN ('closed', 'deleted')".to_string());
} else {
conditions.push("status != 'deleted'".to_string());
}
if config.max_age_days > 0 {
conditions.push(format!(
"(status NOT IN ('closed', 'deleted') OR created_at >= DATE_SUB(NOW(), INTERVAL {} DAY))",
config.max_age_days
));
}
if let Some(id) = only_id {
conditions.push(format!("id = '{}'", sql_literal(id)));
}
if conditions.is_empty() {
String::new()
} else {
format!(" WHERE {}", conditions.join(" AND "))
}
}
pub(crate) fn bead_visible(config: &BeadsConfig, status: &str, created_at: Option<&str>) -> bool {
if status == "deleted" {
return false;
}
let closed = status == "closed";
if closed && !config.include_closed {
return false;
}
if config.max_age_days > 0 && closed {
if let Some(age_days) = age_in_days(created_at) {
if age_days > f64::from(config.max_age_days) {
return false;
}
}
}
true
}
fn age_in_days(created_at: Option<&str>) -> Option<f64> {
let raw = created_at?;
let parsed = chrono::DateTime::parse_from_rfc3339(raw)
.ok()
.map(|d| d.with_timezone(&chrono::Utc))
.or_else(|| {
chrono::NaiveDateTime::parse_from_str(raw, "%Y-%m-%d %H:%M:%S")
.ok()
.map(|n| n.and_utc())
})
.or_else(|| {
chrono::NaiveDate::parse_from_str(raw, "%Y-%m-%d")
.ok()
.and_then(|d| d.and_hms_opt(0, 0, 0))
.map(|n| n.and_utc())
})?;
Some((chrono::Utc::now() - parsed).num_seconds() as f64 / 86_400.0)
}