bigtools/utils/cli/
bigbedinfo.rs1use std::error::Error;
2
3use byteordered::Endianness;
4use clap::Parser;
5
6use crate::{BBIFileRead, BigBedRead};
7
8#[derive(Clone, Debug, Parser, PartialEq)]
9#[command(
10 name = "bigbedinfo",
11 about = "Gets information about a bigBed.",
12 long_about = None,
13)]
14pub struct BigBedInfoArgs {
15 pub bigbed: String,
17
18 #[arg(long)]
20 #[arg(default_value_t = false)]
21 pub chroms: bool,
22
23 #[arg(long)]
25 #[arg(default_value_t = false)]
26 pub zooms: bool,
27
28 #[arg(long)]
30 #[arg(default_value_t = false)]
31 pub autosql: bool,
32
33 #[arg(long, hide(true))]
35 #[arg(default_value_t = false)]
36 pub debug: bool,
37}
38
39pub fn bigbedinfo(args: BigBedInfoArgs) -> Result<(), Box<dyn Error>> {
40 let bigbedpath = &args.bigbed;
41
42 fn print_info<R: BBIFileRead>(
43 mut bigbed: BigBedRead<R>,
44 args: &BigBedInfoArgs,
45 ) -> Result<(), Box<dyn Error>> {
46 let header = bigbed.info().header;
47 println!("version: {}", header.version);
48 println!("fieldCount: {}", header.field_count);
49 println!(
50 "isCompressed: {}",
51 (header.uncompress_buf_size > 0)
52 .then(|| "yes")
53 .unwrap_or("no")
54 );
55 println!(
56 "isSwapped: {}",
57 (matches!(header.endianness, Endianness::Big))
58 .then(|| "1")
59 .unwrap_or("0")
60 );
61 println!("itemCount: {}", bigbed.item_count()?);
62 println!(
63 "primaryDataSize: {}",
64 num_with_commas(header.full_index_offset - header.full_data_offset)
65 );
66 let first_zoom_start = bigbed.info().zoom_headers.first().map(|z| z.data_offset);
67 if let Some(first_zoom_start) = first_zoom_start {
68 println!(
69 "primaryIndexSize: {}",
70 num_with_commas(first_zoom_start - header.full_index_offset)
71 );
72 }
73 println!("zoomLevels: {}", bigbed.info().zoom_headers.len());
74 if args.zooms {
75 for zoom in bigbed.info().zoom_headers.iter() {
76 println!(
77 "\t{}\t{}",
78 zoom.reduction_level,
79 zoom.index_offset - zoom.data_offset
80 );
81 }
82 }
83 println!("chromCount: {}", bigbed.info().chrom_info.len());
84 if args.chroms {
85 for chrom in bigbed.info().chrom_info.iter() {
86 println!("\t{} {} {}", chrom.name, chrom.id, chrom.length);
87 }
88 }
89 if args.autosql {
90 let autosql = bigbed.autosql()?;
91 match autosql {
92 None => {
93 println!("as: n/a");
94 }
95 Some(autosql) if autosql.is_empty() => {
96 println!("as: n/a");
97 }
98 Some(autosql) => {
99 println!("as:");
100 print!("{}", autosql);
101 }
102 }
103 }
104 let summary = bigbed.get_summary()?;
105 println!("basesCovered: {}", num_with_commas(summary.bases_covered));
106 println!(
107 "meanDepth: {:.6}",
108 summary.sum / summary.bases_covered as f64
109 );
110 println!("minDepth: {:.6}", summary.min_val);
111 println!("maxDepth: {:.6}", summary.max_val);
112 let var = (summary.sum_squares
113 - (summary.sum * summary.sum) / summary.bases_covered as f64)
114 / (summary.bases_covered as f64 - 1.0);
115 let std = var.sqrt();
116 println!("std of depth: {:.6}", std);
117 if args.debug {
118 println!("{:?}", header,);
119 }
120
121 Ok(())
122 }
123
124 #[cfg(feature = "remote")]
125 {
126 if bigbedpath.starts_with("http") {
127 use crate::utils::remote_file::RemoteFile;
128 let f = RemoteFile::new(bigbedpath);
129 let bigbed = BigBedRead::open(f)?;
130 print_info(bigbed, &args)?;
131 return Ok(());
132 }
133 }
134
135 let bigbed = BigBedRead::open_file(bigbedpath)?;
136 print_info(bigbed, &args)?;
137
138 Ok(())
139}
140
141fn num_with_commas(mut num: u64) -> String {
142 if num == 0 {
143 return format!("0");
144 }
145 let mut formatted = String::new();
146 loop {
147 let remainder = num % 1000;
148
149 if remainder != 0 {
150 if num > 1000 && remainder < 10 {
151 formatted = format!("00{remainder}") + &formatted;
152 } else if num > 1000 && remainder < 100 {
153 formatted = format!("0{remainder}") + &formatted;
154 } else {
155 formatted = format!("{remainder}") + &formatted;
156 }
157 num -= remainder;
158 } else {
159 formatted = format!("000") + &formatted;
160 }
161
162 num = num / 1000;
163
164 if num > 0 {
165 formatted = ",".to_string() + &formatted;
166 }
167
168 if num == 0 {
169 break formatted;
170 }
171 }
172}
173
174#[test]
175fn test_num_with_commas() {
176 assert_eq!("0", num_with_commas(0));
177 assert_eq!("987", num_with_commas(987));
178 assert_eq!("1,000", num_with_commas(1000));
179 assert_eq!("1,987", num_with_commas(1987));
180 assert_eq!("12,000", num_with_commas(12000));
181 assert_eq!("12,987", num_with_commas(12987));
182 assert_eq!("123,987", num_with_commas(123987));
183 assert_eq!("4,023,987", num_with_commas(4023987));
184 assert_eq!("4,123,987", num_with_commas(4123987));
185 assert_eq!("45,123,987", num_with_commas(45123987));
186 assert_eq!("456,123,987", num_with_commas(456123987));
187 assert_eq!("9,000,123,987", num_with_commas(9000123987));
188 assert_eq!("9,456,000,987", num_with_commas(9456000987));
189 assert_eq!("9,456,123,000", num_with_commas(9456123000));
190 assert_eq!("9,456,123,987", num_with_commas(9456123987));
191}