use anyhow::Result;
use colored::Colorize;
use crate::variant::AnnotatedVariant;
pub fn print(variant: &AnnotatedVariant) -> Result<()> {
let rsid = variant.rsid.as_deref().unwrap_or(".");
let gene = variant.gene.as_deref().unwrap_or("Unknown gene");
let significance = variant
.clinvar
.as_ref()
.map(|c| c.significance.as_str())
.unwrap_or("No clinical data");
let sig_colored = colorize_significance(significance);
let width = 62;
let border = "─".repeat(width);
println!("╭{border}╮");
println!(
"│ {rsid} · {gene} · {sig_colored}{:>pad$}│",
"",
pad = width.saturating_sub(rsid.len() + gene.len() + significance.len() + 8)
);
println!("├{border}┤");
println!(
"│ {:<label_width$} {}:{} ({}){}│",
"Location".bold(),
variant.chrom,
variant.pos,
variant.assembly,
" ".repeat(
width
.saturating_sub(13 + variant.chrom.len() + format!("{}", variant.pos).len() + variant.assembly.len() + 4)
),
label_width = 12,
);
println!(
"│ {:<12} {} > {} {}│",
"Change".bold(),
variant.reference,
variant.alt,
" ".repeat(width.saturating_sub(12 + variant.reference.len() + variant.alt.len() + 7)),
);
println!(
"│ {:<12} {}{}│",
"Gene".bold(),
gene,
" ".repeat(width.saturating_sub(14 + gene.len())),
);
if let Some(clinvar) = &variant.clinvar {
println!("│{:>width$}│", "", width = width);
println!(
"│ {}{}│",
"Clinical".bold(),
" ".repeat(width.saturating_sub(10)),
);
let stars = "★".repeat(clinvar.review_stars as usize);
let empty_stars = "☆".repeat(4 - clinvar.review_stars as usize);
println!(
"│ ├─ {:<10} {} ({}{} reviewed){}│",
"ClinVar",
sig_colored,
stars,
empty_stars,
" ".repeat(
width
.saturating_sub(18 + significance.len() + 4 + 10)
),
);
if !clinvar.conditions.is_empty() {
let condition = truncate(&clinvar.conditions, 40);
println!(
"│ ├─ {:<10} {}{}│",
"Condition",
condition,
" ".repeat(width.saturating_sub(17 + condition.len())),
);
}
if let Some(reviewed) = &clinvar.last_reviewed {
println!(
"│ └─ {:<10} {}{}│",
"Reviewed",
reviewed,
" ".repeat(width.saturating_sub(17 + reviewed.len())),
);
}
}
if let Some(gnomad) = &variant.gnomad {
println!("│{:>width$}│", "", width = width);
println!(
"│ {}{}│",
"Population Frequency".bold(),
" ".repeat(width.saturating_sub(22)),
);
let af = format_af(gnomad.af_global);
println!(
"│ ├─ {:<14} {}{}│",
"Global",
af,
" ".repeat(width.saturating_sub(19 + af.len())),
);
let populations = [
("European", gnomad.af_nfe),
("African", gnomad.af_afr),
("East Asian", gnomad.af_eas),
("South Asian", gnomad.af_sas),
("Latino", gnomad.af_amr),
];
for (i, (name, af_val)) in populations.iter().enumerate() {
let af_str = format_af(*af_val);
let prefix = if i < populations.len() - 1 {
"├─"
} else {
"└─"
};
println!(
"│ {prefix} {:<14} {}{}│",
name,
af_str,
" ".repeat(width.saturating_sub(21 + af_str.len())),
);
}
}
if let Some(pharmgkb) = &variant.pharmgkb {
println!("│{:>width$}│", "", width = width);
println!(
"│ {}{}│",
"Pharmacogenomics".bold(),
" ".repeat(width.saturating_sub(18)),
);
println!(
"│ └─ {} ({}){}│",
pharmgkb.drug,
pharmgkb.evidence_level,
" ".repeat(
width.saturating_sub(7 + pharmgkb.drug.len() + pharmgkb.evidence_level.len() + 3)
),
);
}
println!("╰{border}╯");
println!();
Ok(())
}
fn colorize_significance(significance: &str) -> String {
if significance.contains("Pathogenic") && !significance.contains("Likely") {
significance.red().bold().to_string()
} else if significance.contains("Likely pathogenic") {
significance.yellow().bold().to_string()
} else if significance.contains("Benign") {
significance.green().to_string()
} else if significance.contains("Uncertain") || significance.contains("VUS") {
significance.blue().to_string()
} else {
significance.dimmed().to_string()
}
}
fn format_af(af: f64) -> String {
if af == 0.0 {
"Not observed".to_string()
} else if af < 0.0001 {
format!("{af:.6} (ultra-rare)")
} else if af < 0.01 {
let one_in = (1.0 / af).round() as u64;
format!("{af:.5} (1 in {one_in})")
} else {
format!("{af:.4} ({:.1}%)", af * 100.0)
}
}
fn truncate(s: &str, max: usize) -> String {
if s.len() <= max {
s.to_string()
} else {
format!("{}...", &s[..max - 3])
}
}