1use crate::InternalString;
2
3/// Opaque string storage for raw TOML; internal to `toml_edit`
4#[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 /// Access the underlying string
24 pub fn as_str(&self) -> Option<&str> {
25 match &self.0 {
26 RawStringInner::Empty => Some(""),
27 RawStringInner::Explicit(s) => Some(s.as_str()),
28 RawStringInner::Spanned(_) => None,
29 }
30 }
31
32 pub(crate) fn to_str<'s>(&'s self, input: &'s str) -> &'s str {
33 match &self.0 {
34 RawStringInner::Empty => "",
35 RawStringInner::Explicit(s) => s.as_str(),
36 RawStringInner::Spanned(span) => input.get(span.clone()).unwrap_or_else(|| {
37 panic!("span {:?} should be in input:\n```\n{}\n```", span, input)
38 }),
39 }
40 }
41
42 pub(crate) fn to_str_with_default<'s>(
43 &'s self,
44 input: Option<&'s str>,
45 default: &'s str,
46 ) -> &'s str {
47 match &self.0 {
48 RawStringInner::Empty => "",
49 RawStringInner::Explicit(s) => s.as_str(),
50 RawStringInner::Spanned(span) => {
51 if let Some(input) = input {
52 input.get(span.clone()).unwrap_or_else(|| {
53 panic!("span {:?} should be in input:\n```\n{}\n```", span, input)
54 })
55 } else {
56 default
57 }
58 }
59 }
60 }
61
62 /// Access the underlying span
63 pub(crate) fn span(&self) -> Option<std::ops::Range<usize>> {
64 match &self.0 {
65 RawStringInner::Empty => None,
66 RawStringInner::Explicit(_) => None,
67 RawStringInner::Spanned(span) => Some(span.clone()),
68 }
69 }
70
71 pub(crate) fn despan(&mut self, input: &str) {
72 match &self.0 {
73 RawStringInner::Empty => {}
74 RawStringInner::Explicit(_) => {}
75 RawStringInner::Spanned(span) => {
76 *self = Self::from(input.get(span.clone()).unwrap_or_else(|| {
77 panic!("span {:?} should be in input:\n```\n{}\n```", span, input)
78 }))
79 }
80 }
81 }
82
83 #[cfg(feature = "display")]
84 pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
85 let raw = self.to_str(input);
86 for part in raw.split('\r') {
87 write!(buf, "{}", part)?;
88 }
89 Ok(())
90 }
91
92 #[cfg(feature = "display")]
93 pub(crate) fn encode_with_default(
94 &self,
95 buf: &mut dyn std::fmt::Write,
96 input: Option<&str>,
97 default: &str,
98 ) -> std::fmt::Result {
99 let raw = self.to_str_with_default(input, default);
100 for part in raw.split('\r') {
101 write!(buf, "{}", part)?;
102 }
103 Ok(())
104 }
105}
106
107impl Default for RawString {
108 fn default() -> Self {
109 Self(RawStringInner::Empty)
110 }
111}
112
113impl std::fmt::Debug for RawString {
114 #[inline]
115 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
116 match &self.0 {
117 RawStringInner::Empty => write!(formatter, "empty"),
118 RawStringInner::Explicit(s: &InternalString) => write!(formatter, "{:?}", s),
119 RawStringInner::Spanned(s: &Range) => write!(formatter, "{:?}", s),
120 }
121 }
122}
123
124impl From<&str> for RawString {
125 #[inline]
126 fn from(s: &str) -> Self {
127 if s.is_empty() {
128 Self(RawStringInner::Empty)
129 } else {
130 InternalString::from(s).into()
131 }
132 }
133}
134
135impl From<String> for RawString {
136 #[inline]
137 fn from(s: String) -> Self {
138 if s.is_empty() {
139 Self(RawStringInner::Empty)
140 } else {
141 InternalString::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 InternalString::from(s).into()
153 }
154 }
155}
156
157impl From<InternalString> for RawString {
158 #[inline]
159 fn from(inner: InternalString) -> Self {
160 Self(RawStringInner::Explicit(inner))
161 }
162}
163
164impl From<&InternalString> for RawString {
165 #[inline]
166 fn from(s: &InternalString) -> Self {
167 if s.is_empty() {
168 Self(RawStringInner::Empty)
169 } else {
170 InternalString::from(s).into()
171 }
172 }
173}
174
175impl From<Box<str>> for RawString {
176 #[inline]
177 fn from(s: Box<str>) -> Self {
178 if s.is_empty() {
179 Self(RawStringInner::Empty)
180 } else {
181 InternalString::from(s).into()
182 }
183 }
184}
185