use gbz::{support, FullPathName};
use std::collections::BTreeSet;
use std::fmt::Display;
use std::ops::Range;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum HaplotypeOutput {
All,
Distinct,
ReferenceOnly,
}
impl Display for HaplotypeOutput {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
HaplotypeOutput::All => write!(f, "all"),
HaplotypeOutput::Distinct => write!(f, "distinct"),
HaplotypeOutput::ReferenceOnly => write!(f, "reference only"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(super) enum QueryType {
PathOffset(FullPathName),
PathInterval(FullPathName, usize),
Nodes(BTreeSet<usize>),
Between((usize, usize), Option<usize>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SubgraphQuery {
query_type: QueryType,
context: usize,
snarls: bool,
output: HaplotypeOutput,
}
impl SubgraphQuery {
pub const DEFAULT_CONTEXT: usize = 100;
pub const DEFAULT_SNARLS: bool = false;
pub const DEFAULT_OUTPUT: HaplotypeOutput = HaplotypeOutput::All;
pub fn path_offset(path_name: &FullPathName, offset: usize) -> Self {
let mut path_name = path_name.clone();
path_name.fragment = offset;
SubgraphQuery {
query_type: QueryType::PathOffset(path_name),
context: Self::DEFAULT_CONTEXT,
snarls: Self::DEFAULT_SNARLS,
output: Self::DEFAULT_OUTPUT,
}
}
pub fn path_interval(path_name: &FullPathName, interval: Range<usize>) -> Self {
let mut path_name = path_name.clone();
path_name.fragment = interval.start;
SubgraphQuery {
query_type: QueryType::PathInterval(path_name, interval.len()),
context: Self::DEFAULT_CONTEXT,
snarls: Self::DEFAULT_SNARLS,
output: Self::DEFAULT_OUTPUT,
}
}
pub fn nodes(nodes: impl IntoIterator<Item = usize>) -> Self {
SubgraphQuery {
query_type: QueryType::Nodes(nodes.into_iter().collect()),
context: Self::DEFAULT_CONTEXT,
snarls: Self::DEFAULT_SNARLS,
output: Self::DEFAULT_OUTPUT,
}
}
pub fn between(start: usize, end: usize, limit: Option<usize>) -> Self {
SubgraphQuery {
query_type: QueryType::Between((start, end), limit),
context: Self::DEFAULT_CONTEXT,
snarls: Self::DEFAULT_SNARLS,
output: Self::DEFAULT_OUTPUT,
}
}
pub fn with_context(self, context: usize) -> Self {
SubgraphQuery { context, ..self }
}
pub fn with_snarls(self, snarls: bool) -> Self {
SubgraphQuery { snarls, ..self }
}
pub fn with_output(self, output: HaplotypeOutput) -> Self {
if let QueryType::Nodes(_) = self.query_type {
assert!(output != HaplotypeOutput::ReferenceOnly, "Reference-only output is not supported for node-based queries");
}
SubgraphQuery { output, ..self }
}
pub(super) fn query_type(&self) -> &QueryType {
&self.query_type
}
pub fn context(&self) -> usize {
self.context
}
pub fn snarls(&self) -> bool {
self.snarls
}
pub fn output(&self) -> HaplotypeOutput {
self.output
}
}
impl Display for SubgraphQuery {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let context_str = if self.snarls() {
format!("{} with snarls", self.context)
} else {
format!("{}", self.context)
};
match self.query_type() {
QueryType::PathOffset(path_name) => write!(f, "(path {}, context {}, {})", path_name, context_str, self.output),
QueryType::PathInterval(path_name, len) => write!(f, "(path {}, len {}, context {}, {})", path_name, len, context_str, self.output),
QueryType::Nodes(nodes) => write!(f, "(nodes {:#?}, context {}, {})", nodes, context_str, self.output),
QueryType::Between((start, end), limit) => {
let (start_id, start_o) = support::decode_node(*start);
let (end_id, end_o) = support::decode_node(*end);
let limit_str = if let Some(limit) = limit {
format!(", limit {}", limit)
} else {
String::new()
};
write!(f, "(between ({} {}) and ({} {}){}, {})", start_id, start_o, end_id, end_o, limit_str, self.output)
},
}
}
}