Thomas il y a 1 an
Parent
commit
dd0039b29f
3 fichiers modifiés avec 111 ajouts et 5 suppressions
  1. 12 0
      Cargo.lock
  2. 1 0
      Cargo.toml
  3. 98 5
      src/lib.rs

+ 12 - 0
Cargo.lock

@@ -155,6 +155,7 @@ dependencies = [
  "libflate",
  "serde",
  "serde_json",
+ "smart-default",
 ]
 
 [[package]]
@@ -218,6 +219,17 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "smart-default"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "syn"
 version = "2.0.71"

+ 1 - 0
Cargo.toml

@@ -10,3 +10,4 @@ flate2 = "1.0.30"
 libflate = "2.1.0"
 serde = { version = "^1.0.188", features = ["derive"] }
 serde_json = "1.0.120"
+smart-default = "0.7.1"

+ 98 - 5
src/lib.rs

@@ -1,4 +1,4 @@
-use anyhow::Context;
+use anyhow::{Context, Ok};
 use base64::engine::general_purpose::STANDARD;
 use base64::Engine;
 use flate2::write::DeflateEncoder;
@@ -6,11 +6,11 @@ use flate2::Compression;
 
 use serde::Serialize;
 use serde_json::{json, Value};
+use smart_default::SmartDefault;
 use std::io::prelude::*;
 
 /// 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> {
-    // Convert string to bytes
     let bytes = input.as_bytes();
 
     // Compress bytes
@@ -60,6 +60,7 @@ impl Default for ReferenceValues {
 #[derive(Debug)]
 pub struct Session {
     value: Value,
+    tracks: Vec<Track>,
 }
 
 impl Default for Session {
@@ -72,7 +73,10 @@ impl Default for Session {
             "tracks": [],
         });
 
-        Self { value }
+        Self {
+            value,
+            tracks: Vec::new(),
+        }
     }
 }
 
@@ -83,10 +87,95 @@ impl Session {
         }
         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> {
         let blob = compress_string(&self.value.to_string())?;
         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)]
@@ -95,9 +184,13 @@ mod tests {
 
     #[test]
     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/")?;
-        println!("Compressed and URL-safe encoded string: {}", compressed_str);
+        println!("{compressed_str}");
         Ok(())
     }
 }