#[derive(Debug, Clone)]
pub struct LabelledSequence<'a> {
label_base: String,
labels: Vec<String>,
sequence: &'a str,
negative_substring: bool,
}
impl<'a> LabelledSequence<'a> {
pub fn new(label_base: impl ToString, sequence: &'a str) -> Self {
let label_base = label_base.to_string();
let labels = vec![label_base.clone()];
Self {
label_base,
labels,
sequence,
negative_substring: false,
}
}
pub fn label(&self) -> &String {
self.labels.last().unwrap()
}
pub fn label_cloned(&self) -> String {
self.label().to_owned()
}
pub fn all_labels(&self) -> &[String] {
&self.labels
}
pub fn sequence(&self) -> &'a str {
self.sequence
}
pub fn is_negative_substring(&self) -> bool {
self.negative_substring
}
pub fn increment_label(&mut self) -> Option<(String, String)> {
let result = if self.labels.len() == 1 {
let old_name = self.labels[0].clone();
self.labels[0].push_str(" 1");
let new_name = self.labels[0].clone();
Some((old_name, new_name))
} else {
None
};
self.labels
.push(format!("{} {}", self.label_base, self.labels.len() + 1));
result
}
pub fn substring(&self, offset: usize, limit: usize) -> Self {
assert!(!self.negative_substring);
if offset <= limit {
Self {
label_base: self.label_base.clone(),
labels: self.labels.clone(),
sequence: char_substring(self.sequence, offset, limit),
negative_substring: false,
}
} else {
Self {
label_base: self.label_base.clone(),
labels: self.labels.clone(),
sequence: char_substring(self.sequence, limit, offset),
negative_substring: true,
}
}
}
}
fn char_substring(string: &str, offset: usize, limit: usize) -> &str {
let mut indices = string
.char_indices()
.map(|(index, _)| index)
.chain(Some(string.len()));
let byte_offset = indices.by_ref().nth(offset).unwrap_or_else(|| {
panic!(
"The string contains {} characters, but the offset is {offset}",
string.chars().count()
)
});
if offset == limit {
&string[byte_offset..byte_offset]
} else {
assert!(offset < limit, "offset < limit: {offset} < {limit}");
let byte_limit = indices.nth(limit - offset - 1).unwrap_or_else(|| {
panic!(
"The string contains {} characters, but the limit is {limit}",
string.chars().count()
)
});
&string[byte_offset..byte_limit]
}
}