#![no_std]
extern crate alloc;
mod hunt;
mod pattern;
pub use {
cur_macro::odor,
hunt::Find,
once_cell::sync::Lazy,
pattern::{MultipleOdors, Odor, Scent},
};
use {
alloc::vec::Vec,
core::{
convert::{TryFrom, TryInto},
mem,
},
hunt::{Catch, Fork, ForkKind, Haul, Progress},
log::trace,
pattern::{Odors, Scents},
};
struct UnknownNextCatch;
#[derive(Debug)]
enum NextCatch<'s> {
Success(Catch<'s>),
NonSuccess,
Unknown,
}
impl<'s> NextCatch<'s> {
const fn is_unknown(&self) -> bool {
matches!(*self, Self::Unknown)
}
const fn is_success(&self) -> bool {
matches!(*self, Self::Success(..))
}
fn update(&mut self, catch: Option<Catch<'s>>) {
*self = match catch {
Some(success) => Self::Success(success),
None => Self::NonSuccess,
};
}
fn take(&mut self) -> Self {
mem::take(self)
}
}
impl<'s> Default for NextCatch<'s> {
fn default() -> Self {
Self::Unknown
}
}
impl<'s> TryFrom<NextCatch<'s>> for Option<Catch<'s>> {
type Error = UnknownNextCatch;
fn try_from(next_catch: NextCatch<'s>) -> Result<Self, Self::Error> {
match next_catch {
NextCatch::Success(catch) => Ok(Some(catch)),
NextCatch::NonSuccess => Ok(None),
NextCatch::Unknown => Err(UnknownNextCatch),
}
}
}
#[derive(Debug)]
struct Junction<'s, 'o> {
prev_haul: Haul<'s>,
odors: Odors<'o>,
fork: Fork<'s>,
fork_trail: Option<Trail<'s, 'o>>,
}
impl<'s, 'o> Junction<'s, 'o> {
fn new(prev_haul: Haul<'s>, mut odors: Odors<'o>, fork_kind: ForkKind) -> Self {
let odor = odors.next();
let mut fork = Fork::new(Progress::from(prev_haul.remaining_str()), fork_kind);
Self {
fork_trail: odor
.and_then(|odor| fork.next().map(|progress| Trail::new(progress, odor))),
odors,
prev_haul,
fork,
}
}
}
impl<'s, 'o> Iterator for Junction<'s, 'o> {
type Item = Haul<'s>;
fn next(&mut self) -> Option<Self::Item> {
let mut haul = None;
while let Some(ref mut trail) = self.fork_trail {
haul = trail.next().map(|fork_haul| {
self.fork.process_haul(&fork_haul);
let mut my_haul = self.prev_haul.clone();
#[allow(unsafe_code)]
unsafe {
my_haul.append(fork_haul);
}
my_haul
});
if haul.is_some() {
break;
}
self.fork_trail = self
.odors
.next()
.and_then(|odor| self.fork.next().map(|progress| Trail::new(progress, odor)));
}
haul
}
}
#[derive(Debug)]
struct Trail<'s, 'o> {
haul: Haul<'s>,
odor: &'o Odor,
junctions: Vec<(Junction<'s, 'o>, Scents<'o>)>,
has_hunted: bool,
}
impl<'s, 'o> Trail<'s, 'o> {
fn new(progress: Progress<'s>, odor: &'o Odor) -> Self {
Self {
haul: Haul::from(progress),
odor,
junctions: Vec::new(),
has_hunted: false,
}
}
fn hunt_junction(&mut self, mut junction: Junction<'s, 'o>, scents: &Scents<'o>) -> bool {
junction.next().map_or(false, |haul| {
self.haul = haul;
self.junctions.push((junction, scents.clone()));
true
})
}
fn is_next_char_in_range(&mut self, begin: char, end: char) -> bool {
self.haul
.next_char()
.map_or(false, |c| (begin..=end).contains(&c))
}
fn hunt_scent(&mut self, scent: &'o Scent, remaining_scents: &Scents<'o>) -> bool {
match *scent {
Scent::Char(ch) => self.haul.next_char() == Some(ch),
Scent::Range(begin, end) => self.is_next_char_in_range(begin, end),
Scent::Union(ref branches) => self.hunt_junction(
Junction::new(
self.haul.clone(),
Odors::list(branches.into_iter()),
ForkKind::Stationary,
),
remaining_scents,
),
Scent::Repetition(ref pattern) => self.hunt_junction(
Junction::new(
self.haul.clone(),
Odors::repeat_after_empty(pattern),
ForkKind::Growing,
),
remaining_scents,
),
Scent::Marked(ref odor) => self.hunt_junction(
Junction::new(self.haul.clone(), Odors::single(odor), ForkKind::Stationary),
remaining_scents,
),
}
}
fn hunt_through_scents(&mut self, mut scents: Scents<'o>) -> bool {
trace!("Searching '{:#?}' for {:#?}", self.haul, scents);
while let Some(scent) = scents.next() {
if !(self.hunt_scent(scent, &scents)) {
return false;
}
}
true
}
fn rehunt_from_junctions(&mut self) -> bool {
while let Some((junction, scents)) = self.junctions.pop() {
if self.hunt_junction(junction, &scents) && self.hunt_through_scents(scents) {
return true;
}
}
false
}
fn hunt(&mut self) -> bool {
let success = if self.has_hunted {
self.rehunt_from_junctions()
} else {
self.has_hunted = true;
self.hunt_through_scents(self.odor.scents()) || self.rehunt_from_junctions()
};
if success {
if let Some(name) = self.odor.name() {
self.haul.identify_as(name);
}
}
success
}
}
impl<'s, 'o> Iterator for Trail<'s, 'o> {
type Item = Haul<'s>;
fn next(&mut self) -> Option<Self::Item> {
self.hunt().then(|| self.haul.clone())
}
}
#[derive(Debug)]
pub struct Cur<'s, 'o> {
odor: &'o Odor,
trail: Trail<'s, 'o>,
next_catch: NextCatch<'s>,
}
impl<'s, 'o> Cur<'s, 'o> {
#[must_use]
pub fn new(odor: &'o Odor) -> Self {
Self {
trail: Trail::new(Progress::from(""), odor),
odor,
next_catch: NextCatch::Unknown,
}
}
pub fn set_search(&mut self, search: &'s str) {
self.trail = Trail::new(Progress::from(search), self.odor);
self.next_catch = NextCatch::Unknown;
}
pub fn is_clear(&mut self) -> bool {
if self.next_catch.is_unknown() {
let next_catch = self.next();
self.next_catch.update(next_catch);
}
!self.next_catch.is_success()
}
}
impl<'s, 'o> Iterator for Cur<'s, 'o> {
type Item = Catch<'s>;
fn next(&mut self) -> Option<Self::Item> {
if let Ok(catch) = self.next_catch.take().try_into() {
catch
} else {
let mut catch = None;
for mut haul in &mut self.trail {
if haul.matches_finish() {
catch = Some(haul.take_catch());
break;
}
}
catch
}
}
}