#![allow(dead_code)]
use std::iter::repeat;
use std::iter::zip;
use syn::Field;
use syn::Fields;
mod clause;
mod pattern;
mod subject;
pub use clause::*;
pub use pattern::*;
pub use subject::*;
pub struct ComparablePair<'disc>(
&'disc Comparable<'disc, PatComposite>,
&'disc Comparable<'disc, Fields>,
);
#[non_exhaustive]
pub struct Comparable<'disc, T> {
pub inner: &'disc T,
variadic: Option<usize>,
arity: usize,
}
pub struct ComparablePats<'disc>(Vec<Comparable<'disc, PatComposite>>);
enum MatchKind {
Inferred,
Compound,
Empty,
None,
}
impl<'disc> ComparablePair<'disc> {
pub fn as_composite(&self) -> &PatComposite {
self.0.inner
}
pub fn zip(&self) -> impl Iterator<Item = (&PatFieldKind, &Field)> {
if self.contains_residual() {
debug_assert!(self.has_variadic_last());
}
if let PatComposite::Inferred = self.0.inner {
zip(repeat(&PatFieldKind::Infer), self.1.inner)
.collect::<Vec<(&PatFieldKind, &Field)>>()
.into_iter()
} else {
zip(self.0.inner, self.1.inner)
.collect::<Vec<(&PatFieldKind, &Field)>>()
.into_iter()
}
}
fn check_arity_equality(&self) -> bool {
matches!(self, ComparablePair(p, i) if p.arity == i.arity)
}
fn contains_residual(&self) -> bool {
matches!(self, ComparablePair(p, _) if p.variadic.is_some())
}
fn has_variadic_last(&self) -> bool {
matches!(self, ComparablePair(p, _) if p.variadic.map(|pos| pos == p.arity - 1).unwrap_or_default())
}
fn check_minimum_arity_satisfaction(&self) -> bool {
matches!(self, ComparablePair(p, i) if p.variadic.map(|_| p.arity - 1).unwrap_or_else(|| p.arity) <= i.arity )
}
fn match_kind(&self) -> MatchKind {
match (self.0.inner, self.1.inner) {
(&PatComposite::Named { .. }, &Fields::Named(..)) => MatchKind::Compound,
(&PatComposite::Unnamed { .. }, &Fields::Unnamed(..)) => MatchKind::Compound,
(PatComposite::Unit, Fields::Unit) => MatchKind::Empty,
(PatComposite::Inferred, _) => MatchKind::Inferred,
_ => MatchKind::None,
}
}
}
impl<'disc> ComparablePats<'disc> {
pub fn compare(&'disc self, comp_item: &'disc Comparable<Fields>) -> Option<ComparablePair> {
self.iter().find_map(into_comparable_pair(comp_item))
}
}
pub fn into_comparable_pair<'a>(
fields: &'a Comparable<Fields>,
) -> impl FnMut(&'a Comparable<PatComposite>) -> Option<ComparablePair<'a>> {
move |shape: &Comparable<PatComposite>| {
let cmp_pair = ComparablePair::from((shape, fields));
match cmp_pair.match_kind() {
MatchKind::Inferred => Some(cmp_pair),
MatchKind::Compound => {
if cmp_pair.has_variadic_last() {
cmp_pair
.check_minimum_arity_satisfaction()
.then_some(cmp_pair)
} else {
cmp_pair.check_arity_equality().then_some(cmp_pair)
}
}
MatchKind::Empty => Some(cmp_pair),
_ => None,
}
}
}
mod boilerplate {
use std::ops::Deref;
use super::*;
impl<'disc> Deref for ComparablePats<'disc> {
type Target = Vec<Comparable<'disc, PatComposite>>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<'disc> From<ComparablePair<'disc>> for (&'disc PatComposite, &'disc Fields) {
fn from(val: ComparablePair<'disc>) -> Self {
(val.0.inner, val.1.inner)
}
}
impl<'disc> From<&'disc PenumExpr> for ComparablePats<'disc> {
fn from(value: &'disc PenumExpr) -> Self {
Self(
value
.pattern
.iter()
.map(|pattern| Comparable::from(&pattern.group))
.collect(),
)
}
}
impl<'a> From<(&'a Comparable<'a, PatComposite>, &'a Comparable<'a, Fields>)>
for ComparablePair<'a>
{
fn from(value: (&'a Comparable<PatComposite>, &'a Comparable<Fields>)) -> Self {
Self(value.0, value.1)
}
}
impl<'disc> From<&'disc PatComposite> for Comparable<'disc, PatComposite> {
fn from(value: &'disc PatComposite) -> Self {
Self {
inner: value,
variadic: value.get_variadic_position(),
arity: value.len(),
}
}
}
impl<'disc> Comparable<'disc, PatComposite> {
pub fn new(value: &'disc PatComposite) -> Self {
Self {
inner: value,
variadic: value.get_variadic_position(),
arity: value.len(),
}
}
}
impl<'disc> From<&'disc Fields> for Comparable<'disc, Fields> {
fn from(value: &'disc Fields) -> Self {
Self {
inner: value,
variadic: None,
arity: value.len(),
}
}
}
}