| Crates.io | descape |
| lib.rs | descape |
| version | 3.0.0 |
| created_at | 2024-02-26 18:59:37.892957+00 |
| updated_at | 2025-08-17 18:05:15.359465+00 |
| description | Adds a single extension trait for &str to unescape any backslashes. |
| homepage | |
| repository | https://github.com/balt-dev/descape |
| max_upload_size | |
| id | 1154030 |
| size | 39,395 |
Provides utilities for easily parsing escape sequences in a string, using alloc::borrow::Cow to only borrow when needed.
This library supports many escape sequences:
\\a -> \x07\\b -> \x08\\t -> \x09\\n -> \x0A\\v -> \x0B\\f -> \x0C\\r -> \x0D\\e -> \x1B\\' -> '\\" -> "\\` -> `\\\\ -> \\\\xNN -> \xNN\\o -> \o, for all octal digits o\\oo -> \oo, for all octal digits o\\ooo -> \ooo, for all octal digits o\\uXXXX -> \u{XXXX}\\u{HEX} -> \u{HEX}Along with this, you can define your own custom escape handlers!
Custom escape handlers support multi-character escape results, skipping past escape sequences, and even custom escape prefixes!
See descape::EscapeHandler.
This crate supports no-std.
Optionally, this crate has the std and core_error features,
to allow the error type of an invalid escape to implement the Error trait.
std uses std::error::Error, and core_error depends on core::error::Error, which is stable on Rust 1.82.0 or greater.
let escaped = "Hello,\\nworld!".to_unescaped();
assert_eq!(
escaped.unwrap(),
Cow::Owned::<'_, str>("Hello,\nworld!".to_string())
);
let no_escapes = "No escapes here!".to_unescaped();
assert_eq!(
no_escapes.unwrap(),
Cow::Borrowed("No escapes here!")
);
// v invalid at index 7
let invalid_escape = r"Uh oh! \xJJ".to_unescaped();
assert_eq!(
invalid_escape.unwrap_err().index,
7
);
fn raw(idx: usize, chr: char, _: &mut CharIndices) -> Result<EscapeValue<'_>, ()> {
Ok(chr.into())
}
let escaped = r"\H\e\l\l\o \n \W\o\r\l\d";
let unescaped = escaped.to_unescaped_with(raw).expect("this is fine");
assert_eq!(unescaped, "Hello n World");
fn raw(idx: usize, chr: char, _: &mut CharIndices) -> Result<EscapeValue<'_>, ()> {
Ok(EscapeValue::Remove)
}
let escaped = r"What if I want a \nnewline?";
let unescaped = escaped.to_unescaped_with(raw).expect("this should work");
assert_eq!(unescaped, "What if I want a newline?");
fn rust_only(idx: usize, chr: char, iter: &mut CharIndices) -> Result<EscapeValue<'_>, ()> {
match chr {
'a' | 'b' | 'v' | 'f' | 'e' | '`' => Err(()),
_ => descape::DefaultEscapeHandler.escape(idx, chr, iter)
}
}
r"This is \nfine".to_unescaped_with(rust_only).expect(r"\n is valid");
r"This is not \fine".to_unescaped_with(rust_only).expect_err(r"\f is invalid");
struct PercentEscape;
impl EscapeHandler for PercentEscape {
fn prefix(&self) -> char { '%' };
fn escape<'iter, 'source>(&mut self, idx: usize, chr: char, iter: &'iter mut CharIndices<'source>) -> Result<EscapeValue<'source>, ()> {
descape::DefaultEscapeHandler.escape(idx, chr, iter)
}
}
assert_eq!(
r"Hello,%tworld!".to_unescaped_with(PercentEscape).unwrap(),
"Hello,\tworld!"
)