blob: 86b70c3e6c74df468439788f6b70bb06a4658e54 [file] [log] [blame]
use crate::InternalString;
/// Opaque string storage for raw TOML; internal to `toml_edit`
#[derive(PartialEq, Eq, Clone, Hash)]
pub struct RawString(RawStringInner);
#[derive(PartialEq, Eq, Clone, Hash)]
enum RawStringInner {
Empty,
Explicit(InternalString),
Spanned(std::ops::Range<usize>),
}
impl RawString {
pub(crate) fn with_span(span: std::ops::Range<usize>) -> Self {
if span.start == span.end {
RawString(RawStringInner::Empty)
} else {
RawString(RawStringInner::Spanned(span))
}
}
/// Access the underlying string
///
/// This generally requires a [`DocumentMut`][crate::DocumentMut].
pub fn as_str(&self) -> Option<&str> {
match &self.0 {
RawStringInner::Empty => Some(""),
RawStringInner::Explicit(s) => Some(s.as_str()),
RawStringInner::Spanned(_) => None,
}
}
/// Access the underlying span
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
match &self.0 {
RawStringInner::Empty => None,
RawStringInner::Explicit(_) => None,
RawStringInner::Spanned(span) => Some(span.clone()),
}
}
pub(crate) fn to_str<'s>(&'s self, input: &'s str) -> &'s str {
match &self.0 {
RawStringInner::Empty => "",
RawStringInner::Explicit(s) => s.as_str(),
RawStringInner::Spanned(span) => input.get(span.clone()).unwrap_or_else(|| {
panic!("span {:?} should be in input:\n```\n{}\n```", span, input)
}),
}
}
pub(crate) fn to_str_with_default<'s>(
&'s self,
input: Option<&'s str>,
default: &'s str,
) -> &'s str {
match &self.0 {
RawStringInner::Empty => "",
RawStringInner::Explicit(s) => s.as_str(),
RawStringInner::Spanned(span) => {
if let Some(input) = input {
input.get(span.clone()).unwrap_or_else(|| {
panic!("span {:?} should be in input:\n```\n{}\n```", span, input)
})
} else {
default
}
}
}
}
pub(crate) fn despan(&mut self, input: &str) {
match &self.0 {
RawStringInner::Empty => {}
RawStringInner::Explicit(_) => {}
RawStringInner::Spanned(span) => {
*self = Self::from(input.get(span.clone()).unwrap_or_else(|| {
panic!("span {:?} should be in input:\n```\n{}\n```", span, input)
}))
}
}
}
#[cfg(feature = "display")]
pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
let raw = self.to_str(input);
for part in raw.split('\r') {
write!(buf, "{}", part)?;
}
Ok(())
}
#[cfg(feature = "display")]
pub(crate) fn encode_with_default(
&self,
buf: &mut dyn std::fmt::Write,
input: Option<&str>,
default: &str,
) -> std::fmt::Result {
let raw = self.to_str_with_default(input, default);
for part in raw.split('\r') {
write!(buf, "{}", part)?;
}
Ok(())
}
}
impl Default for RawString {
fn default() -> Self {
Self(RawStringInner::Empty)
}
}
impl std::fmt::Debug for RawString {
#[inline]
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match &self.0 {
RawStringInner::Empty => write!(formatter, "empty"),
RawStringInner::Explicit(s) => write!(formatter, "{:?}", s),
RawStringInner::Spanned(s) => write!(formatter, "{:?}", s),
}
}
}
impl From<&str> for RawString {
#[inline]
fn from(s: &str) -> Self {
if s.is_empty() {
Self(RawStringInner::Empty)
} else {
InternalString::from(s).into()
}
}
}
impl From<String> for RawString {
#[inline]
fn from(s: String) -> Self {
if s.is_empty() {
Self(RawStringInner::Empty)
} else {
InternalString::from(s).into()
}
}
}
impl From<&String> for RawString {
#[inline]
fn from(s: &String) -> Self {
if s.is_empty() {
Self(RawStringInner::Empty)
} else {
InternalString::from(s).into()
}
}
}
impl From<InternalString> for RawString {
#[inline]
fn from(inner: InternalString) -> Self {
Self(RawStringInner::Explicit(inner))
}
}
impl From<&InternalString> for RawString {
#[inline]
fn from(s: &InternalString) -> Self {
if s.is_empty() {
Self(RawStringInner::Empty)
} else {
InternalString::from(s).into()
}
}
}
impl From<Box<str>> for RawString {
#[inline]
fn from(s: Box<str>) -> Self {
if s.is_empty() {
Self(RawStringInner::Empty)
} else {
InternalString::from(s).into()
}
}
}