use crate::throw_sg_err;
use core::{
fmt::{Debug, Display},
ops::Deref,
};
use proc_macro2::{Span, TokenStream as TokenStream2};
use std::ffi::OsStr;
#[repr(transparent)]
pub struct ExprLit(str);
impl PartialEq<ExprLit> for ExprLit {
#[inline]
fn eq(&self, other: &ExprLit) -> bool {
PartialEq::eq(self.as_str(), other.as_str())
}
}
impl PartialEq<str> for ExprLit {
#[inline]
fn eq(&self, other: &str) -> bool {
PartialEq::eq(self.as_str(), other)
}
}
impl AsRef<OsStr> for ExprLit {
#[inline]
fn as_ref(&self) -> &OsStr {
AsRef::as_ref(self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ExprLitTryNewErr {
ExpLen { current: usize, exp: usize },
ExpQuotes,
}
impl ExprLitTryNewErr {
#[inline]
pub const fn expr_len(current: usize, exp: usize) -> Self {
Self::ExpLen { current, exp }
}
#[inline]
pub fn into_tt_err(self, span: Span) -> TokenStream2 {
match self {
Self::ExpLen { current, exp } => throw_sg_err! {
[span]: "More char expected, current: ", #current, "exp: {}", #exp, "."
},
Self::ExpQuotes => throw_sg_err! {
[span]: "Double quotes were expected."
},
}
}
}
impl Deref for ExprLit {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl Debug for ExprLit {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Debug::fmt(self as &str, f)
}
}
impl Display for ExprLit {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Display::fmt(self as &str, f)
}
}
impl ExprLit {
#[inline]
const fn __new(a: &str) -> &ExprLit {
unsafe { &*(a as *const _ as *const ExprLit) }
}
#[inline]
pub const unsafe fn new_unchecked(a: &str) -> &Self {
Self::__new(a)
}
#[allow(dead_code)]
#[inline]
pub fn try_new(a: &str) -> Result<&ExprLit, ExprLitTryNewErr> {
Self::try_new_with_fns(a, Ok, Err)
}
pub fn try_new_with_fns<'a, R>(
a: &'a str,
next: impl FnOnce(&'a ExprLit) -> R,
err: impl FnOnce(ExprLitTryNewErr) -> R,
) -> R {
let a_array = a.as_bytes();
let len = a_array.len();
if len < 2 {
return err(ExprLitTryNewErr::expr_len(len, 2));
}
debug_assert!({
#[allow(clippy::get_first)]
a_array.get(0).is_some()
});
debug_assert!({
#[allow(clippy::get_first)]
a_array.get(len - 1).is_some()
});
match unsafe { (a_array.get_unchecked(0), a_array.get_unchecked(len - 1)) } {
(b'"', b'"') =>
{} (b'\'', b'\'') if len != 3 => {
return err(ExprLitTryNewErr::expr_len(len, 3));
}
(b'\'', b'\'') =>
{} _ => return err(ExprLitTryNewErr::ExpQuotes),
}
next({
debug_assert!(a.get(1..len - 1).is_some());
let str = unsafe { a.get_unchecked(1..len - 1) };
Self::__new(str)
})
}
#[allow(dead_code)]
#[inline]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub const fn as_str(&self) -> &str {
&self.0
}
}
#[cfg(test)]
#[test]
fn test_literal() {
assert_eq!(ExprLit::try_new(""), Err(ExprLitTryNewErr::expr_len(0, 2)));
assert_eq!(
ExprLit::try_new("\""),
Err(ExprLitTryNewErr::expr_len(1, 2))
);
assert_eq!(ExprLit::try_new("\"\""), Ok(ExprLit::__new("")),);
assert_eq!(ExprLit::try_new("'\\'"), Ok(ExprLit::__new("\\")),);
}