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