diff --git a/Cargo.lock b/Cargo.lock index 8ba98a1..c09d9af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" +dependencies = [ + "backtrace", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -51,12 +60,12 @@ dependencies = [ name = "bamtofastq" version = "1.3.5" dependencies = [ + "anyhow", + "backtrace", "bincode", "csv", "docopt", - "failure", "flate2", - "human-panic", "itertools", "regex", "rust-htslib", @@ -78,9 +87,9 @@ dependencies = [ [[package]] name = "bio-types" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b0decf3291fa83a9f26229d49e8bdf637291ab14451d315d6ac743aa7bf044f" +checksum = "3f79d996fbffc59cbaeec4c831f9c1bbf6debdfadd9bb02ff4caf70507159c63" dependencies = [ "derive-new", "lazy_static", @@ -115,9 +124,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bzip2-sys" -version = "0.1.9+1.0.8" +version = "0.1.11+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad3b39a260062fca31f7b0b12f207e8f2590a67d32ec7d59c20484b07ea7285e" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" dependencies = [ "cc", "libc", @@ -249,28 +258,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - [[package]] name = "flate2" version = "1.0.22" @@ -351,21 +338,6 @@ dependencies = [ "openssl-sys", ] -[[package]] -name = "human-panic" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39f357a500abcbd7c5f967c1d45c8838585b36743823b9d43488f24850534e36" -dependencies = [ - "backtrace", - "os_type", - "serde", - "serde_derive", - "termcolor", - "toml", - "uuid", -] - [[package]] name = "idna" version = "0.2.3" @@ -385,9 +357,9 @@ checksum = "9007da9cacbd3e6343da136e98b0d2df013f553d35bdec8b518f07bea768e19c" [[package]] name = "itertools" -version = "0.8.2" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" dependencies = [ "either", ] @@ -547,15 +519,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "os_type" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96eaebe22d9f12429b1af6a0b5dd411ccfc5cb5968710abbb8c512046be9df90" -dependencies = [ - "regex", -] - [[package]] name = "percent-encoding" version = "2.1.0" @@ -570,9 +533,9 @@ checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb" [[package]] name = "ppv-lite86" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "proc-macro2" @@ -758,14 +721,14 @@ dependencies = [ [[package]] name = "shardio" -version = "0.8.0" -source = "git+https://github.com/10XGenomics/rust-shardio.git#b700c2d66ea59cd5cd7563137d7b63f205a61993" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8496139ffe2b2dafa0ecaec339bb6d6a760c70781d1d5977e33de638ce80ca2" dependencies = [ + "anyhow", "bincode", "byteorder", "crossbeam-channel", - "failure", - "failure_derive", "log", "lz4", "min-max-heap", @@ -801,18 +764,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "synstructure" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "474aaa926faa1603c40b7885a9eaea29b444d1cb2850cb7c0e37bb1a4182f4fa" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - [[package]] name = "tempfile" version = "3.2.0" @@ -827,15 +778,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" version = "1.0.29" @@ -871,15 +813,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - [[package]] name = "unicode-bidi" version = "0.3.7" @@ -919,15 +852,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom", -] - [[package]] name = "vcpkg" version = "0.2.15" @@ -956,15 +880,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index b9faf22..c468a35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,18 +9,18 @@ resolver = "2" docopt = "*" rust-htslib = "0.38" flate2 = { version = "1.0", features = ["zlib"], default-features = false } -shardio = { git = "https://github.com/10XGenomics/rust-shardio.git" } +shardio = "0.8.1" bincode = "1" itertools = ">=0.8.0" regex = "*" tempfile = "*" +backtrace = "0.3" csv = "1.0.0" serde = "^1.0.7" serde_derive = "^1" serde_bytes = "*" -failure = "*" -human-panic = "1" +anyhow = { version = "1.0", features = ["backtrace"] } [profile.release] debug = 1 diff --git a/src/bx_index.rs b/src/bx_index.rs index e886be5..9447b48 100644 --- a/src/bx_index.rs +++ b/src/bx_index.rs @@ -8,8 +8,7 @@ use rust_htslib::bam::record::{Aux, Record}; use rust_htslib::bam::Read; use serde::Deserialize; -use failure::Error; -use failure::ResultExt; +use anyhow::{Context, Error}; #[derive(Deserialize, Ord, PartialOrd, Eq, PartialEq)] struct BcObs { diff --git a/src/locus.rs b/src/locus.rs index 4c41fdd..955eafe 100644 --- a/src/locus.rs +++ b/src/locus.rs @@ -1,7 +1,6 @@ // Copyright (c) 2020 10x Genomics, Inc. All rights reserved. -use failure::format_err; -use failure::Error; +use anyhow::{anyhow, Error}; use regex::Regex; use serde::Deserialize; use std::fmt; @@ -33,7 +32,7 @@ impl FromStr for Locus { let cap = re.captures(s); if cap.is_none() { - return Err(format_err!("invalid locus string: {}", s)); + return Err(anyhow!("invalid locus string: {}", s)); } let cap = cap.unwrap(); diff --git a/src/main.rs b/src/main.rs index 97e49c3..78709ba 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,13 +5,13 @@ use std::collections::HashMap; use std::fs::create_dir; use std::fs::File; use std::io::{BufWriter, Write}; +use std::panic; use std::path::{Path, PathBuf}; use std::str::FromStr; +use anyhow::{anyhow, Context, Error}; use docopt::Docopt; -use failure::{format_err, Error, ResultExt}; use flate2::write::GzEncoder; -use human_panic::setup_panic; use itertools::Itertools; use regex::Regex; use serde::{Deserialize, Serialize}; @@ -493,7 +493,7 @@ impl FormatBamRecords { }, }, _ => { - let e = format_err!( + let e = anyhow!( "Not a valid read pair: {}, {}", rec.is_first_in_template(), rec.is_last_in_template() @@ -513,7 +513,7 @@ impl FormatBamRecords { if last_tag { return Ok(()); } - let e = format_err!( + let e = anyhow!( "BAM record missing tag: {:?} on read {:?}. You do not appear to have an original 10x BAM file.\nIf you downloaded this BAM file from SRA, you likely need to download the 'Original Format' version of the BAM available for most 10x datasets.", tag, std::str::from_utf8(rec.qname()).unwrap() @@ -521,7 +521,7 @@ impl FormatBamRecords { return Err(e); } Ok(tag_val) => { - let e = format_err!("Invalid BAM record: read: {:?} unexpected tag type. Expected string for {:?}, got {:?}.\n You do not appear to have the original 10x BAM file. If you downloaded this BAM file from SRA, you likely need to download the 'Original Format' version of the BAM available for most 10x datasets.", std::str::from_utf8(rec.qname()).unwrap(), tag, tag_val); + let e = anyhow!("Invalid BAM record: read: {:?} unexpected tag type. Expected string for {:?}, got {:?}.\n You do not appear to have the original 10x BAM file. If you downloaded this BAM file from SRA, you likely need to download the 'Original Format' version of the BAM available for most 10x datasets.", std::str::from_utf8(rec.qname()).unwrap(), tag, tag_val); return Err(e); } } @@ -913,7 +913,7 @@ impl FastqWriter { } /// Write a set of fastq records - pub fn write( + fn write( &mut self, r1: &FqRecord, r2: &FqRecord, @@ -967,8 +967,8 @@ impl FastqWriter { } fn main() { - // initialize human_panic - setup_panic!(); + set_panic_handler(); + std::env::set_var("RUST_BACKTRACE", "1"); println!("bamtofastq v{}", VERSION); let args: Args = Docopt::new(USAGE) @@ -979,18 +979,45 @@ fn main() { let res = go(args, None); if let Err(ref e) = res { - println!("bamtofastqerror: {}\n", e); - println!("Please contact patrick@10xgenomics.com for assistance. Please re-run with --traceback and include stack trace with an error report"); + println!("bamtofastq error: {}\n", e); + println!("If this error is unexpected, contact support@10xgenomics.com for assistance. Please re-run with --traceback and include stack trace with an error report"); if traceback { println!("see below for more details:"); println!("=========================="); - println!("{}\n{}", e.as_fail(), e.backtrace()); - } + println!("{}\n{}", e, e.backtrace()); + }; ::std::process::exit(1); } } +fn set_panic_handler() { + panic::set_hook(Box::new(move |info| { + let backtrace = backtrace::Backtrace::new(); + + let msg = match info.payload().downcast_ref::<&'static str>() { + Some(s) => *s, + None => match info.payload().downcast_ref::() { + Some(s) => &**s, + None => "Box", + }, + }; + + let msg = match info.location() { + Some(location) => format!( + "bamtofastq failed unexpectedly. Please contact support@10xgenomics.com with the following information: '{}' {}:{}:\n{:?}", + msg, + location.file(), + location.line(), + backtrace + ), + None => format!("bamtofastq failed unexpectedly. Please contact support@10xgenomics.com with the following information: '{}':\n{:?}", msg, backtrace), + }; + + println!("{}", msg); + })); +} + pub fn go( args: Args, cache_size: Option, @@ -999,7 +1026,7 @@ pub fn go( let path = std::path::PathBuf::from(args.arg_bam.clone()); if !path.exists() { - return Err(format_err!("BAM file doesn't exist: {:?}", path)); + return Err(anyhow!("BAM file doesn't exist: {:?}", path)); } match args.flag_locus { @@ -1012,7 +1039,7 @@ pub fn go( let tid = bam .header() .tid(loc.chrom.as_bytes()) - .ok_or_else(|| format_err!("Requested chromosome not present: {}", loc.chrom))?; + .ok_or_else(|| anyhow!("Requested chromosome not present: {}", loc.chrom))?; bam.fetch((tid, loc.start, loc.end))?; inner(args.clone(), cache_size, bam) @@ -1054,15 +1081,15 @@ pub fn inner( } if args.flag_gemcode { - return Err(format_err!("Do not use a pipeline-specific command-line flag: --gemcode. Supplied BAM file already contains bamtofastq headers.")); + return Err(anyhow!("Do not use a pipeline-specific command-line flag: --gemcode. Supplied BAM file already contains bamtofastq headers.")); } if args.flag_lr20 { - return Err(format_err!("Do not use a pipeline-specific command-line flag: --lr20. Supplied BAM file already contains bamtofastq headers.")); + return Err(anyhow!("Do not use a pipeline-specific command-line flag: --lr20. Supplied BAM file already contains bamtofastq headers.")); } if args.flag_cr11 { - return Err(format_err!("Do not use a pipeline-specific command-line flag: --cr11. Supplied BAM file already contains bamtofastq headers.")); + return Err(anyhow!("Do not use a pipeline-specific command-line flag: --cr11. Supplied BAM file already contains bamtofastq headers.")); } f @@ -1087,13 +1114,12 @@ pub fn inner( // make output dir let out_path = Path::new(&args.arg_output_path); - create_dir(&args.arg_output_path).context(format_err!( + create_dir(&args.arg_output_path).context(anyhow!( "error creating output directory: {:?}. Does it already exist?", &out_path ))?; // prep output files - println!("{:?}", args); let fq = FastqManager::new( out_path, formatter.clone(), @@ -1125,7 +1151,6 @@ pub fn inner( let bx_iter = BxListIter::from_path(args.flag_bx_list.unwrap(), bxi, bam)?; proc_double_ended(bx_iter, formatter, fq, cache_size, false, args.flag_relaxed) } else { - println!("entry"); proc_single_ended(bam.records(), formatter, fq) } } @@ -1140,8 +1165,7 @@ fn proc_double_ended( ) -> Result, Option)>, Error> where I: Iterator>, - E: Send + Sync, - Result: ResultExt, + Result: Context, { // Temp file for hold unpaired reads. Will be cleaned up automatically. let tmp_file = NamedTempFile::new_in(&fq.out_path)?; @@ -1215,7 +1239,7 @@ where if item_vec.len() != 2 && !restricted_locus { let header = std::str::from_utf8(&item_vec[0].rec.head).unwrap(); if !relaxed { - let msg = format_err!("Didn't find both records for a paired end read. Is your BAM file complete?\nRead name of unpaired record: {}", header); + let msg = anyhow!("Didn't find both records for a paired end read. Is your BAM file complete?\nRead name of unpaired record: {}", header); return Err(msg); } else { println!("Didn't find both records for a paired end read. Skipping. Read name of unpaired record: {}", header); @@ -1244,14 +1268,13 @@ where Ok(fq.paths()) } -fn proc_single_ended( +fn proc_single_ended( records: I, formatter: FormatBamRecords, mut fq: FastqManager, ) -> Result, Option)>, Error> where - I: Iterator>, - Result: ResultExt, + I: Iterator>, { let total_reads = { // Count total R1s observed, so we can make sure we've preserved all read pairs