|
@@ -1,4 +1,4 @@
|
|
|
-use anyhow::Context;
|
|
|
|
|
|
|
+use anyhow::{Context, Ok};
|
|
|
use base64::engine::general_purpose::STANDARD;
|
|
use base64::engine::general_purpose::STANDARD;
|
|
|
use base64::Engine;
|
|
use base64::Engine;
|
|
|
use flate2::write::DeflateEncoder;
|
|
use flate2::write::DeflateEncoder;
|
|
@@ -6,11 +6,11 @@ use flate2::Compression;
|
|
|
|
|
|
|
|
use serde::Serialize;
|
|
use serde::Serialize;
|
|
|
use serde_json::{json, Value};
|
|
use serde_json::{json, Value};
|
|
|
|
|
+use smart_default::SmartDefault;
|
|
|
use std::io::prelude::*;
|
|
use std::io::prelude::*;
|
|
|
|
|
|
|
|
/// Compress string according to compressString from: https://github.com/igvteam/igv-utils/blob/master/src/bgzf.js#L125
|
|
/// Compress string according to compressString from: https://github.com/igvteam/igv-utils/blob/master/src/bgzf.js#L125
|
|
|
fn compress_string(input: &str) -> anyhow::Result<String> {
|
|
fn compress_string(input: &str) -> anyhow::Result<String> {
|
|
|
- // Convert string to bytes
|
|
|
|
|
let bytes = input.as_bytes();
|
|
let bytes = input.as_bytes();
|
|
|
|
|
|
|
|
// Compress bytes
|
|
// Compress bytes
|
|
@@ -60,6 +60,7 @@ impl Default for ReferenceValues {
|
|
|
#[derive(Debug)]
|
|
#[derive(Debug)]
|
|
|
pub struct Session {
|
|
pub struct Session {
|
|
|
value: Value,
|
|
value: Value,
|
|
|
|
|
+ tracks: Vec<Track>,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl Default for Session {
|
|
impl Default for Session {
|
|
@@ -72,7 +73,10 @@ impl Default for Session {
|
|
|
"tracks": [],
|
|
"tracks": [],
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- Self { value }
|
|
|
|
|
|
|
+ Self {
|
|
|
|
|
+ value,
|
|
|
|
|
+ tracks: Vec::new(),
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -83,10 +87,95 @@ impl Session {
|
|
|
}
|
|
}
|
|
|
self
|
|
self
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /// Add a locus
|
|
|
|
|
+ pub fn with_locus(mut self, from: (String, i32), to: i32) -> anyhow::Result<Self> {
|
|
|
|
|
+ *self
|
|
|
|
|
+ .value
|
|
|
|
|
+ .get_mut("locus")
|
|
|
|
|
+ .context("Can't access locus value")? = json!(format!("{}:{}-{}", from.0, from.1, to));
|
|
|
|
|
+ Ok(self)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pub fn with_locus_at(mut self, at: (String, i32)) -> anyhow::Result<Self> {
|
|
|
|
|
+ *self
|
|
|
|
|
+ .value
|
|
|
|
|
+ .get_mut("locus")
|
|
|
|
|
+ .context("Can't access locus value")? =
|
|
|
|
|
+ json!(format!("{}:{}-{}", at.0, at.1 - 19, at.1 + 19));
|
|
|
|
|
+ Ok(self)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
pub fn link(&self, base_url: &str) -> anyhow::Result<String> {
|
|
pub fn link(&self, base_url: &str) -> anyhow::Result<String> {
|
|
|
let blob = compress_string(&self.value.to_string())?;
|
|
let blob = compress_string(&self.value.to_string())?;
|
|
|
Ok(format!("{base_url}?sessionURL=blob:{blob}"))
|
|
Ok(format!("{base_url}?sessionURL=blob:{blob}"))
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ pub fn add_track(mut self, track: Track) -> anyhow::Result<Self> {
|
|
|
|
|
+ let mut track = track;
|
|
|
|
|
+ let pos = self.tracks.len() + 1;
|
|
|
|
|
+ track.order(pos as i16);
|
|
|
|
|
+ self.tracks.push(track);
|
|
|
|
|
+
|
|
|
|
|
+ let tv: Vec<Value> = self.tracks.iter().map(|t| t.to_json()).collect();
|
|
|
|
|
+
|
|
|
|
|
+ *self
|
|
|
|
|
+ .value
|
|
|
|
|
+ .get_mut("tracks")
|
|
|
|
|
+ .context("Can't access locus value")? = Value::Array(tv);
|
|
|
|
|
+ Ok(self)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Debug, Serialize, SmartDefault)]
|
|
|
|
|
+pub struct BamTrack {
|
|
|
|
|
+ #[serde(rename = "type")]
|
|
|
|
|
+ #[default = "alignment"]
|
|
|
|
|
+ pub igv_type: String,
|
|
|
|
|
+ #[default = 0]
|
|
|
|
|
+ pub order: i16,
|
|
|
|
|
+ #[default = ""]
|
|
|
|
|
+ pub url: String,
|
|
|
|
|
+ #[default = ""]
|
|
|
|
|
+ pub filename: String,
|
|
|
|
|
+ #[serde(rename = "indexURL")]
|
|
|
|
|
+ #[default = ""]
|
|
|
|
|
+ pub index_url: String,
|
|
|
|
|
+ #[default = ""]
|
|
|
|
|
+ pub name: String,
|
|
|
|
|
+ #[default = "bam"]
|
|
|
|
|
+ pub format: String,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl BamTrack {
|
|
|
|
|
+ pub fn new(name: &str, url: &str) -> Self {
|
|
|
|
|
+ BamTrack {
|
|
|
|
|
+ url: url.to_string(),
|
|
|
|
|
+ filename: name.to_string(),
|
|
|
|
|
+ index_url: format!("{url}.bai"),
|
|
|
|
|
+ name: name.to_string(),
|
|
|
|
|
+ ..Default::default()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+#[derive(Debug)]
|
|
|
|
|
+pub enum Track {
|
|
|
|
|
+ Bam(BamTrack),
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+impl Track {
|
|
|
|
|
+ pub fn to_json(&self) -> Value {
|
|
|
|
|
+ match self {
|
|
|
|
|
+ Track::Bam(bam) => json!(bam),
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ pub fn order(&mut self, order: i16) {
|
|
|
|
|
+ match self {
|
|
|
|
|
+ Track::Bam(track) => track.order = order,
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
#[cfg(test)]
|
|
@@ -95,9 +184,13 @@ mod tests {
|
|
|
|
|
|
|
|
#[test]
|
|
#[test]
|
|
|
fn it_works() -> anyhow::Result<()> {
|
|
fn it_works() -> anyhow::Result<()> {
|
|
|
- let sess = Session::default().with_reference(ReferenceValues::default());
|
|
|
|
|
|
|
+ let sess = Session::default()
|
|
|
|
|
+ .with_reference(ReferenceValues::default())
|
|
|
|
|
+ .with_locus_at(("chr1".to_string(), 47093698))?
|
|
|
|
|
+ .add_track(Track::Bam(BamTrack::new("ROBIN diag", "http://store-desktop.local/data/longreads_basic_pipe/ROBIN/diag/ROBIN_diag_hs1.bam")))?
|
|
|
|
|
+ .add_track(Track::Bam(BamTrack::new("ROBIN mrd", "http://store-desktop.local/data/longreads_basic_pipe/ROBIN/mrd/ROBIN_mrd_hs1.bam")))?;
|
|
|
let compressed_str = sess.link("http://store-desktop.local/igv/")?;
|
|
let compressed_str = sess.link("http://store-desktop.local/igv/")?;
|
|
|
- println!("Compressed and URL-safe encoded string: {}", compressed_str);
|
|
|
|
|
|
|
+ println!("{compressed_str}");
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|