about summary refs log tree commit diff
path: root/tvix/eval/src/value/string.rs
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2022-08-10T15·51+0300
committertazjin <tazjin@tvl.su>2022-08-24T21·23+0000
commit4c9aad17add413a387bf927438de8831c00065c5 (patch)
tree5004546f6a9b5f0723e48647570ba5c716dfe991 /tvix/eval/src/value/string.rs
parenta2b4b4a48521d1751fa9c88cec980f0b4b59427d (diff)
fix(tvix/value): add escaping logic for Nix strings r/4459
Nix strings displayed to users must be escaped the same way as they
are in C++ Nix. This adds the scaffolding for escapes, but is most
likely not yet complete.

Change-Id: Icfdcb2ac98d292c567ba894a92b6529a53e0cc17
Reviewed-on: https://cl.tvl.fyi/c/depot/+/6124
Tested-by: BuildkiteCI
Reviewed-by: grfn <grfn@gws.fyi>
Diffstat (limited to 'tvix/eval/src/value/string.rs')
-rw-r--r--tvix/eval/src/value/string.rs56
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("\"")
+    }
+}