use std::fmt::Display;
use std::fmt::Formatter;
use std::iter::Peekable;
use std::str::FromStr;
pub fn ident_needs_quote(ident: &str) -> bool {
if ident.is_empty() {
return true;
}
if ident == "~" {
return false;
}
let mut chars = ident.chars();
let first = chars.next().unwrap();
if !first.is_ascii_alphabetic() && first != '_' {
return true;
}
for c in chars {
if !c.is_ascii_alphanumeric() && c != '_' && c != '$' {
return true;
}
}
false
}
pub struct QuotedIdent<T: AsRef<str>>(pub T, pub char);
impl FromStr for QuotedIdent<String> {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut chars = s.chars();
let quote = chars.next().ok_or(())?;
if chars.next_back().ok_or(())? != quote {
return Err(());
}
let mut output = String::new();
while let Some(c) = chars.next() {
if c == quote && chars.next() != Some(quote) {
return Err(());
}
output.push(c);
}
Ok(QuotedIdent(output, quote))
}
}
impl<T: AsRef<str>> Display for QuotedIdent<T> {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let QuotedIdent(ident, quote) = self;
let ident = ident.as_ref();
write!(f, "{}", quote)?;
for c in ident.chars() {
if c == *quote {
write!(f, "{}", quote)?;
}
write!(f, "{}", c)?;
}
write!(f, "{}", quote)
}
}
pub struct QuotedString<T: AsRef<str>>(pub T, pub char);
impl FromStr for QuotedString<String> {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut chars = s.chars();
let quote = chars.next().ok_or(())?;
if chars.next_back().ok_or(())? != quote {
return Err(());
}
let mut chars = chars.peekable();
let mut output = String::new();
while let Some(c) = chars.next() {
if c == '\\' {
match chars.next() {
Some('b') => output.push('\u{0008}'),
Some('f') => output.push('\u{000C}'),
Some('n') => output.push('\n'),
Some('r') => output.push('\r'),
Some('t') => output.push('\t'),
Some('\'') => output.push('\''),
Some('"') => output.push('"'),
Some('\\') => output.push('\\'),
Some('u') => output.push(unescape_unicode(&mut chars).ok_or(())?),
Some('x') => output.push(unescape_byte(&mut chars).ok_or(())?),
Some(c) if c.is_digit(8) => output.push(unescape_octal(c, &mut chars)),
Some(c) => {
output.push('\\');
output.push(c);
}
None => {
output.push('\\');
}
};
} else if c == quote {
if chars.next() != Some(quote) {
return Err(());
}
output.push(quote);
} else {
output.push(c);
}
}
Ok(QuotedString(output, quote))
}
}
impl<T: AsRef<str>> Display for QuotedString<T> {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let QuotedString(s, quote) = self;
let s = s.as_ref();
write!(f, "{}", quote)?;
for c in s.chars() {
match c {
'\t' => write!(f, "\\t")?,
'\r' => write!(f, "\\r")?,
'\n' => write!(f, "\\n")?,
'\\' => write!(f, "\\\\")?,
'\x00'..='\x1F' => write!(f, "\\x{:02x}", c as u8)?,
c if c == *quote => write!(f, "\\{}", quote)?,
_ => write!(f, "{}", c)?,
}
}
write!(f, "{}", quote)
}
}
fn unescape_unicode(chars: &mut Peekable<impl Iterator<Item = char>>) -> Option<char> {
let mut code = 0;
for c in chars.take(4) {
code = code * 16 + c.to_digit(16)?;
}
char::from_u32(code)
}
fn unescape_byte(chars: &mut Peekable<impl Iterator<Item = char>>) -> Option<char> {
let mut byte = 0;
for c in chars.take(2) {
byte = byte * 16 + c.to_digit(16)?;
}
char::from_u32(byte)
}
fn unescape_octal(c1: char, chars: &mut Peekable<impl Iterator<Item = char>>) -> char {
let mut oct = c1.to_digit(8).unwrap();
while let Some(c) = chars.peek() {
if let Some(digit) = c.to_digit(8) {
oct = oct * 8 + digit;
chars.next();
} else {
break;
}
}
char::from_u32(oct).unwrap()
}
pub struct AtString<T: AsRef<str>>(pub T);
impl FromStr for AtString<String> {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut chars = s.chars();
if chars.next() != Some('@') {
return Err(());
}
let mut output = String::new();
while let Some(c) = chars.next() {
if c == '\\' {
match chars.next() {
Some(' ') => output.push(' '),
Some('\t') => output.push('\t'),
Some('\'') => output.push('\''),
Some('\"') => output.push('\"'),
Some('\\') => output.push('\\'),
Some(c) => {
output.push('\\');
output.push(c);
}
None => {
output.push('\\');
}
}
} else {
output.push(c);
}
}
Ok(AtString(output))
}
}
impl<T: AsRef<str>> Display for AtString<T> {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let AtString(s) = self;
let s = s.as_ref();
write!(f, "@")?;
for c in s.chars() {
match c {
' ' => write!(f, "\\ ")?,
'\t' => write!(f, "\\t")?,
'\'' => write!(f, "\\'")?,
'"' => write!(f, "\\\"")?,
'\\' => write!(f, "\\\\")?,
c => write!(f, "{}", c)?,
}
}
Ok(())
}
}
pub struct EscapedString<T: AsRef<str>>(pub T, pub &'static [u8]);
impl<T: AsRef<str>> Display for EscapedString<T> {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
let EscapedString(s, chars) = self;
let s = s.as_ref();
for c in s.chars() {
if chars.iter().any(|&x| x as char == c) {
write!(f, "%{:02x}", c as u8)?;
} else {
write!(f, "{}", c)?;
}
}
Ok(())
}
}