1use crate::encode::EncodeState;
2use hcl_primitives::InternalString;
3use std::borrow::Cow;
4use std::fmt::Write;
5use std::ops::{self, Range};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct RawString(RawStringInner);
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12enum RawStringInner {
13 Empty,
14 Spanned(Range<usize>),
15 Explicit(InternalString),
16}
17
18impl RawString {
19 pub(crate) fn from_span(span: Range<usize>) -> Self {
20 if span.is_empty() {
21 RawString(RawStringInner::Empty)
22 } else {
23 RawString(RawStringInner::Spanned(span))
24 }
25 }
26
27 pub(crate) fn span(&self) -> Option<Range<usize>> {
28 match &self.0 {
29 RawStringInner::Empty | RawStringInner::Explicit(_) => None,
30 RawStringInner::Spanned(span) => Some(span.clone()),
31 }
32 }
33
34 pub(crate) fn as_str(&self) -> &str {
36 match &self.0 {
37 RawStringInner::Empty | RawStringInner::Spanned(_) => "",
38 RawStringInner::Explicit(s) => s.as_str(),
39 }
40 }
41
42 pub(crate) fn encode_with_default(
43 &self,
44 buf: &mut EncodeState,
45 default: &str,
46 ) -> std::fmt::Result {
47 if let RawStringInner::Spanned(_) = self.0 {
48 buf.write_str(default)
49 } else {
50 buf.write_str(self.as_str())
51 }
52 }
53
54 pub(crate) fn despan(&mut self, input: &str) {
55 match &self.0 {
56 RawStringInner::Empty | RawStringInner::Explicit(_) => {}
57 RawStringInner::Spanned(span) => {
58 *self = RawString::from(input.get(span.clone()).unwrap_or_else(|| {
59 panic!("span {span:?} should be in input:\n```\n{input}\n```")
60 }));
61 }
62 }
63 }
64}
65
66impl Default for RawString {
67 fn default() -> Self {
68 RawString(RawStringInner::Empty)
69 }
70}
71
72impl ops::Deref for RawString {
73 type Target = str;
74
75 #[inline]
76 fn deref(&self) -> &Self::Target {
77 self.as_str()
78 }
79}
80
81impl From<&str> for RawString {
82 #[inline]
83 fn from(s: &str) -> Self {
84 if s.is_empty() {
85 RawString(RawStringInner::Empty)
86 } else {
87 RawString::from(InternalString::from(s))
88 }
89 }
90}
91
92impl<'a> From<Cow<'a, str>> for RawString {
93 #[inline]
94 fn from(s: Cow<'a, str>) -> Self {
95 if s.is_empty() {
96 RawString(RawStringInner::Empty)
97 } else {
98 RawString::from(InternalString::from(s))
99 }
100 }
101}
102
103impl From<String> for RawString {
104 #[inline]
105 fn from(s: String) -> Self {
106 if s.is_empty() {
107 RawString(RawStringInner::Empty)
108 } else {
109 RawString::from(InternalString::from(s))
110 }
111 }
112}
113
114impl From<InternalString> for RawString {
115 #[inline]
116 fn from(inner: InternalString) -> Self {
117 RawString(RawStringInner::Explicit(inner))
118 }
119}
120
121impl<'a> From<RawString> for Cow<'a, str> {
122 #[inline]
123 fn from(s: RawString) -> Self {
124 match s.0 {
125 RawStringInner::Empty | RawStringInner::Spanned(_) => Cow::Borrowed(""),
126 RawStringInner::Explicit(s) => s.into(),
127 }
128 }
129}
130
131impl<'a> From<&'a RawString> for Cow<'a, str> {
132 #[inline]
133 fn from(s: &'a RawString) -> Self {
134 match &s.0 {
135 RawStringInner::Empty | RawStringInner::Spanned(_) => Cow::Borrowed(""),
136 RawStringInner::Explicit(s) => s.into(),
137 }
138 }
139}