use alloc::borrow::ToOwned as _;
use alloc::sync::Arc;
use core::ops::{Deref, Range};
use core::ptr;
use crate::types::{ErrorLocationProvider, SourceLocation};
#[derive(Clone, Debug, Eq)]
pub enum TokenText {
Slice {
source: Arc<str>,
range: Range<usize>,
},
Owned(Arc<str>),
Static(&'static str),
}
impl TokenText {
#[must_use]
pub const fn slice(source: Arc<str>, start: usize, end: usize) -> Self {
Self::Slice {
source,
range: start..end,
}
}
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::Slice { source, range } => &source[range.clone()],
Self::Owned(text) => text,
Self::Static(text) => text,
}
}
#[must_use]
pub fn len(&self) -> usize {
self.as_str().len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn to_owned_string(&self) -> String {
self.as_str().to_owned()
}
pub fn clone_into(&self, buf: &mut String) {
buf.clear();
buf.push_str(self.as_str());
}
}
impl Deref for TokenText {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl PartialEq for TokenText {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::Slice {
source: s1,
range: r1,
},
Self::Slice {
source: s2,
range: r2,
},
) => {
if Arc::ptr_eq(s1, s2) && r1 == r2 {
true
} else {
s1[r1.clone()] == s2[r2.clone()]
}
}
(Self::Owned(t1), Self::Owned(t2)) => Arc::ptr_eq(t1, t2) || t1 == t2,
(Self::Static(t1), Self::Static(t2)) => t1 == t2,
_ => self.as_str() == other.as_str(),
}
}
}
impl From<String> for TokenText {
fn from(value: String) -> Self {
Self::Owned(Arc::from(value))
}
}
impl From<&'static str> for TokenText {
#[inline]
fn from(value: &'static str) -> Self {
Self::Static(value)
}
}
impl From<Arc<str>> for TokenText {
fn from(value: Arc<str>) -> Self {
Self::Owned(value)
}
}
impl PartialEq<&str> for TokenText {
fn eq(&self, other: &&str) -> bool {
if matches!(self, Self::Static(s) if ptr::eq(s, other)) {
return true;
}
self.as_str() == *other
}
}
impl PartialEq<TokenText> for &str {
fn eq(&self, other: &TokenText) -> bool {
*self == other.as_str()
}
}
impl PartialEq<String> for TokenText {
fn eq(&self, other: &String) -> bool {
self.as_str() == other
}
}
impl PartialEq<TokenText> for String {
fn eq(&self, other: &TokenText) -> bool {
self == other.as_str()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Token {
pub text: TokenText,
pub loc: Option<SourceLocation>,
pub noexpand: Option<bool>,
pub treat_as_relax: Option<bool>,
}
impl Token {
#[must_use]
pub fn new<T>(text: T, loc: Option<SourceLocation>) -> Self
where
T: Into<TokenText>,
{
Self {
text: text.into(),
loc,
noexpand: None,
treat_as_relax: None,
}
}
#[inline]
#[must_use]
pub fn text(&self) -> &str {
self.text.as_str()
}
pub fn set_text<T>(&mut self, text: T)
where
T: Into<TokenText>,
{
self.text = text.into();
}
#[must_use]
pub fn range<T: Into<TokenText>>(self, end_token: Self, text: T) -> Option<Self> {
let loc = SourceLocation::range(self.loc, end_token.loc)?;
Some(Self {
text: text.into(),
loc: Some(loc),
noexpand: None,
treat_as_relax: None,
})
}
}
impl ErrorLocationProvider for Token {
fn loc(&self) -> Option<&SourceLocation> {
self.loc.as_ref()
}
}
impl ErrorLocationProvider for Option<Token> {
fn loc(&self) -> Option<&SourceLocation> {
let t = self.as_ref()?;
t.loc.as_ref()
}
}