|
|
@@ -196,6 +196,64 @@ impl VcfVariant {
|
|
|
},
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ pub fn bnd_desc(&self) -> anyhow::Result<BNDDesc> {
|
|
|
+ let alt = self.alternative.to_string();
|
|
|
+ if self.alteration_category() == AlterationCategory::BND {
|
|
|
+ let b_sens = alt.contains('[');
|
|
|
+
|
|
|
+ let a_sens = if b_sens {
|
|
|
+ !alt.starts_with('[')
|
|
|
+ } else {
|
|
|
+ !alt.starts_with(']')
|
|
|
+ };
|
|
|
+
|
|
|
+ let parts: Vec<&str> = alt
|
|
|
+ .split(&['[', ']', ':'])
|
|
|
+ .filter(|v| !v.is_empty())
|
|
|
+ .collect();
|
|
|
+
|
|
|
+ if parts.len() != 3 {
|
|
|
+ return Err(anyhow::anyhow!("Failed to parse parts: {parts:?}"));
|
|
|
+ }
|
|
|
+
|
|
|
+ let (nt, b_contig, b_position) = if a_sens {
|
|
|
+ (parts[0], parts[1], parts[2])
|
|
|
+ } else {
|
|
|
+ (parts[2], parts[0], parts[1])
|
|
|
+ };
|
|
|
+
|
|
|
+ let added_nt = if nt.len() > 1 {
|
|
|
+ nt[1..].to_string()
|
|
|
+ } else {
|
|
|
+ nt.to_string()
|
|
|
+ };
|
|
|
+
|
|
|
+ Ok(BNDDesc {
|
|
|
+ a_contig: self.position.contig(),
|
|
|
+ a_position: self.position.position + 1,
|
|
|
+ a_sens,
|
|
|
+ b_contig: b_contig.to_string(),
|
|
|
+ b_position: b_position
|
|
|
+ .parse()
|
|
|
+ .map_err(|e| anyhow::anyhow!("Failed to parse: {b_position}\n{e}"))?,
|
|
|
+ b_sens,
|
|
|
+ added_nt,
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ Err(anyhow::anyhow!("The alteration is not BND: {alt}"))
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
|
|
|
+pub struct BNDDesc {
|
|
|
+ pub a_contig: String,
|
|
|
+ pub a_position: u32, // 1-based
|
|
|
+ pub a_sens: bool,
|
|
|
+ pub b_contig: String,
|
|
|
+ pub b_position: u32, // 1-based
|
|
|
+ pub b_sens: bool,
|
|
|
+ pub added_nt: String,
|
|
|
}
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
|