Skip to main content

wax/
output.rs

1use crate::error::Result;
2use crate::model::{
3    CandidateRecord, CollectorsOutput, DigOutput, ItemKind, LibraryOutput, ResolveOutput,
4};
5
6pub enum OutputFormat {
7    Table,
8    Json,
9    Csv,
10}
11
12pub fn print_resolve(output: &ResolveOutput, format: OutputFormat) -> Result<()> {
13    match format {
14        OutputFormat::Json => println!("{}", serde_json::to_string_pretty(output)?),
15        OutputFormat::Table | OutputFormat::Csv => {
16            println!("Platform: {}", output.seed.platform.as_str());
17            println!(
18                "Type    : {}",
19                match output.seed.kind {
20                    ItemKind::Album => "album",
21                    ItemKind::Track => "track",
22                    ItemKind::Playlist => "playlist",
23                }
24            );
25            println!("Title   : {}", output.seed.title);
26            println!("Artist  : {}", output.seed.artist);
27            println!("URL     : {}", output.seed.url);
28            if !output.seed.tags.is_empty() {
29                println!("Tags    : {}", output.seed.tags.join(", "));
30            }
31        }
32    }
33    Ok(())
34}
35
36pub fn print_collectors(output: &CollectorsOutput, format: OutputFormat) -> Result<()> {
37    match format {
38        OutputFormat::Json => println!("{}", serde_json::to_string_pretty(output)?),
39        OutputFormat::Csv => {
40            let mut writer = csv::Writer::from_writer(std::io::stdout());
41            writer.write_record(["handle", "url", "display_name"])?;
42            for collector in &output.collectors {
43                writer.write_record([
44                    collector.handle.as_str(),
45                    collector.url.as_str(),
46                    collector.display_name.as_deref().unwrap_or(""),
47                ])?;
48            }
49            writer.flush()?;
50        }
51        OutputFormat::Table => {
52            println!("Seed: {} - {}", output.seed.artist, output.seed.title);
53            println!("Collectors discovered: {}", output.collectors_discovered);
54            for collector in &output.collectors {
55                println!(
56                    "- {} {}",
57                    collector.handle,
58                    collector.display_name.as_deref().unwrap_or("")
59                );
60            }
61        }
62    }
63    Ok(())
64}
65
66pub fn print_library(output: &LibraryOutput, format: OutputFormat) -> Result<()> {
67    match format {
68        OutputFormat::Json => println!("{}", serde_json::to_string_pretty(output)?),
69        OutputFormat::Csv => {
70            let mut writer = csv::Writer::from_writer(std::io::stdout());
71            writer.write_record(["artist", "album", "url"])?;
72            for album in &output.albums {
73                writer.write_record([
74                    album.artist.as_str(),
75                    album.title.as_str(),
76                    album.url.as_str(),
77                ])?;
78            }
79            writer.flush()?;
80        }
81        OutputFormat::Table => {
82            println!("Collector: {}", output.collector_url);
83            for album in &output.albums {
84                println!("- {} - {}", album.artist, album.title);
85            }
86        }
87    }
88    Ok(())
89}
90
91pub fn print_dig(output: &DigOutput, format: OutputFormat) -> Result<()> {
92    match format {
93        OutputFormat::Json => println!("{}", serde_json::to_string_pretty(output)?),
94        OutputFormat::Csv => print_results_csv(&output.results)?,
95        OutputFormat::Table => print_results_table(output),
96    }
97    Ok(())
98}
99
100fn print_results_csv(results: &[CandidateRecord]) -> Result<()> {
101    let mut writer = csv::Writer::from_writer(std::io::stdout());
102    writer.write_record([
103        "rank",
104        "artist",
105        "album",
106        "url",
107        "overlap_count",
108        "overlap_ratio",
109        "score",
110        "reason",
111    ])?;
112    for result in results {
113        writer.write_record([
114            result.rank.to_string(),
115            result.artist.clone(),
116            result.title.clone(),
117            result.url.clone(),
118            result.overlap_count.to_string(),
119            format!("{:.4}", result.overlap_ratio),
120            format!("{:.2}", result.score),
121            result.reason.clone(),
122        ])?;
123    }
124    writer.flush()?;
125    Ok(())
126}
127
128fn print_results_table(output: &DigOutput) {
129    println!("Seed: {} - {}", output.seed.artist, output.seed.title);
130    println!(
131        "Collectors: discovered={} sampled={} scanned={} skipped={}",
132        output.summary.collectors_discovered,
133        output.summary.collectors_sampled,
134        output.summary.collectors_scanned,
135        output.summary.collectors_skipped
136    );
137    println!();
138    println!(
139        "{:<4} {:<24} {:<28} {:>8} {:>8} {:>8}  Reason",
140        "#", "Artist", "Album", "Overlap", "Pct", "Score"
141    );
142    for item in &output.results {
143        println!(
144            "{:<4} {:<24} {:<28} {:>8} {:>7.1}% {:>8.2}  {}",
145            item.rank,
146            truncate(&item.artist, 24),
147            truncate(&item.title, 28),
148            item.overlap_count,
149            item.overlap_ratio * 100.0,
150            item.score,
151            item.reason
152        );
153    }
154}
155
156fn truncate(value: &str, width: usize) -> String {
157    let mut chars = value.chars();
158    let collected: String = chars.by_ref().take(width).collect();
159    if chars.next().is_some() && width > 1 {
160        format!("{}…", collected.chars().take(width - 1).collect::<String>())
161    } else {
162        collected
163    }
164}