hcl_edit/
raw_string.rs

1use crate::encode::EncodeState;
2use hcl_primitives::InternalString;
3use std::borrow::Cow;
4use std::fmt::Write;
5use std::ops::{self, Range};
6
7/// Opaque string storage for raw HCL.
8#[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    /// Returns the `RawString` as a `&str`.
35    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}