-
Notifications
You must be signed in to change notification settings - Fork 33
/
string.rs
114 lines (98 loc) · 2.93 KB
/
string.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use super::{CloneIn, IntoObject};
use crate::core::gc::{Block, GcManaged, GcMark};
use anyhow::Result;
use bstr::{BStr, BString, ByteSlice};
use rune_macros::Trace;
use std::{
fmt::{Debug, Display},
ops::Deref,
};
#[derive(PartialEq, Eq, Trace)]
pub(crate) struct LispString {
gc: GcMark,
#[no_trace]
string: StrType,
}
unsafe impl Sync for LispString {}
#[derive(Debug, PartialEq, Eq)]
enum StrType {
String(String),
BString(BString),
}
impl LispString {
pub(crate) fn get_char_at(&self, idx: usize) -> Option<u32> {
match &self.string {
StrType::String(s) => s.chars().nth(idx).map(|c| c.into()),
StrType::BString(s) => s.iter().nth(idx).map(|b| (*b).into()),
}
}
pub(crate) fn len(&self) -> usize {
match &self.string {
StrType::String(s) => s.chars().count(),
StrType::BString(s) => s.len(),
}
}
pub(crate) unsafe fn from_string(value: String) -> Self {
Self { gc: GcMark::default(), string: StrType::String(value) }
}
pub(crate) unsafe fn from_bstring(value: Vec<u8>) -> Self {
Self { gc: GcMark::default(), string: StrType::BString(BString::from(value)) }
}
pub(crate) fn is_valid_unicode(&self) -> bool {
matches!(&self.string, StrType::String(_))
}
}
impl<'new> CloneIn<'new, &'new Self> for LispString {
fn clone_in<const C: bool>(&self, bk: &'new Block<C>) -> super::Gc<&'new Self> {
match &self.string {
StrType::String(s) => s.clone().into_obj(bk),
StrType::BString(s) => s.as_bytes().to_vec().into_obj(bk),
}
}
}
impl GcManaged for LispString {
fn get_mark(&self) -> &GcMark {
&self.gc
}
}
impl Deref for LispString {
type Target = BStr;
fn deref(&self) -> &Self::Target {
match &self.string {
StrType::String(s) => BStr::new(s),
StrType::BString(s) => s.as_ref(),
}
}
}
impl Display for LispString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.string {
StrType::String(s) => write!(f, "{s}"),
StrType::BString(s) => {
let bytes: &[u8] = s.as_ref();
for byte in bytes {
if byte.is_ascii() {
write!(f, "{}", *byte as char)?;
} else {
write!(f, "\\{:03o}", byte)?;
}
}
Ok(())
}
}
}
}
impl Debug for LispString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self}")
}
}
impl<'a> TryFrom<&'a LispString> for &'a str {
type Error = anyhow::Error;
fn try_from(value: &'a LispString) -> Result<Self, Self::Error> {
match &value.string {
StrType::String(s) => Ok(s),
StrType::BString(s) => Ok(s.try_into()?),
}
}
}