use crate::{digest::Digest, instant::Instant};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Output<Value = ()> {
pub value: Value,
pub digested: usize,
}
impl<Text: ?Sized + Digest> Instant<&Text> {
#[inline]
pub unsafe fn accept_unchecked(&self, n: usize) -> Output<()> {
debug_assert!(self.rest().validate(n));
Output {
value: (),
digested: n,
}
}
#[inline]
pub fn accept(&self, n: usize) -> Option<Output<()>> {
self
.rest()
.validate(n)
.then(|| unsafe { self.accept_unchecked(n) })
}
}
impl<Value> Output<Value> {
#[inline]
pub fn map<NewValue>(self, f: impl FnOnce(Value) -> NewValue) -> Output<NewValue> {
Output {
value: f(self.value),
digested: self.digested,
}
}
#[inline]
pub const fn as_ref(&self) -> Output<&Value> {
Output {
value: &self.value,
digested: self.digested,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::instant::Instant;
#[test]
fn instant_accept_unchecked() {
let instant = Instant::new("123");
assert_eq!(unsafe { instant.accept_unchecked(0).digested }, 0);
assert_eq!(unsafe { instant.accept_unchecked(1).digested }, 1);
assert_eq!(unsafe { instant.accept_unchecked(2).digested }, 2);
assert_eq!(unsafe { instant.accept_unchecked(3).digested }, 3);
let instant = Instant::new(b"123" as &[u8]);
assert_eq!(unsafe { instant.accept_unchecked(0).digested }, 0);
assert_eq!(unsafe { instant.accept_unchecked(1).digested }, 1);
assert_eq!(unsafe { instant.accept_unchecked(2).digested }, 2);
assert_eq!(unsafe { instant.accept_unchecked(3).digested }, 3);
}
#[test]
#[should_panic]
fn instant_accept_unchecked_overflow() {
let instant = Instant::new("123");
unsafe { instant.accept_unchecked(4) };
}
#[test]
#[should_panic]
fn instant_bytes_accept_unchecked_overflow() {
let instant = Instant::new(b"123" as &[u8]);
unsafe { instant.accept_unchecked(4) };
}
#[test]
#[should_panic]
fn instant_accept_unchecked_invalid_code_point() {
let instant = Instant::new("好");
unsafe { instant.accept_unchecked(1) };
}
#[test]
fn instant_accept() {
let instant = Instant::new("123");
assert_eq!(instant.accept(0).map(|output| output.digested), Some(0));
assert_eq!(instant.accept(1).map(|output| output.digested), Some(1));
assert_eq!(instant.accept(2).map(|output| output.digested), Some(2));
assert_eq!(instant.accept(3).map(|output| output.digested), Some(3));
assert!(instant.accept(4).is_none());
let instant = Instant::new("好");
assert_eq!(instant.accept(0).map(|output| output.digested), Some(0));
assert!(instant.accept(1).is_none());
assert!(instant.accept(2).is_none());
assert_eq!(instant.accept(3).map(|output| output.digested), Some(3));
assert!(instant.accept(4).is_none());
let instant = Instant::new(b"123" as &[u8]);
assert_eq!(instant.accept(0).map(|output| output.digested), Some(0));
assert_eq!(instant.accept(1).map(|output| output.digested), Some(1));
assert_eq!(instant.accept(2).map(|output| output.digested), Some(2));
assert_eq!(instant.accept(3).map(|output| output.digested), Some(3));
assert!(instant.accept(4).is_none());
}
#[test]
fn output_map() {
assert_eq!(
Output {
value: 1,
digested: 0,
}
.map(|value| value + 1),
Output {
value: 2,
digested: 0,
}
);
}
#[test]
fn output_as_ref() {
let o = Output {
digested: 1,
value: 1,
};
assert_eq!(o.as_ref().digested, 1);
assert_eq!(o.as_ref().value, &1);
}
#[test]
fn output_debug() {
let _ = format!(
"{:?}",
Output {
digested: 1,
value: 1
}
);
}
#[test]
fn output_clone_eq() {
let o = Output {
digested: 1,
value: 1,
};
let o2 = o.clone();
assert_eq!(o, o2);
}
}