Skip to content

Commit b44de4b

Browse files
nleroy917jackh726
authored andcommitted
add option for writing to std out for bigwig average over bed
1 parent 676e0cc commit b44de4b

File tree

4 files changed

+159
-54
lines changed

4 files changed

+159
-54
lines changed

bigtools/src/utils/cli/bigbedtobed.rs

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ use crate::{BBIReadError, BigBedRead, ChromInfo};
2020
long_about = "Converts an input bigBed to a bed. Can be multi-threaded for substantial speedups. Note for roughly each core, one temporary file will be opened."
2121
)]
2222
pub struct BigBedToBedArgs {
23-
/// the bigbed to get convert to bed
23+
/// The bigbed to get convert to bed.
2424
pub big_bed: String,
2525

26-
/// the path of the bed to output to
26+
/// The path of the bed to output to.
27+
/// Specifying `-` will write to `stdout`.
2728
pub bed: String,
2829

2930
/// If set, restrict output to given chromosome
@@ -65,7 +66,6 @@ pub fn bigbedtobed(args: BigBedToBedArgs) -> Result<(), Box<dyn Error>> {
6566
let nthreads = args.nthreads;
6667

6768
let bigbed = BigBedRead::open_file(&bigbedpath)?;
68-
let bed = File::create(bedpath)?;
6969

7070
if (args.start.is_some() || args.end.is_some()) && args.chrom.is_none() {
7171
eprintln!("Cannot specify --start or --end without specifying --chrom.");
@@ -79,30 +79,62 @@ pub fn bigbedtobed(args: BigBedToBedArgs) -> Result<(), Box<dyn Error>> {
7979

8080
match args.overlap_bed {
8181
Some(overlap_bed) => {
82-
if !Path::exists(&Path::new(&overlap_bed)) {
82+
if !Path::new(&overlap_bed).exists() {
8383
eprintln!("Overlap bed file does not exist.");
8484
return Ok(());
8585
}
8686
let overlap_bed = File::open(overlap_bed)?;
87-
write_bed_from_bed(bigbed, bed, overlap_bed)?;
87+
match bedpath.as_str() {
88+
"-" => {
89+
let stdout = io::stdout().lock();
90+
write_bed_from_bed(bigbed, stdout, overlap_bed)?;
91+
}
92+
_ => {
93+
let file = File::create(bedpath)?;
94+
write_bed_from_bed(bigbed, file, overlap_bed)?;
95+
}
96+
}
8897
}
8998
None => {
9099
// Right now, we don't offload decompression to separate threads,
91100
// so specifying `chrom` effectively means single-threaded
92101
if nthreads == 1 || args.chrom.is_some() || args.zoom.is_some() {
93-
write_bed_singlethreaded(bigbed, bed, args.chrom, args.start, args.end, args.zoom)?;
102+
match bedpath.as_str() {
103+
"-" => {
104+
let stdout = io::stdout().lock();
105+
write_bed_singlethreaded(
106+
bigbed, stdout, args.chrom, args.start, args.end, args.zoom,
107+
)?;
108+
}
109+
_ => {
110+
let file = File::create(bedpath)?;
111+
write_bed_singlethreaded(
112+
bigbed, file, args.chrom, args.start, args.end, args.zoom,
113+
)?;
114+
}
115+
}
94116
} else {
95-
write_bed(bigbed, bed, args.inmemory, nthreads)?;
117+
match bedpath.as_str() {
118+
"-" => {
119+
// FIXME: would be nice to refactor so that each write doesn't need to lock individually
120+
let stdout = io::stdout();
121+
write_bed(bigbed, stdout, args.inmemory, nthreads)?;
122+
}
123+
_ => {
124+
let out_file = File::create(bedpath)?;
125+
write_bed(bigbed, out_file, args.inmemory, nthreads)?;
126+
}
127+
}
96128
}
97129
}
98130
}
99131

100132
Ok(())
101133
}
102134

103-
pub fn write_bed_singlethreaded<R: Reopen + SeekableRead>(
135+
pub fn write_bed_singlethreaded<R: Reopen + SeekableRead, O: Write>(
104136
mut bigbed: BigBedRead<R>,
105-
out_file: File,
137+
writer: O,
106138
chrom: Option<String>,
107139
start: Option<u32>,
108140
end: Option<u32>,
@@ -121,7 +153,7 @@ pub fn write_bed_singlethreaded<R: Reopen + SeekableRead>(
121153
} else {
122154
bigbed.chroms().to_vec()
123155
};
124-
let mut writer = io::BufWriter::with_capacity(32 * 1000, out_file);
156+
let mut writer = io::BufWriter::with_capacity(32 * 1000, writer);
125157
let mut buf: String = String::with_capacity(50); // Estimate
126158
if let Some(zoom) = zoom {
127159
if bigbed
@@ -182,9 +214,9 @@ pub fn write_bed_singlethreaded<R: Reopen + SeekableRead>(
182214
Ok(())
183215
}
184216

185-
pub fn write_bed<R: Reopen + SeekableRead + Send + 'static>(
217+
pub fn write_bed<R: Reopen + SeekableRead + Send + 'static, O: Write + Send + 'static>(
186218
bigbed: BigBedRead<R>,
187-
mut out_file: File,
219+
mut writer: O,
188220
inmemory: bool,
189221
nthreads: usize,
190222
) -> Result<(), BBIReadError> {
@@ -196,10 +228,10 @@ pub fn write_bed<R: Reopen + SeekableRead + Send + 'static>(
196228
let mut remaining_chroms = bigbed.chroms().to_vec();
197229
remaining_chroms.reverse();
198230

199-
async fn file_future<R: SeekableRead + 'static>(
231+
async fn file_future<R: SeekableRead + 'static, O: Write + Send + 'static>(
200232
mut bigbed: BigBedRead<R>,
201233
chrom: ChromInfo,
202-
mut writer: io::BufWriter<TempFileBufferWriter<File>>,
234+
mut writer: io::BufWriter<TempFileBufferWriter<O>>,
203235
) -> Result<(), BBIReadError> {
204236
let mut buf: String = String::with_capacity(50); // Estimate
205237
for raw_val in bigbed.get_interval(&chrom.name, 0, chrom.length)? {
@@ -232,7 +264,7 @@ pub fn write_bed<R: Reopen + SeekableRead + Send + 'static>(
232264
};
233265

234266
let bigbed = bigbed.reopen()?;
235-
let (buf, file): (TempFileBuffer<File>, TempFileBufferWriter<File>) =
267+
let (buf, file): (TempFileBuffer<O>, TempFileBufferWriter<O>) =
236268
TempFileBuffer::new(inmemory);
237269
let writer = io::BufWriter::new(file);
238270
let handle = tokio::task::spawn(file_future(bigbed, chrom, writer));
@@ -259,24 +291,24 @@ pub fn write_bed<R: Reopen + SeekableRead + Send + 'static>(
259291
return Ok::<_, BBIReadError>(());
260292
};
261293

262-
buf.switch(out_file);
294+
buf.switch(writer);
263295
while !buf.is_real_file_ready() {
264296
tokio::task::yield_now().await;
265297
}
266-
out_file = buf.await_real_file();
298+
writer = buf.await_real_file();
267299
}
268300
})?;
269301

270302
Ok(())
271303
}
272304

273-
pub fn write_bed_from_bed<R: Reopen + SeekableRead + Send + 'static>(
305+
pub fn write_bed_from_bed<R: Reopen + SeekableRead, O: Write>(
274306
mut bigbed: BigBedRead<R>,
275-
out_file: File,
307+
writer: O,
276308
bed: File,
277309
) -> Result<(), BBIReadError> {
278310
let mut bedstream = StreamingLineReader::new(BufReader::new(bed));
279-
let mut writer = io::BufWriter::new(out_file);
311+
let mut writer = io::BufWriter::new(writer);
280312

281313
let mut buf = String::with_capacity(50); // Estimate
282314
while let Some(line) = bedstream.read() {

bigtools/src/utils/cli/bigwigaverageoverbed.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ pub struct BigWigAverageOverBedArgs {
2424
/// The input bigwig file
2525
pub bigwig: String,
2626

27-
/// The input bed file
27+
/// The input bed file.
2828
pub bedin: String,
2929

30-
/// The output bed file
30+
/// The output bed file.
31+
/// Specifying `-` will write to `stdout`.
3132
pub output: String,
3233

3334
/// Supports three types of options: `interval`, `none`, or a column number (one indexed).
@@ -60,12 +61,13 @@ pub struct BigWigAverageOverBedArgs {
6061
pub nthreads: usize,
6162
}
6263

63-
pub fn bigwigaverageoverbed(
64+
fn bigwigaverageoverbed_impl<O: Write>(
6465
args: BigWigAverageOverBedArgs,
66+
bedoutwriter: O,
6567
) -> Result<(), Box<dyn Error + Send + Sync>> {
6668
let bigwigpath = args.bigwig;
6769
let bedinpath = args.bedin;
68-
let bedoutpath = args.output;
70+
6971
let add_min_max = args.min_max;
7072

7173
let reopen = ReopenableFile {
@@ -74,8 +76,7 @@ pub fn bigwigaverageoverbed(
7476
};
7577
let mut inbigwig = BigWigRead::open(reopen)?.cached();
7678

77-
let outbed = File::create(bedoutpath)?;
78-
let mut bedoutwriter: BufWriter<File> = BufWriter::new(outbed);
79+
let mut bedoutwriter = BufWriter::new(bedoutwriter);
7980

8081
let name = match args.namecol.as_deref() {
8182
Some("interval") => Name::Interval,
@@ -311,3 +312,19 @@ pub fn bigwigaverageoverbed(
311312

312313
Ok(())
313314
}
315+
316+
pub fn bigwigaverageoverbed(
317+
args: BigWigAverageOverBedArgs,
318+
) -> Result<(), Box<dyn Error + Send + Sync>> {
319+
let bedoutpath = args.output.clone();
320+
match bedoutpath.as_str() {
321+
"-" => {
322+
let stdout = io::stdout().lock();
323+
bigwigaverageoverbed_impl(args, stdout)
324+
}
325+
_ => {
326+
let outbed = File::create(bedoutpath)?;
327+
bigwigaverageoverbed_impl(args, outbed)
328+
}
329+
}
330+
}

bigtools/src/utils/cli/bigwigtobedgraph.rs

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ use ufmt::uwrite;
2121
long_about = "Converts an input bigWig to a bedGraph. Can be multi-threaded for substantial speedups."
2222
)]
2323
pub struct BigWigToBedGraphArgs {
24-
/// the bigwig to get convert to bedgraph
24+
/// The bigwig to get convert to bedgraph.
2525
pub bigwig: String,
2626

27-
/// the path of the bedgraph to output to
27+
/// The path of the bedgraph to output to.
28+
/// Specifying `-` will write to `stdout`.
2829
pub bedgraph: String,
2930

3031
/// If set, restrict output to given chromosome
@@ -61,7 +62,6 @@ pub fn bigwigtobedgraph(args: BigWigToBedGraphArgs) -> Result<(), Box<dyn Error>
6162
let nthreads = args.nthreads;
6263

6364
let bigwig = BigWigRead::open_file(&bigwigpath)?;
64-
let bedgraph = File::create(bedgraphpath)?;
6565

6666
if (args.start.is_some() || args.end.is_some()) && args.chrom.is_none() {
6767
eprintln!("Cannot specify --start or --end without specifying --chrom.");
@@ -75,30 +75,58 @@ pub fn bigwigtobedgraph(args: BigWigToBedGraphArgs) -> Result<(), Box<dyn Error>
7575

7676
match args.overlap_bed {
7777
Some(overlap_bed) => {
78-
if !Path::exists(&Path::new(&overlap_bed)) {
78+
if !Path::new(&overlap_bed).exists() {
7979
eprintln!("Overlap bed file does not exist.");
8080
return Ok(());
8181
}
8282
let overlap_bed = File::open(overlap_bed)?;
83-
write_bg_from_bed(bigwig, bedgraph, overlap_bed)?;
83+
match bedgraphpath.as_str() {
84+
"-" => {
85+
let stdout = io::stdout().lock();
86+
write_bg_from_bed(bigwig, stdout, overlap_bed)?;
87+
}
88+
_ => {
89+
let file = File::create(bedgraphpath)?;
90+
write_bg_from_bed(bigwig, file, overlap_bed)?;
91+
}
92+
}
8493
}
8594
None => {
8695
// Right now, we don't offload decompression to separate threads,
8796
// so specifying `chrom` effectively means single-threaded
8897
if nthreads == 1 || args.chrom.is_some() {
89-
write_bg_singlethreaded(bigwig, bedgraph, args.chrom, args.start, args.end)?;
98+
match bedgraphpath.as_str() {
99+
"-" => {
100+
let stdout = io::stdout().lock();
101+
write_bg_singlethreaded(bigwig, stdout, args.chrom, args.start, args.end)?;
102+
}
103+
_ => {
104+
let file = File::create(bedgraphpath)?;
105+
write_bg_singlethreaded(bigwig, file, args.chrom, args.start, args.end)?;
106+
}
107+
}
90108
} else {
91-
write_bg(bigwig, bedgraph, args.inmemory, nthreads)?;
109+
match bedgraphpath.as_str() {
110+
"-" => {
111+
// FIXME: would be nice to refactor so that each write doesn't need to lock individually
112+
let stdout = io::stdout();
113+
write_bg(bigwig, stdout, args.inmemory, nthreads)?;
114+
}
115+
_ => {
116+
let file = File::create(bedgraphpath)?;
117+
write_bg(bigwig, file, args.inmemory, nthreads)?;
118+
}
119+
}
92120
}
93121
}
94122
}
95123

96124
Ok(())
97125
}
98126

99-
pub fn write_bg_singlethreaded<R: SeekableRead + Send + 'static>(
127+
pub fn write_bg_singlethreaded<R: SeekableRead, O: Write>(
100128
mut bigwig: BigWigRead<R>,
101-
out_file: File,
129+
writer: O,
102130
chrom: Option<String>,
103131
start: Option<u32>,
104132
end: Option<u32>,
@@ -116,7 +144,7 @@ pub fn write_bg_singlethreaded<R: SeekableRead + Send + 'static>(
116144
} else {
117145
bigwig.chroms().to_vec()
118146
};
119-
let mut writer = io::BufWriter::with_capacity(32 * 1000, out_file);
147+
let mut writer = io::BufWriter::with_capacity(32 * 1000, writer);
120148
for chrom in chroms {
121149
let start = start.unwrap_or(0);
122150
let end = end.unwrap_or(chrom.length);
@@ -143,9 +171,9 @@ pub fn write_bg_singlethreaded<R: SeekableRead + Send + 'static>(
143171
Ok(())
144172
}
145173

146-
pub fn write_bg<R: Reopen + SeekableRead + Send + 'static>(
174+
pub fn write_bg<R: Reopen + SeekableRead + Send + 'static, O: Write + Send + 'static>(
147175
bigwig: BigWigRead<R>,
148-
mut out_file: File,
176+
mut writer: O,
149177
inmemory: bool,
150178
nthreads: usize,
151179
) -> Result<(), BBIReadError> {
@@ -157,10 +185,10 @@ pub fn write_bg<R: Reopen + SeekableRead + Send + 'static>(
157185
let mut remaining_chroms = bigwig.chroms().to_vec();
158186
remaining_chroms.reverse();
159187

160-
async fn file_future<R: SeekableRead + 'static>(
188+
async fn file_future<R: SeekableRead + 'static, O: Write + Send + 'static>(
161189
mut bigwig: BigWigRead<R>,
162190
chrom: ChromInfo,
163-
mut writer: io::BufWriter<TempFileBufferWriter<File>>,
191+
mut writer: io::BufWriter<TempFileBufferWriter<O>>,
164192
) -> Result<(), BBIReadError> {
165193
let mut buf: String = String::with_capacity(50); // Estimate
166194
for raw_val in bigwig.get_interval(&chrom.name, 0, chrom.length)? {
@@ -190,7 +218,7 @@ pub fn write_bg<R: Reopen + SeekableRead + Send + 'static>(
190218
};
191219

192220
let bigbed = bigwig.reopen()?;
193-
let (buf, file): (TempFileBuffer<File>, TempFileBufferWriter<File>) =
221+
let (buf, file): (TempFileBuffer<O>, TempFileBufferWriter<O>) =
194222
TempFileBuffer::new(inmemory);
195223
let writer = io::BufWriter::new(file);
196224
let handle = tokio::task::spawn(file_future(bigbed, chrom, writer));
@@ -217,24 +245,24 @@ pub fn write_bg<R: Reopen + SeekableRead + Send + 'static>(
217245
return Ok::<_, BBIReadError>(());
218246
};
219247

220-
buf.switch(out_file);
248+
buf.switch(writer);
221249
while !buf.is_real_file_ready() {
222250
tokio::task::yield_now().await;
223251
}
224-
out_file = buf.await_real_file();
252+
writer = buf.await_real_file();
225253
}
226254
})?;
227255

228256
Ok(())
229257
}
230258

231-
pub fn write_bg_from_bed<R: Reopen + SeekableRead + Send + 'static>(
259+
pub fn write_bg_from_bed<R: Reopen + SeekableRead, O: Write>(
232260
mut bigbed: BigWigRead<R>,
233-
out_file: File,
261+
writer: O,
234262
bed: File,
235263
) -> Result<(), BBIReadError> {
236264
let mut bedstream = StreamingLineReader::new(BufReader::new(bed));
237-
let mut writer = io::BufWriter::new(out_file);
265+
let mut writer = io::BufWriter::new(writer);
238266

239267
while let Some(line) = bedstream.read() {
240268
let line = line?;

0 commit comments

Comments
 (0)