1use crate::InternalString;
2
3#[derive(PartialEq, Eq, Clone, Hash)]
5pub struct RawString(RawStringInner);
6
7#[derive(PartialEq, Eq, Clone, Hash)]
8enum RawStringInner {
9 Empty,
10 Explicit(InternalString),
11 Spanned(std::ops::Range<usize>),
12}
13
14impl RawString {
15 pub(crate) fn with_span(span: std::ops::Range<usize>) -> Self {
16 if span.start == span.end {
17 RawString(RawStringInner::Empty)
18 } else {
19 RawString(RawStringInner::Spanned(span))
20 }
21 }
22
23 pub fn as_str(&self) -> Option<&str> {
27 match &self.0 {
28 RawStringInner::Empty => Some(""),
29 RawStringInner::Explicit(s) => Some(s.as_str()),
30 RawStringInner::Spanned(_) => None,
31 }
32 }
33
34 pub fn span(&self) -> Option<std::ops::Range<usize>> {
38 match &self.0 {
39 RawStringInner::Empty => None,
40 RawStringInner::Explicit(_) => None,
41 RawStringInner::Spanned(span) => Some(span.clone()),
42 }
43 }
44
45 pub(crate) fn to_str<'s>(&'s self, input: &'s str) -> &'s str {
46 match &self.0 {
47 RawStringInner::Empty => "",
48 RawStringInner::Explicit(s) => s.as_str(),
49 RawStringInner::Spanned(span) => input
50 .get(span.clone())
51 .unwrap_or_else(|| panic!("span {span:?} should be in input:\n```\n{input}\n```")),
52 }
53 }
54
55 pub(crate) fn to_str_with_default<'s>(
56 &'s self,
57 input: Option<&'s str>,
58 default: &'s str,
59 ) -> &'s str {
60 match &self.0 {
61 RawStringInner::Empty => "",
62 RawStringInner::Explicit(s) => s.as_str(),
63 RawStringInner::Spanned(span) => {
64 if let Some(input) = input {
65 input.get(span.clone()).unwrap_or_else(|| {
66 panic!("span {span:?} should be in input:\n```\n{input}\n```")
67 })
68 } else {
69 default
70 }
71 }
72 }
73 }
74
75 pub(crate) fn despan(&mut self, input: &str) {
76 match &self.0 {
77 RawStringInner::Empty => {}
78 RawStringInner::Explicit(_) => {}
79 RawStringInner::Spanned(span) => {
80 *self = Self::from(input.get(span.clone()).unwrap_or_else(|| {
81 panic!("span {span:?} should be in input:\n```\n{input}\n```")
82 }));
83 }
84 }
85 }
86
87 #[cfg(feature = "display")]
88 pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
89 let raw = self.to_str(input);
90 for part in raw.split('\r') {
91 write!(buf, "{part}")?;
92 }
93 Ok(())
94 }
95
96 #[cfg(feature = "display")]
97 pub(crate) fn encode_with_default(
98 &self,
99 buf: &mut dyn std::fmt::Write,
100 input: Option<&str>,
101 default: &str,
102 ) -> std::fmt::Result {
103 let raw = self.to_str_with_default(input, default);
104 for part in raw.split('\r') {
105 write!(buf, "{part}")?;
106 }
107 Ok(())
108 }
109}
110
111impl Default for RawString {
112 fn default() -> Self {
113 Self(RawStringInner::Empty)
114 }
115}
116
117impl std::fmt::Debug for RawString {
118 #[inline]
119 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
120 match &self.0 {
121 RawStringInner::Empty => write!(formatter, "empty"),
122 RawStringInner::Explicit(s) => write!(formatter, "{s:?}"),
123 RawStringInner::Spanned(s) => write!(formatter, "{s:?}"),
124 }
125 }
126}
127
128impl From<&str> for RawString {
129 #[inline]
130 fn from(s: &str) -> Self {
131 if s.is_empty() {
132 Self(RawStringInner::Empty)
133 } else {
134 InternalString::from(s).into()
135 }
136 }
137}
138
139impl From<String> for RawString {
140 #[inline]
141 fn from(s: String) -> Self {
142 if s.is_empty() {
143 Self(RawStringInner::Empty)
144 } else {
145 InternalString::from(s).into()
146 }
147 }
148}
149
150impl From<&String> for RawString {
151 #[inline]
152 fn from(s: &String) -> Self {
153 if s.is_empty() {
154 Self(RawStringInner::Empty)
155 } else {
156 InternalString::from(s).into()
157 }
158 }
159}
160
161impl From<InternalString> for RawString {
162 #[inline]
163 fn from(inner: InternalString) -> Self {
164 Self(RawStringInner::Explicit(inner))
165 }
166}
167
168impl From<&InternalString> for RawString {
169 #[inline]
170 fn from(s: &InternalString) -> Self {
171 if s.is_empty() {
172 Self(RawStringInner::Empty)
173 } else {
174 InternalString::from(s).into()
175 }
176 }
177}
178
179impl From<Box<str>> for RawString {
180 #[inline]
181 fn from(s: Box<str>) -> Self {
182 if s.is_empty() {
183 Self(RawStringInner::Empty)
184 } else {
185 InternalString::from(s).into()
186 }
187 }
188}