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 pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
84 let raw = self.to_str(input);
85 for part in raw.split('\r') {
86 write!(buf, "{}", part)?;
87 }
88 Ok(())
89 }
90
91 pub(crate) fn encode_with_default(
92 &self,
93 buf: &mut dyn std::fmt::Write,
94 input: Option<&str>,
95 default: &str,
96 ) -> std::fmt::Result {
97 let raw = self.to_str_with_default(input, default);
98 for part in raw.split('\r') {
99 write!(buf, "{}", part)?;
100 }
101 Ok(())
102 }
103}
104
105impl Default for RawString {
106 fn default() -> Self {
107 Self(RawStringInner::Empty)
108 }
109}
110
111impl std::fmt::Debug for RawString {
112 #[inline]
113 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
114 match &self.0 {
115 RawStringInner::Empty => write!(formatter, "empty"),
116 RawStringInner::Explicit(s: &InternalString) => write!(formatter, "{:?}", s),
117 RawStringInner::Spanned(s: &Range) => write!(formatter, "{:?}", s),
118 }
119 }
120}
121
122impl From<&str> for RawString {
123 #[inline]
124 fn from(s: &str) -> Self {
125 if s.is_empty() {
126 Self(RawStringInner::Empty)
127 } else {
128 InternalString::from(s).into()
129 }
130 }
131}
132
133impl From<String> for RawString {
134 #[inline]
135 fn from(s: String) -> Self {
136 if s.is_empty() {
137 Self(RawStringInner::Empty)
138 } else {
139 InternalString::from(s).into()
140 }
141 }
142}
143
144impl From<&String> for RawString {
145 #[inline]
146 fn from(s: &String) -> Self {
147 if s.is_empty() {
148 Self(RawStringInner::Empty)
149 } else {
150 InternalString::from(s).into()
151 }
152 }
153}
154
155impl From<InternalString> for RawString {
156 #[inline]
157 fn from(inner: InternalString) -> Self {
158 Self(RawStringInner::Explicit(inner))
159 }
160}
161
162impl From<&InternalString> for RawString {
163 #[inline]
164 fn from(s: &InternalString) -> Self {
165 if s.is_empty() {
166 Self(RawStringInner::Empty)
167 } else {
168 InternalString::from(s).into()
169 }
170 }
171}
172
173impl From<Box<str>> for RawString {
174 #[inline]
175 fn from(s: Box<str>) -> Self {
176 if s.is_empty() {
177 Self(RawStringInner::Empty)
178 } else {
179 InternalString::from(s).into()
180 }
181 }
182}
183