use std::ops::Range;
use super::brackets::{install, same_header, Block};
use super::Traced;
use crate::commands::variables::DialStringTarget;
pub(crate) fn quote_bare_after(target: DialStringTarget, passes: u32) -> String {
let consumed_by_carrier = if target
.carrier()
.expands()
{
2
} else {
1
};
let run = (1usize << passes) - consumed_by_carrier;
format!("{}'", "\\".repeat(run))
}
pub(crate) fn protects_dollars(value: &str, target: DialStringTarget) -> bool {
target
.carrier()
.expands()
&& value.contains("$$")
&& !names_a_variable(value)
}
#[cfg(test)]
mod c_oracle;
pub(crate) fn expand_escapes(text: &[Traced]) -> (Vec<Traced>, Vec<Range<usize>>) {
let has_escaped_data = text
.windows(2)
.any(|w| w[0].0 == '\\' && matches!(w[1].0, '\\' | 'n' | 's' | 't' | '\''));
if !has_escaped_data
&& !var_check_const(
text.iter()
.map(|&(c, ..)| c),
)
{
return (text.to_vec(), Vec::new());
}
let at = |i: usize| {
text.get(i)
.map(|&(c, ..)| c)
};
let mut out = Vec::with_capacity(text.len());
let mut references = Vec::new();
let mut p = 0;
while let Some(c) = at(p) {
match (c, at(p + 1)) {
('\\', Some('$')) => {
p += 1;
if at(p + 1) == Some('$') {
p += 1;
}
}
('\\', Some('\'')) => {
p += 2;
continue;
}
('\\', Some('\\')) => {
out.push((c, text[p].1, text[p + 1].2));
p += 2;
continue;
}
('$', _) => {
let reference = p;
if at(p + 1) == Some('$') {
p += 1;
}
if at(p + 1) == Some('{') {
p = reference_end(text, p + 2);
let written = out.len();
out.extend_from_slice(&text[reference..p]);
references.push(written..out.len());
if at(p).is_some_and(|next| next != '$') {
out.push(text[p]);
p += 1;
}
continue;
}
}
_ => {}
}
out.push(text[p]);
p += 1;
}
(out, references)
}
fn reference_end(text: &[Traced], start: usize) -> usize {
let mut depth = 1usize;
for (e, &(c, ..)) in text
.iter()
.enumerate()
.skip(start)
{
if depth == 1 && c == '}' {
return e + 1;
}
if c == '{' && e != start {
depth += 1;
} else if depth > 1 && c == '}' {
depth -= 1;
}
}
text.len()
}
pub(crate) fn names_a_variable(value: &str) -> bool {
var_check_const(value.chars())
}
fn var_check_const(chars: impl IntoIterator<Item = char>) -> bool {
let mut dollar = false;
for c in chars {
if c == '$' {
dollar = true;
} else if dollar && c == '{' {
return true;
} else if c != '\\' {
dollar = false;
}
}
false
}
pub(crate) fn enterprise_nests<'a>(
blocks: impl IntoIterator<Item = &'a Block>,
switch_true: impl Fn(&str) -> bool,
) -> bool {
install(blocks)
.into_iter()
.find(|(name, _)| same_header(name, "origination_nested_vars"))
.is_some_and(|(_, value)| switch_true(value))
}