From 7c9a35a07f50cf0583b11477e4c4b610d54eef68 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Sun, 7 Apr 2024 07:50:45 -0400 Subject: [PATCH 1/4] Start a simple implementation of `odgi position` --- polbin/src/cmds.rs | 26 ++++++++++++++++++++++++++ polbin/src/flatgfa.rs | 24 ++++++++++++++++++++++++ polbin/src/main.rs | 4 ++++ 3 files changed, 54 insertions(+) diff --git a/polbin/src/cmds.rs b/polbin/src/cmds.rs index d12613ce..47305b9d 100644 --- a/polbin/src/cmds.rs +++ b/polbin/src/cmds.rs @@ -73,6 +73,32 @@ pub fn stats(gfa: &flatgfa::FlatGFA, args: Stats) { } } +/// find a nucleotide position within a path +#[derive(FromArgs, PartialEq, Debug)] +#[argh(subcommand, name = "position")] +pub struct Position { + /// path_name,offset,orientation + #[argh(option, short = 'p')] + path_pos: String, +} + +pub fn position(gfa: &flatgfa::FlatGFA, args: Position) -> Result<(), &'static str> { + // Parse the position triple, which looks like `path,42,+`. + let (path_name, offset, orientation) = { + let parts: Vec<_> = args.path_pos.split(",").collect(); + if parts.len() != 3 { + return Err("position must be path_name,offset,orientation"); + } + let off: usize = parts[1].parse().or(Err("offset must be a number"))?; + let ori: flatgfa::Orientation = parts[2].parse().or(Err("orientation must be + or -"))?; + (parts[0], off, ori) + }; + + let path_id = gfa.find_path(path_name.into()).ok_or("path not found")?; + dbg!(path_id, offset, orientation); + Ok(()) +} + /// create a subset graph #[derive(FromArgs, PartialEq, Debug)] #[argh(subcommand, name = "extract")] diff --git a/polbin/src/flatgfa.rs b/polbin/src/flatgfa.rs index 3e9ad063..07848ca5 100644 --- a/polbin/src/flatgfa.rs +++ b/polbin/src/flatgfa.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use crate::pool::{Index, Pool, Span}; use bstr::BStr; use num_enum::{IntoPrimitive, TryFromPrimitive}; @@ -133,6 +135,20 @@ pub enum Orientation { Backward, // - } +impl FromStr for Orientation { + type Err = (); + + fn from_str(s: &str) -> Result { + if s == "+" { + Ok(Orientation::Forward) + } else if s == "-" { + Ok(Orientation::Backward) + } else { + Err(()) + } + } +} + /// An oriented reference to a segment. /// /// A Handle refers to the forward (+) or backward (-) orientation for a given segment. @@ -234,6 +250,14 @@ impl<'a> FlatGFA<'a> { .map(|i| i as Index) } + /// Look up a path by its name. + pub fn find_path(&self, name: &BStr) -> Option { + self.paths + .iter() + .position(|path| self.get_path_name(path) == name) + .map(|i| i as Index) + } + /// Get all the steps for a path. pub fn get_steps(&self, path: &Path) -> &[Handle] { &self.steps[path.steps.range()] diff --git a/polbin/src/main.rs b/polbin/src/main.rs index f2c479d7..56af38f2 100644 --- a/polbin/src/main.rs +++ b/polbin/src/main.rs @@ -68,6 +68,7 @@ enum Command { Toc(cmds::Toc), Paths(cmds::Paths), Stats(cmds::Stats), + Position(cmds::Position), Extract(cmds::Extract), } @@ -124,6 +125,9 @@ fn main() -> Result<(), &'static str> { Some(Command::Stats(sub_args)) => { cmds::stats(&gfa, sub_args); } + Some(Command::Position(sub_args)) => { + cmds::position(&gfa, sub_args)?; + } Some(Command::Extract(sub_args)) => { let store = cmds::extract(&gfa, sub_args)?; dump(&store.view(), &args.output); From 57b8efaea2a0414a4f2bacde4d3bb3589d9e5c41 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Sun, 7 Apr 2024 08:04:07 -0400 Subject: [PATCH 2/4] Actually find the position! --- polbin/src/cmds.rs | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/polbin/src/cmds.rs b/polbin/src/cmds.rs index 47305b9d..76c756f8 100644 --- a/polbin/src/cmds.rs +++ b/polbin/src/cmds.rs @@ -95,7 +95,36 @@ pub fn position(gfa: &flatgfa::FlatGFA, args: Position) -> Result<(), &'static s }; let path_id = gfa.find_path(path_name.into()).ok_or("path not found")?; - dbg!(path_id, offset, orientation); + let path = &gfa.paths[path_id as usize]; + assert_eq!( + orientation, + flatgfa::Orientation::Forward, + "only + is implemented so far" + ); + dbg!(path_id, offset, &orientation); + + // Traverse the path until we reach the position. + let mut cur_pos = 0; + let mut found = None; + for step in gfa.get_steps(path) { + let seg = gfa.get_handle_seg(*step); + // TODO Handle backwards segments! + let end_pos = cur_pos + seg.len(); + if offset < end_pos { + // Found it! + found = Some((*step, offset - cur_pos)); + break; + } + cur_pos = end_pos; + } + + // Print the match. + if let Some((handle, seg_off)) = found { + let seg = gfa.get_handle_seg(handle); + let seg_name = seg.name; + println!("{},{},{}", seg_name, seg_off, handle.orient()); + } + Ok(()) } From 629cb66e9310c14390c3a780633fcdb08dde9914 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Sun, 7 Apr 2024 08:06:05 -0400 Subject: [PATCH 3/4] Print position like odgi --- polbin/src/cmds.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/polbin/src/cmds.rs b/polbin/src/cmds.rs index 76c756f8..10b567d6 100644 --- a/polbin/src/cmds.rs +++ b/polbin/src/cmds.rs @@ -101,7 +101,6 @@ pub fn position(gfa: &flatgfa::FlatGFA, args: Position) -> Result<(), &'static s flatgfa::Orientation::Forward, "only + is implemented so far" ); - dbg!(path_id, offset, &orientation); // Traverse the path until we reach the position. let mut cur_pos = 0; @@ -122,7 +121,16 @@ pub fn position(gfa: &flatgfa::FlatGFA, args: Position) -> Result<(), &'static s if let Some((handle, seg_off)) = found { let seg = gfa.get_handle_seg(handle); let seg_name = seg.name; - println!("{},{},{}", seg_name, seg_off, handle.orient()); + println!("#source.path.pos\ttarget.graph.pos"); + println!( + "{},{},{}\t{},{},{}", + path_name, + offset, + orientation, + seg_name, + seg_off, + handle.orient() + ); } Ok(()) From 7fa5b90d2c1f31f682e158dd4b0cb41468256ec1 Mon Sep 17 00:00:00 2001 From: Adrian Sampson Date: Sun, 7 Apr 2024 08:09:52 -0400 Subject: [PATCH 4/4] Remove an outdated TODO --- polbin/src/cmds.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/polbin/src/cmds.rs b/polbin/src/cmds.rs index 10b567d6..685c53dc 100644 --- a/polbin/src/cmds.rs +++ b/polbin/src/cmds.rs @@ -107,7 +107,6 @@ pub fn position(gfa: &flatgfa::FlatGFA, args: Position) -> Result<(), &'static s let mut found = None; for step in gfa.get_steps(path) { let seg = gfa.get_handle_seg(*step); - // TODO Handle backwards segments! let end_pos = cur_pos + seg.len(); if offset < end_pos { // Found it!