use std::hash::{Hash, Hasher};
use std::cmp::Ordering;
use std::str::FromStr;
use std::marker::PhantomData;
use std::error::Error as StdError;
use std::ops::{Range, Deref, DerefMut};
use std::borrow::{Borrow, BorrowMut};
use std::fmt::{self, Display, Debug, Formatter};
use regex::{Match, Captures};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
use crate::{FromMatch, RegexPattern, Error};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Spanned<T: ?Sized> {
span: (usize, usize),
value: T,
}
impl<T> Spanned<T> {
pub const fn new(value: T, span: Range<usize>) -> Self {
let span = (span.start, span.end);
Spanned { value, span }
}
pub fn into_value(self) -> T {
self.value
}
}
impl<T: ?Sized> Spanned<T> {
pub fn value(&self) -> &T {
&self.value
}
pub fn value_mut(&mut self) -> &mut T {
&mut self.value
}
pub fn span(&self) -> Range<usize> {
let (start, end) = self.span;
start..end
}
}
impl<T: ?Sized + RegexPattern> RegexPattern for Spanned<T> {
fn fmt_pattern(f: &mut Formatter<'_>) -> fmt::Result {
<T as RegexPattern>::fmt_pattern(f)
}
}
impl<'h, T> FromMatch<'h> for Spanned<T>
where
T: FromMatch<'h>,
{
fn from_match(name: &'static str, m: Match<'h>, captures: &Captures<'h>) -> Result<Self, Error> {
T::from_match(name, m, captures).map(|value| Spanned::new(value, m.range()))
}
}
impl<T: ?Sized + ErasedLifetime> ErasedLifetime for Spanned<T> {
type Erased = Spanned<T::Erased>;
}
impl<T: ?Sized + PartialEq> PartialEq<T> for Spanned<T> {
fn eq(&self, other: &T) -> bool {
self.value.eq(other)
}
}
impl<T: ?Sized + PartialEq> PartialEq<&T> for Spanned<T> {
fn eq(&self, other: &&T) -> bool {
self.value.eq(*other)
}
}
impl<T: ?Sized + PartialEq> PartialEq<&mut T> for Spanned<T> {
fn eq(&self, other: &&mut T) -> bool {
self.value.eq(&**other)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd<T> for Spanned<T> {
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
self.value.partial_cmp(other)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd<&T> for Spanned<T> {
fn partial_cmp(&self, other: &&T) -> Option<Ordering> {
self.value.partial_cmp(*other)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd<&mut T> for Spanned<T> {
fn partial_cmp(&self, other: &&mut T) -> Option<Ordering> {
self.value.partial_cmp(&**other)
}
}
impl<T: ?Sized + Hash> Hash for Spanned<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.value.hash(state);
}
}
impl<T: ?Sized + Display> Display for Spanned<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.value, f)
}
}
impl<T: ?Sized> Deref for Spanned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T: ?Sized> DerefMut for Spanned<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
impl<T: ?Sized> Borrow<T> for Spanned<T> {
fn borrow(&self) -> &T {
&self.value
}
}
impl<T: ?Sized> BorrowMut<T> for Spanned<T> {
fn borrow_mut(&mut self) -> &mut T {
&mut self.value
}
}
impl<T: ?Sized> AsRef<T> for Spanned<T> {
fn as_ref(&self) -> &T {
&self.value
}
}
impl<T: ?Sized> AsMut<T> for Spanned<T> {
fn as_mut(&mut self) -> &mut T {
&mut self.value
}
}
#[derive(Clone, Copy, Eq, Default, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(transparent)]
pub struct MatchFromStr<T: ?Sized>(pub T);
impl<T: ?Sized + RegexPattern> RegexPattern for MatchFromStr<T> {
fn fmt_pattern(f: &mut Formatter<'_>) -> fmt::Result {
<T as RegexPattern>::fmt_pattern(f)
}
}
impl<'h, T> FromMatch<'h> for MatchFromStr<T>
where
T: FromStr,
T::Err: StdError + Send + Sync + 'static,
{
fn from_match(name: &'static str, m: Match<'h>, _captures: &Captures<'h>) -> Result<Self, Error> {
let value = T::from_str(m.as_str()).map_err(|error| {
Error::group_from_str(name, m.range(), error)
})?;
Ok(MatchFromStr(value))
}
}
impl<T: ?Sized + ErasedLifetime> ErasedLifetime for MatchFromStr<T> {
type Erased = MatchFromStr<T::Erased>;
}
impl<T: ?Sized + PartialEq> PartialEq for MatchFromStr<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T: ?Sized + PartialEq> PartialEq<T> for MatchFromStr<T> {
fn eq(&self, other: &T) -> bool {
self.0.eq(other)
}
}
impl<T: ?Sized + PartialEq> PartialEq<&T> for MatchFromStr<T> {
fn eq(&self, other: &&T) -> bool {
self.0.eq(*other)
}
}
impl<T: ?Sized + PartialEq> PartialEq<&mut T> for MatchFromStr<T> {
fn eq(&self, other: &&mut T) -> bool {
self.0.eq(&**other)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd for MatchFromStr<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd<T> for MatchFromStr<T> {
fn partial_cmp(&self, other: &T) -> Option<Ordering> {
self.0.partial_cmp(other)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd<&T> for MatchFromStr<T> {
fn partial_cmp(&self, other: &&T) -> Option<Ordering> {
self.0.partial_cmp(*other)
}
}
impl<T: ?Sized + PartialOrd> PartialOrd<&mut T> for MatchFromStr<T> {
fn partial_cmp(&self, other: &&mut T) -> Option<Ordering> {
self.0.partial_cmp(&**other)
}
}
impl<T: ?Sized + Ord> Ord for MatchFromStr<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0)
}
}
impl<T: ?Sized + Hash> Hash for MatchFromStr<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
impl<T> From<T> for MatchFromStr<T> {
fn from(value: T) -> Self {
MatchFromStr(value)
}
}
impl<T: ?Sized + Display> Display for MatchFromStr<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl<T: ?Sized> Deref for MatchFromStr<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: ?Sized> DerefMut for MatchFromStr<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: ?Sized> Borrow<T> for MatchFromStr<T> {
fn borrow(&self) -> &T {
&self.0
}
}
impl<T: ?Sized> BorrowMut<T> for MatchFromStr<T> {
fn borrow_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T: ?Sized> AsRef<T> for MatchFromStr<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T: ?Sized> AsMut<T> for MatchFromStr<T> {
fn as_mut(&mut self) -> &mut T {
&mut self.0
}
}
pub struct PatternDisplay<T: ?Sized>(PhantomData<fn(&T)>);
impl<T: ?Sized> Clone for PatternDisplay<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> Copy for PatternDisplay<T> {}
impl<T: ?Sized> Default for PatternDisplay<T> {
fn default() -> Self {
PatternDisplay(Default::default())
}
}
impl<T: ?Sized + RegexPattern> Debug for PatternDisplay<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"PatternDisplay<{ty}>(\"{self}\")",
ty = std::any::type_name::<T>(),
)
}
}
impl<T: ?Sized + RegexPattern> Display for PatternDisplay<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
T::fmt_pattern(f)
}
}
pub trait ErasedLifetime {
type Erased: ?Sized + 'static;
}
#[cfg(test)]
mod tests {
#[test]
fn spanned_eq_hash_borrow_consistency() {
use std::hash::BuildHasher as _;
use std::collections::HashMap;
use super::Spanned;
let sp_a = Spanned::new(String::from("hello world"), 1..12);
let sp_b = Spanned::new(String::from("hello world"), 6..17);
let sp_x = Spanned::new(String::from("nope, nope!"), 1..12);
assert_eq!(sp_a.value(), sp_b.value());
assert_eq!(sp_a, sp_a.value());
assert_eq!(sp_b, sp_b.value());
assert_eq!(sp_a, sp_b.value());
assert_eq!(sp_b, sp_a.value());
assert_ne!(sp_a.value(), sp_x.value());
assert_ne!(sp_b.value(), sp_x.value());
assert_ne!(sp_a, sp_b);
assert_ne!(sp_a, sp_x);
assert_ne!(sp_b, sp_x);
let hasher = std::hash::RandomState::new();
let hs_a = hasher.hash_one(&sp_a);
let hs_a_val = hasher.hash_one(sp_a.value());
let hs_b = hasher.hash_one(&sp_b);
let hs_b_val = hasher.hash_one(sp_b.value());
assert_eq!(hs_a, hs_a_val);
assert_eq!(hs_b, hs_b_val);
assert_eq!(hs_a, hs_b);
assert_eq!(hs_a, hs_b_val);
assert_eq!(hs_b, hs_a_val);
let map = HashMap::from([
(sp_a, 0),
(sp_b, 0),
(sp_x, 1),
]);
assert_eq!(map.len(), 3);
assert_eq!(map.get(&"hello world".to_owned()), Some(&0));
assert_eq!(map.get(&"nope, nope!".to_owned()), Some(&1));
assert_eq!(map.get(&"gibberish".to_owned()), None);
}
}