Skip to content

Commit 489c0d7

Browse files
feat: Add to_vcf_string method for bcf::Record (#443)
* Add bcf record to_vcf_string method and test * Reformat as Display trait * Revert to Record::to_vcf_string method Added return value checking on vcf_format, which also suggests that the methods should return a Result and shound't be implemented directly as a Display trait. --------- Co-authored-by: Johannes Köster <[email protected]>
1 parent 9bda5f7 commit 489c0d7

File tree

2 files changed

+60
-0
lines changed

2 files changed

+60
-0
lines changed

src/bcf/record.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,38 @@ impl Record {
10881088
}
10891089
"".to_owned()
10901090
}
1091+
1092+
/// Convert to VCF String
1093+
///
1094+
/// Intended for debug only. Use Writer for efficient VCF output.
1095+
///
1096+
pub fn to_vcf_string(&self) -> Result<String> {
1097+
let mut buf = htslib::kstring_t {
1098+
l: 0,
1099+
m: 0,
1100+
s: ptr::null_mut(),
1101+
};
1102+
let ret = unsafe { htslib::vcf_format(self.header().inner, self.inner, &mut buf) };
1103+
1104+
if ret < 0 {
1105+
if !buf.s.is_null() {
1106+
unsafe {
1107+
libc::free(buf.s as *mut libc::c_void);
1108+
}
1109+
}
1110+
return Err(Error::BcfToString);
1111+
}
1112+
1113+
let vcf_str = unsafe {
1114+
let vcf_str = String::from(ffi::CStr::from_ptr(buf.s).to_str().unwrap());
1115+
if !buf.s.is_null() {
1116+
libc::free(buf.s as *mut libc::c_void);
1117+
}
1118+
vcf_str
1119+
};
1120+
1121+
Ok(vcf_str)
1122+
}
10911123
}
10921124

10931125
impl Clone for Record {
@@ -1714,4 +1746,30 @@ mod tests {
17141746
assert!(!record.has_filter(&bar));
17151747
assert!(record.has_filter("PASS".as_bytes()));
17161748
}
1749+
1750+
#[test]
1751+
fn test_record_to_vcf_string_err() {
1752+
let tmp = NamedTempFile::new().unwrap();
1753+
let path = tmp.path();
1754+
let header = Header::new();
1755+
let vcf = Writer::from_path(path, &header, true, Format::Vcf).unwrap();
1756+
let record = vcf.empty_record();
1757+
assert!(record.to_vcf_string().is_err());
1758+
}
1759+
1760+
#[test]
1761+
fn test_record_to_vcf_string() {
1762+
let tmp = NamedTempFile::new().unwrap();
1763+
let path = tmp.path();
1764+
let mut header = Header::new();
1765+
header.push_record(b"##contig=<ID=chr1,length=1000>");
1766+
header.push_record(br#"##FILTER=<ID=foo,Description="sample is a foo fighter">"#);
1767+
let vcf = Writer::from_path(path, &header, true, Format::Vcf).unwrap();
1768+
let mut record = vcf.empty_record();
1769+
record.push_filter("foo".as_bytes()).unwrap();
1770+
assert_eq!(
1771+
record.to_vcf_string().unwrap(),
1772+
"chr1\t1\t.\t.\t.\t0\tfoo\t.\n"
1773+
);
1774+
}
17171775
}

src/errors.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ pub enum Error {
131131
BcfSetValues,
132132
#[error("failed to remove alleles in BCF/VCF record")]
133133
BcfRemoveAlleles,
134+
#[error("failed to render BCF record as string")]
135+
BcfToString,
134136

135137
#[error("invalid compression level {level}")]
136138
BgzfInvalidCompressionLevel { level: i8 },

0 commit comments

Comments
 (0)