///|
pub fn format_header(header : VcfHeader) -> String {
let lines : Array[String] = []
for item in header.meta {
lines.push(format_meta_line(item))
}
lines.push(header.columns.join("\t"))
lines.join("\n") + "\n"
}
///|
pub fn format_record(record : VcfRecord) -> String {
let fields : Array[String] = [
record.chrom,
record.pos.to_string(),
format_string_list(record.ids, ';'),
record.ref_allele,
format_string_list(record.alt_alleles, ','),
format_optional_double(record.qual),
format_string_list(record.filters, ';'),
format_info(record.info),
]
if record.format_keys.length() > 0 {
fields.push(record.format_keys.join(":"))
for sample in record.samples {
fields.push(format_sample(sample, record.format_keys))
}
}
fields.join("\t")
}
///|
pub fn format_document(document : VcfDocument) -> String {
let builder = StringBuilder()
builder.write_string(format_header(document.header))
for record in document.records {
builder.write_string(format_record(record))
builder.write_char('\n')
}
builder.to_string()
}
///|
fn format_meta_line(item : MetaLine) -> String {
match item {
MetaLine::FileFormat(value) => "##fileformat=\{value}"
Info(def) =>
"##INFO="
Format(def) =>
"##FORMAT="
Filter(def) =>
"##FILTER="
Contig(def) => {
let base = "##contig= base + ",length=\{length}"
None => base
}
with_length + format_extra_attrs(def.attributes, ["ID", "length"]) + ">"
}
Generic(key~, value~) => "##\{key}=\{value}"
}
}
///|
fn format_cardinality(cardinality : Cardinality) -> String {
match cardinality {
Cardinality::Fixed(n) => n.to_string()
PerAlt => "A"
PerAllele => "R"
PerGenotype => "G"
Variable => "."
Unknown(value) => value
}
}
///|
fn format_value_type(value_type : ValueType) -> String {
match value_type {
ValueType::Integer => "Integer"
Float => "Float"
Flag => "Flag"
Character => "Character"
String => "String"
Unknown(value) => value
}
}
///|
fn format_extra_attrs(
attrs : Array[(String, String)],
reserved : Array[String],
) -> String {
let extras : Array[String] = []
for pair in attrs {
let (key, value) = pair
if !contains_string(reserved, key) {
extras.push(",\{key}=\{value}")
}
}
extras.join("")
}
///|
fn contains_string(values : Array[String], needle : String) -> Bool {
for value in values {
if value == needle {
return true
}
}
false
}
///|
fn format_string_list(values : Array[String], sep : Char) -> String {
if values.length() == 0 {
"."
} else {
values.join(sep.to_string())
}
}
///|
fn format_optional_double(value : Double?) -> String {
match value {
Some(n) => n.to_string()
None => "."
}
}
///|
fn format_info(info : Array[(String, VcfValue)]) -> String {
if info.length() == 0 {
"."
} else {
let parts : Array[String] = []
for pair in info {
let (key, value) = pair
match value {
VcfValue::Flag => parts.push(key)
_ => parts.push("\{key}=\{format_value(value)}")
}
}
parts.join(";")
}
}
///|
fn format_sample(sample : SampleCall, format_keys : Array[String]) -> String {
let values : Array[String] = []
for key in format_keys {
values.push(
match find_sample_value(sample, key) {
Some(value) => format_value(value)
None => "."
},
)
}
values.join(":")
}
///|
fn find_sample_value(sample : SampleCall, key : String) -> VcfValue? {
for pair in sample.values {
let (item_key, value) = pair
if item_key == key {
return Some(value)
}
}
None
}
///|
fn format_value(value : VcfValue) -> String {
match value {
VcfValue::Missing => "."
Flag => ""
Integer(n) => n.to_string()
Float(n) => n.to_string()
String(value) => value
List(values) => values.map(format_value).join(",")
}
}