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