use std::hash::{Hash, Hasher};
use std::sync::Arc;
use crate::parser::{RawString, StringEncoding};
#[derive(Debug, Clone, Default)]
pub struct LuaString {
bytes: Arc<[u8]>,
text: Option<Arc<str>>,
encoding: Option<StringEncoding>,
}
impl PartialEq for LuaString {
fn eq(&self, other: &Self) -> bool {
self.bytes == other.bytes
}
}
impl Eq for LuaString {}
impl PartialOrd for LuaString {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for LuaString {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.bytes.cmp(&other.bytes)
}
}
impl Hash for LuaString {
fn hash<H: Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}
impl LuaString {
pub fn from_raw(raw: &RawString) -> Self {
Self {
bytes: raw.bytes.clone(),
text: raw.text.as_ref().map(|text| text.value.clone()),
encoding: raw.text.as_ref().map(|text| text.encoding),
}
}
pub fn from_bytes(bytes: Vec<u8>) -> Self {
Self {
bytes: bytes.into(),
text: None,
encoding: None,
}
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn decoded_text(&self) -> Option<&str> {
self.text.as_deref()
}
pub fn preferred_text(&self) -> Option<&str> {
match self.encoding {
Some(StringEncoding::EncodingRs(enc))
if std::ptr::eq(enc, encoding_rs::WINDOWS_1252) =>
{
None
}
_ => self.decoded_text(),
}
}
pub fn as_utf8(&self) -> Option<&str> {
std::str::from_utf8(&self.bytes).ok()
}
pub fn debug_literal(&self) -> String {
self.preferred_text()
.or_else(|| self.as_utf8())
.map_or_else(
|| {
const HEX: &[u8; 16] = b"0123456789abcdef";
let mut literal = format!("<{} bytes: ", self.bytes.len()).into_bytes();
literal.reserve(self.bytes.len().saturating_mul(3));
if let Some((&first, rest)) = self.bytes.split_first() {
literal.push(HEX[usize::from(first >> 4)]);
literal.push(HEX[usize::from(first & 0x0f)]);
for byte in rest.iter().copied() {
literal.push(b' ');
literal.push(HEX[usize::from(byte >> 4)]);
literal.push(HEX[usize::from(byte & 0x0f)]);
}
}
literal.push(b'>');
String::from_utf8(literal).expect("hex debug literal must be ASCII")
},
|text| format!("{text:?}"),
)
}
}
impl From<&str> for LuaString {
fn from(value: &str) -> Self {
Self {
bytes: value.as_bytes().into(),
text: Some(value.into()),
encoding: Some(StringEncoding::Utf8),
}
}
}
impl From<String> for LuaString {
fn from(value: String) -> Self {
let bytes = Arc::from(value.as_bytes());
Self {
bytes,
text: Some(value.into()),
encoding: Some(StringEncoding::Utf8),
}
}
}