feat(tvix/eval): teach builtins.toXML context
XmlEmitter gains a NixContext field, and `write_typed_value` extends it with all context elements present in the passed value. Once all serialization is done, a into_context() function returns the collected context, so we can construct a NixString with context. Tests for this live in tvix-glue, as we use builtins.derivation, which is not present in the tvix-eval crate. Fixes b/398. Change-Id: I85feaaa17b753885f8a017a54e419ec4e602af21 Reviewed-on: https://cl.tvl.fyi/c/depot/+/11704 Tested-by: BuildkiteCI Reviewed-by: flokli <flokli@flokli.de> Autosubmit: flokli <flokli@flokli.de> Reviewed-by: Alyssa Ross <hi@alyssa.is>
This commit is contained in:
parent
a4a313cdd2
commit
ec8d79f3db
4 changed files with 53 additions and 6 deletions
|
@ -1504,8 +1504,19 @@ mod pure_builtins {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buf: Vec<u8> = vec![];
|
let mut buf: Vec<u8> = vec![];
|
||||||
to_xml::value_to_xml(&mut buf, &value)?;
|
let context = to_xml::value_to_xml(&mut buf, &value)?;
|
||||||
Ok(buf.into())
|
|
||||||
|
Ok((
|
||||||
|
buf,
|
||||||
|
// FUTUREWORK: We have a distinction between an empty context, and
|
||||||
|
// no context at all. Fix this.
|
||||||
|
if !context.is_empty() {
|
||||||
|
Some(Box::new(context))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[builtin("placeholder")]
|
#[builtin("placeholder")]
|
||||||
|
|
|
@ -6,11 +6,12 @@ use bstr::ByteSlice;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::{io::Write, rc::Rc};
|
use std::{io::Write, rc::Rc};
|
||||||
|
|
||||||
use crate::{ErrorKind, Value};
|
use crate::{ErrorKind, NixContext, NixContextElement, Value};
|
||||||
|
|
||||||
/// Recursively serialise a value to XML. The value *must* have been
|
/// Recursively serialise a value to XML. The value *must* have been
|
||||||
/// deep-forced before being passed to this function.
|
/// deep-forced before being passed to this function.
|
||||||
pub fn value_to_xml<W: Write>(mut writer: W, value: &Value) -> Result<(), ErrorKind> {
|
/// On success, returns the NixContext.
|
||||||
|
pub fn value_to_xml<W: Write>(mut writer: W, value: &Value) -> Result<NixContext, ErrorKind> {
|
||||||
// Write a literal document declaration, using C++-Nix-style
|
// Write a literal document declaration, using C++-Nix-style
|
||||||
// single quotes.
|
// single quotes.
|
||||||
writeln!(writer, "<?xml version='1.0' encoding='utf-8'?>")?;
|
writeln!(writer, "<?xml version='1.0' encoding='utf-8'?>")?;
|
||||||
|
@ -21,7 +22,7 @@ pub fn value_to_xml<W: Write>(mut writer: W, value: &Value) -> Result<(), ErrorK
|
||||||
value_variant_to_xml(&mut emitter, value)?;
|
value_variant_to_xml(&mut emitter, value)?;
|
||||||
emitter.write_closing_tag("expr")?;
|
emitter.write_closing_tag("expr")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(emitter.into_context())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_typed_value<W: Write, V: ToString>(
|
fn write_typed_value<W: Write, V: ToString>(
|
||||||
|
@ -45,7 +46,12 @@ fn value_variant_to_xml<W: Write>(w: &mut XmlEmitter<W>, value: &Value) -> Resul
|
||||||
Value::Bool(b) => return write_typed_value(w, "bool", b),
|
Value::Bool(b) => return write_typed_value(w, "bool", b),
|
||||||
Value::Integer(i) => return write_typed_value(w, "int", i),
|
Value::Integer(i) => return write_typed_value(w, "int", i),
|
||||||
Value::Float(f) => return write_typed_value(w, "float", f),
|
Value::Float(f) => return write_typed_value(w, "float", f),
|
||||||
Value::String(s) => return write_typed_value(w, "string", s.to_str()?),
|
Value::String(s) => {
|
||||||
|
if let Some(context) = s.context() {
|
||||||
|
w.extend_context(context.iter().cloned());
|
||||||
|
}
|
||||||
|
return write_typed_value(w, "string", s.to_str()?);
|
||||||
|
}
|
||||||
Value::Path(p) => return write_typed_value(w, "path", p.to_string_lossy()),
|
Value::Path(p) => return write_typed_value(w, "path", p.to_string_lossy()),
|
||||||
|
|
||||||
Value::List(list) => {
|
Value::List(list) => {
|
||||||
|
@ -137,6 +143,7 @@ struct XmlEmitter<W> {
|
||||||
/// The current indentation
|
/// The current indentation
|
||||||
cur_indent: usize,
|
cur_indent: usize,
|
||||||
writer: W,
|
writer: W,
|
||||||
|
context: NixContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W: Write> XmlEmitter<W> {
|
impl<W: Write> XmlEmitter<W> {
|
||||||
|
@ -144,6 +151,7 @@ impl<W: Write> XmlEmitter<W> {
|
||||||
XmlEmitter {
|
XmlEmitter {
|
||||||
cur_indent: 0,
|
cur_indent: 0,
|
||||||
writer,
|
writer,
|
||||||
|
context: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +253,19 @@ impl<W: Write> XmlEmitter<W> {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extends the existing context with more context elements.
|
||||||
|
fn extend_context<T>(&mut self, iter: T)
|
||||||
|
where
|
||||||
|
T: IntoIterator<Item = NixContextElement>,
|
||||||
|
{
|
||||||
|
self.context.extend(iter)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes [Self] and returns the [NixContext] collected.
|
||||||
|
fn into_context(self) -> NixContext {
|
||||||
|
self.context
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
[ { "/nix/store/y1s2fiq89v2h9vkb38w508ir20dwv6v2-test.drv" = { allOutputs = true; }; } false ]
|
14
tvix/glue/src/tests/tvix_tests/eval-okay-toxml-context.nix
Normal file
14
tvix/glue/src/tests/tvix_tests/eval-okay-toxml-context.nix
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
[
|
||||||
|
# builtins.toXML retains context where there is.
|
||||||
|
(builtins.getContext (builtins.toXML {
|
||||||
|
inherit (derivation {
|
||||||
|
name = "test";
|
||||||
|
builder = "/bin/sh";
|
||||||
|
system = builtins.currentSystem;
|
||||||
|
}) drvPath;
|
||||||
|
}))
|
||||||
|
|
||||||
|
# this should have no context.
|
||||||
|
(builtins.hasContext
|
||||||
|
(builtins.toXML { }))
|
||||||
|
]
|
Loading…
Reference in a new issue