From 4c9aad17add413a387bf927438de8831c00065c5 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 10 Aug 2022 18:51:45 +0300 Subject: fix(tvix/value): add escaping logic for Nix strings 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 --- tvix/eval/src/value/string.rs | 56 +++++++++++++++++++++++++++++++++++-------- 1 file 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 { + 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("\"") + } +} -- cgit 1.4.1