feat(tvix/eval): Implement builtins.sort
This is a bit tricky because the comparator can throw errors, so we need to propagate them out if they exist and try to avoid sorting forever by returning a reasonable ordering in this case (as short-circuiting is not available). Co-Authored-By: Vincent Ambo <tazjin@tvl.su> Change-Id: Icae1d30f43ec1ae64b2ba51e73ee467605686792 Reviewed-on: https://cl.tvl.fyi/c/depot/+/7072 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
This commit is contained in:
parent
d0a836b0e1
commit
3412ae4956
4 changed files with 46 additions and 3 deletions
|
@ -628,10 +628,47 @@ fn pure_builtins() -> Vec<Builtin> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Builtin::new("seq", &[true, true], |mut args: Vec<Value>, _: &mut VM| {
|
Builtin::new("seq", &[true, true], |mut args: Vec<Value>, _: &mut VM| {
|
||||||
// The builtin calling infra has already forced both args for us, so we just return the
|
// The builtin calling infra has already forced both args for us, so
|
||||||
// second and ignore the first
|
// we just return the second and ignore the first
|
||||||
Ok(args.pop().unwrap())
|
Ok(args.pop().unwrap())
|
||||||
}),
|
}),
|
||||||
|
Builtin::new("sort", &[true, true], |args: Vec<Value>, vm: &mut VM| {
|
||||||
|
let mut list = args[1].to_list()?;
|
||||||
|
let comparator = &args[0];
|
||||||
|
|
||||||
|
// Used to let errors "escape" from the sorting closure. If anything
|
||||||
|
// ends up setting an error, it is returned from this function.
|
||||||
|
let mut error: Option<ErrorKind> = None;
|
||||||
|
|
||||||
|
list.sort_by(|lhs, rhs| {
|
||||||
|
let result = vm
|
||||||
|
.call_with(comparator, [lhs.clone(), rhs.clone()])
|
||||||
|
.map_err(|err| ErrorKind::ThunkForce(Box::new(err)))
|
||||||
|
.and_then(|v| v.force(vm)?.as_bool());
|
||||||
|
|
||||||
|
match (&error, result) {
|
||||||
|
// The contained closure only returns a "less
|
||||||
|
// than?"-boolean, no way to yield "equal".
|
||||||
|
(None, Ok(true)) => Ordering::Less,
|
||||||
|
(None, Ok(false)) => Ordering::Greater,
|
||||||
|
|
||||||
|
// Closest thing to short-circuiting out if an error was
|
||||||
|
// thrown.
|
||||||
|
(Some(_), _) => Ordering::Equal,
|
||||||
|
|
||||||
|
// Propagate the error if one was encountered.
|
||||||
|
(_, Err(e)) => {
|
||||||
|
error = Some(e);
|
||||||
|
Ordering::Equal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
match error {
|
||||||
|
None => Ok(Value::List(list)),
|
||||||
|
Some(e) => Err(e),
|
||||||
|
}
|
||||||
|
}),
|
||||||
Builtin::new("splitVersion", &[true], |args: Vec<Value>, _: &mut VM| {
|
Builtin::new("splitVersion", &[true], |args: Vec<Value>, _: &mut VM| {
|
||||||
let s = args[0].to_str()?;
|
let s = args[0].to_str()?;
|
||||||
let s = VersionPartsIter::new(s.as_str());
|
let s = VersionPartsIter::new(s.as_str());
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//! This module implements Nix lists.
|
//! This module implements Nix lists.
|
||||||
use std::ops::Deref;
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use crate::errors::ErrorKind;
|
use crate::errors::ErrorKind;
|
||||||
use crate::vm::VM;
|
use crate::vm::VM;
|
||||||
|
@ -135,3 +135,9 @@ impl Deref for NixList {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DerefMut for NixList {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue