git.delta.rocks / jrsonnet / refs/commits / 38c2f665c6fe

difftreelog

source

crates/jrsonnet-ir/src/unescape.rs1.5 KiBsourcehistory
1use std::str::Chars;23fn decode_unicode(chars: &mut Chars) -> Option<u16> {4	IntoIterator::into_iter([chars.next()?, chars.next()?, chars.next()?, chars.next()?])5		.map(|c| c.to_digit(16).map(|f| f as u16))6		.try_fold(0u16, |acc, v| Some((acc << 4) | (v?)))7}89pub fn unescape(s: &str) -> Option<String> {10	let mut chars = s.chars();11	let mut out = String::with_capacity(s.len());1213	while let Some(c) = chars.next() {14		if c != '\\' {15			out.push(c);16			continue;17		}18		match chars.next()? {19			c @ ('\\' | '"' | '\'') => out.push(c),20			'b' => out.push('\u{0008}'),21			'f' => out.push('\u{000c}'),22			'n' => out.push('\n'),23			'r' => out.push('\r'),24			't' => out.push('\t'),25			'u' => match decode_unicode(&mut chars)? {26				// May only be second byte27				0xDC00..=0xDFFF => return None,28				// Surrogate pair29				n1 @ 0xD800..=0xDBFF => {30					if chars.next() != Some('\\') {31						return None;32					}33					if chars.next() != Some('u') {34						return None;35					}36					let n2 = decode_unicode(&mut chars)?;37					if !matches!(n2, 0xDC00..=0xDFFF) {38						return None;39					}40					let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000;41					out.push(char::from_u32(n)?);42				}43				n => out.push(char::from_u32(n as u32)?),44			},45			'x' => {46				let c = IntoIterator::into_iter([chars.next()?, chars.next()?])47					.map(|c| c.to_digit(16))48					.try_fold(0u32, |acc, v| Some((acc << 8) | (v?)))?;49				out.push(char::from_u32(c)?)50			}51			_ => return None,52		}53	}54	Some(out)55}