use alloc::borrow::Cow;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum Strip {
#[default]
None,
Start,
End,
Both,
}
impl Strip {
pub fn strip_start(self) -> bool {
matches!(self, Strip::Start | Strip::Both)
}
pub fn strip_end(self) -> bool {
matches!(self, Strip::End | Strip::Both)
}
}
impl From<(bool, bool)> for Strip {
fn from((start, end): (bool, bool)) -> Self {
match (start, end) {
(true, true) => Strip::Both,
(true, false) => Strip::Start,
(false, true) => Strip::End,
(false, false) => Strip::None,
}
}
}
pub fn escape_markers(literal: &str) -> Cow<'_, str> {
if literal.len() < 2 {
return Cow::Borrowed(literal);
}
for (idx, window) in literal.as_bytes().windows(2).enumerate() {
if let b"${" | b"%{" = window {
return Cow::Owned(escape_markers_owned(literal, idx));
}
}
Cow::Borrowed(literal)
}
fn escape_markers_owned(literal: &str, idx: usize) -> String {
let (mut buf, rest) = split_buf(literal, idx);
let mut chars = rest.chars();
while let Some(ch) = chars.next() {
buf.push(ch);
if ch != '$' && ch != '%' {
continue;
}
match chars.next() {
Some(ch2) => {
if ch2 == '{' {
buf.push(ch);
}
buf.push(ch2);
}
None => break,
}
}
buf
}
pub fn unescape_markers(literal: &str) -> Cow<'_, str> {
if literal.len() < 3 {
return Cow::Borrowed(literal);
}
for (idx, window) in literal.as_bytes().windows(3).enumerate() {
if let b"$${" | b"%%{" = window {
return Cow::Owned(unescape_markers_owned(literal, idx));
}
}
Cow::Borrowed(literal)
}
fn unescape_markers_owned(literal: &str, idx: usize) -> String {
let (mut buf, rest) = split_buf(literal, idx);
let mut chars = rest.chars();
while let Some(ch) = chars.next() {
buf.push(ch);
if ch != '$' && ch != '%' {
continue;
}
match (chars.next(), chars.next()) {
(Some(ch2), Some('{')) if ch2 == ch => {
buf.push('{');
}
(Some(ch2), ch3) => {
buf.push(ch2);
if let Some(ch) = ch3 {
buf.push(ch);
}
}
(_, _) => break,
}
}
buf
}
fn split_buf(s: &str, idx: usize) -> (String, &str) {
let mut buf = String::with_capacity(s.len());
buf.push_str(&s[..idx]);
(buf, &s[idx..])
}