bigtools/utils/cli/
bigbedinfo.rs

1use 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    /// The bigbed to get info for.
16    pub bigbed: String,
17
18    /// If set, will print out the list of chromosomes in the bigBed and their sizes.
19    #[arg(long)]
20    #[arg(default_value_t = false)]
21    pub chroms: bool,
22
23    /// If set, will print out the list of all zoom levels.
24    #[arg(long)]
25    #[arg(default_value_t = false)]
26    pub zooms: bool,
27
28    /// If set, will print out the autosql spec.
29    #[arg(long)]
30    #[arg(default_value_t = false)]
31    pub autosql: bool,
32
33    /// Show all info for debugging.
34    #[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}