use std::fmt::Display;
use itertools::Itertools;
use util::{chars_needed_to_complete, go_on, oops, yes_and};
pub mod util;
macro_rules! oops {
($tt:tt) => {
Err(UpError::Oops {
message: format!($tt),
})
};
}
pub type UpResult<'input, T> = Result<YesAnd<'input, T>, UpError>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct YesAnd<'input, T> {
pub yes: T,
pub and: &'input str,
pub could_also: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum UpError {
Oops { message: String },
GoOn { go_on: Vec<String> },
}
pub trait UpParser<'input, T> {
fn parse(&self, input: &'input str) -> UpResult<'input, T>;
}
impl<'input, T, F> UpParser<'input, T> for F
where
F: Fn(&'input str) -> UpResult<'input, T>,
{
fn parse(&self, input: &'input str) -> UpResult<'input, T> {
self(input)
}
}
pub fn tag<'input, 'tag>(tag: &'tag str) -> impl UpParser<'input, &'input str> + 'tag {
move |input: &'input str| match input.strip_prefix(tag) {
Some(rest) => yes_and(&input[..tag.len()], rest),
None => match chars_needed_to_complete(tag, input) {
Some("") => unreachable!("would've been caught in prefix"),
Some(suggestion) => go_on([suggestion]),
None => oops!("expected {tag}, not {input}"),
},
}
}
pub fn dictionary<'input, KeyT, ValueT>(
items: impl IntoIterator<Item = (KeyT, ValueT)>,
) -> impl UpParser<'input, ValueT>
where
KeyT: Display,
ValueT: Clone,
{
let pairs = items
.into_iter()
.map(|(k, v)| (k.to_string(), v))
.sorted_by_key(|(k, _v)| std::cmp::Reverse(k.clone()))
.collect::<Vec<_>>();
move |input: &str| todo!()
}
pub fn and_then<'input, L, R>(
left: impl UpParser<'input, L>,
right: impl UpParser<'input, R>,
) -> impl UpParser<'input, (L, R)> {
move |input| todo!()
}