diff options
-rw-r--r-- | tvix/eval/src/value/string.rs | 56 |
1 files changed, 46 insertions, 10 deletions
diff --git a/tvix/eval/src/value/string.rs b/tvix/eval/src/value/string.rs index 1937a35870d3..0b665a0a5ec6 100644 --- a/tvix/eval/src/value/string.rs +++ b/tvix/eval/src/value/string.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::{borrow::Cow, fmt::Display}; /// This module implements Nix language strings and their different /// backing implementations. @@ -9,15 +9,6 @@ pub enum NixString { Heap(String), } -impl Display for NixString { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - NixString::Static(s) => f.write_str(s), - NixString::Heap(s) => f.write_str(s), - } - } -} - impl PartialEq for NixString { fn eq(&self, other: &Self) -> bool { self.as_str() == other.as_str() @@ -53,3 +44,48 @@ impl NixString { } } } + +fn nix_escape_char(ch: char) -> Option<&'static str> { + match ch { + '\\' => Some("\\"), + '"' => Some("\\"), + '\n' => Some("\\n"), + _ => None, + } +} + +// Escape a Nix string for display, as the user-visible representation +// is always an escaped string (except for traces). +// +// Note that this does not add the outer pair of surrounding quotes. +fn escape_string(input: &str) -> Cow<str> { + for (i, c) in input.chars().enumerate() { + if let Some(esc) = nix_escape_char(c) { + let mut escaped = String::with_capacity(input.len()); + escaped.push_str(&input[..i]); + escaped.push_str(esc); + + for c in input[i + 1..].chars() { + match nix_escape_char(c) { + Some(esc) => escaped.push_str(esc), + None => escaped.push(c), + } + } + + return Cow::Owned(escaped); + } + } + + Cow::Borrowed(input) +} + +impl Display for NixString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("\"")?; + match self { + NixString::Static(s) => f.write_str(&escape_string(s))?, + NixString::Heap(s) => f.write_str(&escape_string(s))?, + }; + f.write_str("\"") + } +} |