Skip to content

Commit 4a827b1

Browse files
committed
feat: Improve verbose output, add colors
1 parent c1dd8ca commit 4a827b1

5 files changed

Lines changed: 104 additions & 18 deletions

File tree

Cargo.lock

Lines changed: 37 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ edition = "2024"
77
anyhow = "1.0.100"
88
bgzip = { version = "0.3.1", features = ["zlib-ng"] }
99
clap = { version = "4.5.50", features = ["derive"] }
10+
console = "0.16.1"
1011
needletail = "0.6.3"
1112
rand = "0.9.2"
1213
uuid = { version = "1.18.1", features = ["v4"] }

src/cli.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
use clap::Parser;
2+
use clap::builder::styling::{AnsiColor, Effects, Styles};
3+
use console::style;
4+
use std::fmt::Display;
25
use std::path::PathBuf;
36

7+
fn get_styles() -> Styles {
8+
Styles::styled()
9+
.header(AnsiColor::Cyan.on_default() | Effects::BOLD)
10+
.usage(AnsiColor::Cyan.on_default() | Effects::BOLD)
11+
.literal(AnsiColor::Green.on_default() | Effects::BOLD)
12+
.placeholder(AnsiColor::Yellow.on_default())
13+
}
14+
415
#[derive(Parser)]
516
#[command(
617
name = "readfaker",
718
version,
819
about = "Simulate Oxford Nanopore reads with realistic quality profiles",
9-
long_about = None
20+
long_about = None,
21+
styles = get_styles()
1022
)]
1123
pub struct Cli {
1224
/// Reference sequences (FASTA format) to sample reads from
@@ -22,7 +34,7 @@ pub struct Cli {
2234
pub output: PathBuf,
2335

2436
/// Number of reads to generate
25-
#[arg(short = 'n', long, default_value = "10000")]
37+
#[arg(short = 'n', long, default_value = "100000")]
2638
pub num_reads: usize,
2739

2840
/// Random seed for reproducibility
@@ -33,3 +45,29 @@ pub struct Cli {
3345
#[arg(short, long)]
3446
pub verbose: bool,
3547
}
48+
49+
/// Formatting utilities for console output
50+
pub mod fmt {
51+
use super::*;
52+
53+
pub fn header(text: impl Display) -> String {
54+
style(text).bold().cyan().to_string()
55+
}
56+
57+
pub fn param(text: impl Display) -> String {
58+
style(text).bold().to_string()
59+
}
60+
61+
/// Format a parameter with proper alignment (width must account for raw text length)
62+
pub fn param_aligned(text: &str, width: usize) -> String {
63+
format!("{:<width$}", style(text).bold(), width = width + style(text).to_string().len() - text.len())
64+
}
65+
66+
pub fn progress(text: impl Display) -> String {
67+
format!("{} {}", style("→").cyan(), style(text).dim())
68+
}
69+
70+
pub fn success(text: impl Display) -> String {
71+
format!("{} {}", style("✓").green().bold(), style(text).green())
72+
}
73+
}

src/main.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use anyhow::Result;
22
use clap::Parser;
3-
use readfaker::cli::Cli;
3+
use readfaker::cli::{fmt, Cli};
44
use readfaker::generator::ReadGenerator;
55
use readfaker::io::{FastaReader, FastqWriter};
66
use readfaker::models::ErrorModel;
@@ -10,18 +10,22 @@ fn main() -> Result<()> {
1010
let cli = Cli::parse();
1111

1212
if cli.verbose {
13-
eprintln!("=== ReadFaker Configuration ===");
14-
eprintln!("Reference: {}", cli.reference.display());
15-
eprintln!("Input: {}", cli.input.display());
16-
eprintln!("Output: {}", cli.output.display());
17-
eprintln!("Number of reads: {}", cli.num_reads);
13+
eprintln!("{}", fmt::header("ReadFaker Configuration"));
14+
eprintln!("{}: {}", fmt::param_aligned("Reference", 16), cli.reference.display());
15+
eprintln!("{}: {}", fmt::param_aligned("Input", 16), cli.input.display());
16+
eprintln!("{}: {}", fmt::param_aligned("Output", 16), cli.output.display());
17+
eprintln!("{}: {}", fmt::param_aligned("Number of reads", 16), cli.num_reads);
1818
if let Some(seed) = cli.seed {
19-
eprintln!("Random seed: {}", seed);
19+
eprintln!("{}: {}", fmt::param_aligned("Random seed", 16), seed);
2020
}
21-
eprintln!("================================\n");
21+
eprintln!();
2222
}
2323

24+
if cli.verbose {
25+
eprintln!("{}", fmt::progress("Creating models from input FASTQ..."));
26+
}
2427
let (length_model, quality_model) = load_models(&cli.input, cli.seed)?;
28+
2529
let error_model = ErrorModel::new(None, None, None)?;
2630
let mut generator = ReadGenerator::new(
2731
FastaReader::read(&cli.reference)?,
@@ -32,10 +36,18 @@ fn main() -> Result<()> {
3236
)?;
3337
let mut writer = FastqWriter::new(&cli.output)?;
3438

39+
if cli.verbose {
40+
eprintln!("{}", fmt::progress(format!("Generating {} reads...", cli.num_reads)));
41+
}
42+
3543
for _ in 0..cli.num_reads {
3644
let read = generator.generate_read()?;
3745
writer.write_record(&read)?;
3846
}
3947

48+
if cli.verbose {
49+
eprintln!("{}", fmt::success(format!("Output written to {}", cli.output.display())));
50+
}
51+
4052
Ok(())
4153
}

src/models/quality.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,15 +196,15 @@ mod tests {
196196
let mut rng = StdRng::seed_from_u64(42);
197197

198198
// Add quality strings at different lengths
199-
model.add_value(50, vec![b'A'; 50], &mut rng); // Bucket 0 (0-99)
200-
model.add_value(150, vec![b'B'; 150], &mut rng); // Bucket 1 (100-199)
201-
model.add_value(350, vec![b'C'; 350], &mut rng); // Bucket 3 (300-399)
199+
model.add_value(50, vec![b'A'; 50], &mut rng); // Bucket 0 (0-99)
200+
model.add_value(150, vec![b'B'; 150], &mut rng); // Bucket 1 (100-199)
201+
model.add_value(350, vec![b'C'; 350], &mut rng); // Bucket 3 (300-399)
202202
model.add_value(25000, vec![b'D'; 25000], &mut rng); // Catch-all bucket
203203

204204
// Verify buckets contain the expected reads
205-
assert_eq!(model.batches[0].qualities.len(), 1); // Bucket 0 has 1 read
206-
assert_eq!(model.batches[1].qualities.len(), 1); // Bucket 1 has 1 read
207-
assert_eq!(model.batches[3].qualities.len(), 1); // Bucket 3 has 1 read
205+
assert_eq!(model.batches[0].qualities.len(), 1); // Bucket 0 has 1 read
206+
assert_eq!(model.batches[1].qualities.len(), 1); // Bucket 1 has 1 read
207+
assert_eq!(model.batches[3].qualities.len(), 1); // Bucket 3 has 1 read
208208
assert_eq!(model.batches.last().unwrap().qualities.len(), 1); // Catch-all has 1 read
209209
}
210210

0 commit comments

Comments
 (0)