#![no_std]
#![forbid(
unsafe_code,
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
future_incompatible,
rust_2018_idioms
)]
#[cfg(feature = "builder")]
pub mod builder;
#[cfg(all(feature = "builder", not(intern_str_no_alloc)))]
extern crate alloc;
#[cfg(all(feature = "builder", intern_str_no_alloc))]
extern crate std as alloc;
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "builder")]
use alloc::vec::Vec;
use core::{cmp, hash, ops};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Node<'inst, Input, Output> {
inputs: MaybeSlice<'inst, (Input, usize)>,
output: Output,
default: usize,
amount: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Graph<'inst, 'nodes, Input, Output> {
nodes: &'nodes [Node<'inst, Input, Output>],
start: usize,
}
impl<'inst, Input, Output> Node<'inst, Input, Output> {
pub const fn new(
inputs: &'inst [(Input, usize)],
output: Output,
default: usize,
amount: usize,
) -> Self {
Self {
inputs: MaybeSlice::Slice(inputs),
output,
default,
amount,
}
}
}
impl<'inst, Input: Segmentable, Output> Node<'inst, Input, Output> {
fn next(&self, input: &Input) -> usize {
match self.inputs.binary_search_by(|(i, _)| i.cmp(input)) {
Ok(i) => self.inputs[i].1,
Err(_) => self.default,
}
}
pub fn inputs(&self) -> &[(Input, usize)] {
match &self.inputs {
MaybeSlice::Slice(s) => s,
#[cfg(feature = "builder")]
MaybeSlice::Vec(v) => v,
}
}
pub fn output(&self) -> &Output {
&self.output
}
pub fn default(&self) -> usize {
self.default
}
pub fn amount(&self) -> usize {
self.amount
}
}
impl<'nodes, 'inst, Input, Output> Graph<'inst, 'nodes, Input, Output> {
pub const fn new(nodes: &'nodes [Node<'inst, Input, Output>], start: usize) -> Self {
Self { nodes, start }
}
}
impl<'nodes, 'inst, Input: Segmentable, Output> Graph<'inst, 'nodes, Input, Output> {
pub fn nodes(&self) -> &'nodes [Node<'inst, Input, Output>] {
self.nodes
}
pub fn start(&self) -> usize {
self.start
}
pub fn process(&self, mut input: Input) -> &Output {
let mut node = &self.nodes[self.start];
loop {
let (chunk, rest) = match input.split(node.amount) {
Some(result) => result,
None => {
return &node.output;
}
};
node = &self.nodes[node.next(&chunk)];
input = rest;
}
}
}
pub trait Segmentable: Ord + Sized {
fn split(self, at: usize) -> Option<(Self, Self)>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<'a> Segmentable for &'a str {
fn split(self, at: usize) -> Option<(Self, Self)> {
if at > self.len() {
return None;
}
let (left, right) = self.split_at(at);
Some((left, right))
}
fn len(&self) -> usize {
str::len(self)
}
}
impl<'a, T: Ord> Segmentable for &'a [T] {
fn split(self, at: usize) -> Option<(Self, Self)> {
if at > self.len() {
return None;
}
let (left, right) = self.split_at(at);
Some((left, right))
}
fn len(&self) -> usize {
<[T]>::len(self)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CaseInsensitive<T>(pub T);
impl<T> ops::Deref for CaseInsensitive<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> ops::DerefMut for CaseInsensitive<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T> From<T> for CaseInsensitive<T> {
fn from(value: T) -> Self {
CaseInsensitive(value)
}
}
impl<T: AsRef<[u8]>> PartialEq for CaseInsensitive<T> {
fn eq(&self, other: &Self) -> bool {
self.0.as_ref().eq_ignore_ascii_case(other.0.as_ref())
}
}
impl<T: AsRef<[u8]>> Eq for CaseInsensitive<T> {}
impl<T: AsRef<[u8]>> PartialOrd for CaseInsensitive<T> {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T: AsRef<[u8]>> Ord for CaseInsensitive<T> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
let this = self.0.as_ref();
let other = other.0.as_ref();
let common_len = cmp::min(this.len(), other.len());
let this_seg = &this[..common_len];
let other_seg = &other[..common_len];
for (a, b) in this_seg.iter().zip(other_seg.iter()) {
let a = a.to_ascii_lowercase();
let b = b.to_ascii_lowercase();
match a.cmp(&b) {
cmp::Ordering::Equal => continue,
other => return other,
}
}
this.len().cmp(&other.len())
}
}
impl<T: AsRef<[u8]>> hash::Hash for CaseInsensitive<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
for byte in self.0.as_ref() {
state.write_u8(byte.to_ascii_lowercase());
}
}
}
impl<T: Segmentable + AsRef<[u8]>> Segmentable for CaseInsensitive<T> {
fn split(self, at: usize) -> Option<(Self, Self)> {
T::split(self.0, at).map(|(left, right)| (left.into(), right.into()))
}
fn len(&self) -> usize {
T::len(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum MaybeSlice<'a, T> {
Slice(&'a [T]),
#[cfg(feature = "builder")]
Vec(Vec<T>),
}
impl<'a, T> core::ops::Deref for MaybeSlice<'a, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
match self {
MaybeSlice::Slice(slice) => slice,
#[cfg(feature = "builder")]
MaybeSlice::Vec(vec) => vec,
}
}
}