use std::ops::Range;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct KeyPos {
key: Vec<u8>,
tag: Range<usize>,
}
impl KeyPos {
#[must_use]
pub fn new(key: Vec<u8>, tag: Range<usize>) -> Self {
assert!(tag.start <= tag.end, "tag range must be valid");
assert!(tag.end <= key.len(), "tag range must be within key");
Self { key, tag }
}
#[must_use]
pub fn without_tag(key: Vec<u8>) -> Self {
let len = key.len();
Self { key, tag: 0..len }
}
#[must_use]
pub fn key(&self) -> &[u8] {
&self.key
}
#[must_use]
pub fn key_len(&self) -> usize {
self.key.len()
}
#[must_use]
pub fn tag(&self) -> Range<usize> {
self.tag.clone()
}
#[must_use]
pub fn tag_bytes(&self) -> &[u8] {
&self.key[self.tag.clone()]
}
#[must_use]
pub fn tag_len(&self) -> usize {
self.tag.end - self.tag.start
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ArgPos {
data: Vec<u8>,
}
impl ArgPos {
#[must_use]
pub fn new(data: Vec<u8>) -> Self {
Self { data }
}
#[must_use]
pub fn bytes(&self) -> &[u8] {
&self.data
}
#[must_use]
pub fn len(&self) -> usize {
self.data.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn keypos_default_tag_is_full_key() {
let kp = KeyPos::without_tag(b"abc".to_vec());
assert_eq!(kp.tag_bytes(), b"abc");
assert_eq!(kp.tag_len(), 3);
assert_eq!(kp.key_len(), 3);
}
#[test]
fn keypos_inner_tag_indexes_inside_key() {
let kp = KeyPos::new(b"{ab}cd".to_vec(), 1..3);
assert_eq!(kp.tag_bytes(), b"ab");
assert_eq!(kp.key(), b"{ab}cd");
}
#[test]
#[should_panic(expected = "tag range must be valid")]
fn keypos_rejects_inverted_range() {
let bad = std::ops::Range::<usize> { start: 2, end: 1 };
let _ = KeyPos::new(b"abc".to_vec(), bad);
}
#[test]
#[should_panic(expected = "tag range must be within key")]
fn keypos_rejects_overrun_range() {
let _ = KeyPos::new(b"abc".to_vec(), 0..4);
}
#[test]
fn argpos_round_trips() {
let a = ArgPos::new(vec![1, 2, 3]);
assert_eq!(a.len(), 3);
assert!(!a.is_empty());
assert_eq!(a.bytes(), &[1, 2, 3]);
}
}