Skip to content

Commit

Permalink
fix(expr): remove unnecessary unescaping
Browse files Browse the repository at this point in the history
  • Loading branch information
martinohmann committed Jul 13, 2023
1 parent c6d0588 commit 8001e5e
Show file tree
Hide file tree
Showing 2 changed files with 1 addition and 85 deletions.
6 changes: 1 addition & 5 deletions crates/hcl-rs/src/expr/template_expr.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::util::try_unescape;
use crate::{Error, Identifier, Result};
use serde::Deserialize;
use std::fmt;
Expand Down Expand Up @@ -51,10 +50,7 @@ impl From<Heredoc> for TemplateExpr {

impl fmt::Display for TemplateExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TemplateExpr::QuotedString(_) => f.write_str(&try_unescape(self.as_str())),
TemplateExpr::Heredoc(_) => f.write_str(self.as_str()),
}
f.write_str(self.as_str())
}
}

Expand Down
80 changes: 0 additions & 80 deletions crates/hcl-rs/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,83 +1,3 @@
use crate::{Error, Result};
use std::borrow::Cow;
use std::str::Chars;

/// Takes in a string with backslash escapes written out with literal backslash characters and
/// converts it to a string with the proper escaped characters.
///
/// ## Errors
///
/// Returns an error if an invalid or incomplete escape sequence or unicode code point is
/// encountered.
pub fn unescape(s: &str) -> Result<Cow<str>> {
for (idx, ch) in s.chars().enumerate() {
if ch == '\\' {
// At least one char needs unescaping so we need to return a new `String` instead of a
// borrowed `&str`.
return unescape_owned(s, idx).map(Cow::Owned);
}
}

Ok(Cow::Borrowed(s))
}

fn unescape_owned(s: &str, idx: usize) -> Result<String> {
let mut buf = String::with_capacity(s.len());

// Put all preceeding chars into buf already.
buf.push_str(&s[..idx]);

let mut chars = s[idx..].chars();
let mut scratch = String::new();

while let Some(ch) = chars.next() {
if ch != '\\' {
buf.push(ch);
continue;
}

let ch = match chars.next() {
Some('b') => '\u{0008}',
Some('f') => '\u{000C}',
Some('n') => '\n',
Some('r') => '\r',
Some('t') => '\t',
Some('\'') => '\'',
Some('\"') => '\"',
Some('\\') => '\\',
Some('u') => match unescape_unicode(&mut chars, &mut scratch) {
Some(ch) => ch,
None => return Err(Error::InvalidUnicodeCodePoint(scratch)),
},
Some(ch) => return Err(Error::InvalidEscape(ch)),
None => return Err(Error::Eof),
};

buf.push(ch);
}

Ok(buf)
}

fn unescape_unicode(chars: &mut Chars<'_>, scratch: &mut String) -> Option<char> {
scratch.clear();

for _ in 0..4 {
scratch.push(chars.next()?);
}

char::from_u32(u32::from_str_radix(scratch, 16).ok()?)
}

/// Like [`unescape`], but returns the original `&str` if it contains invalid escape sequences
/// instead of failing.
pub fn try_unescape(s: &str) -> Cow<str> {
match unescape(s) {
Ok(s) => s,
Err(_) => Cow::Borrowed(s),
}
}

/// Scan `s` for sequences that introduce a template interpolation or directive. Returns `true`
/// once it found one of these start markers, `false` otherwise.
///
Expand Down

0 comments on commit 8001e5e

Please sign in to comment.