fix: lazy select_with_default
This commit is contained in:
@@ -249,7 +249,7 @@ export const builtins: any = {
|
||||
|
||||
builtins: createThunk(() => builtins),
|
||||
currentSystem: createThunk(() => {
|
||||
throw new Error("Not implemented: currentSystem");
|
||||
return "x86_64-linux"
|
||||
}),
|
||||
currentTime: createThunk(() => Date.now()),
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
import { force } from "../thunk";
|
||||
import { CatchableError } from "../types";
|
||||
import type { NixBool, NixStrictValue, NixValue, NixString } from "../types";
|
||||
import type { NixBool, NixStrictValue, NixValue } from "../types";
|
||||
import { forceList, forceAttrs, forceFunction } from "../type-assert";
|
||||
import * as context from "./context";
|
||||
|
||||
export const addErrorContext =
|
||||
@@ -138,6 +139,34 @@ export const tryEval = (e: NixValue): { success: NixBool; value: NixStrictValue
|
||||
|
||||
export const zipAttrsWith =
|
||||
(f: NixValue) =>
|
||||
(list: NixValue): never => {
|
||||
throw new Error("Not implemented: zipAttrsWith");
|
||||
(list: NixValue): NixValue => {
|
||||
const listForced = forceList(list);
|
||||
|
||||
// Map to collect all values for each attribute name
|
||||
const attrMap = new Map<string, NixValue[]>();
|
||||
|
||||
// Iterate through each attribute set in the list
|
||||
for (const item of listForced) {
|
||||
const attrs = forceAttrs(force(item) as NixValue);
|
||||
|
||||
// Collect all attribute names and their values
|
||||
for (const [key, value] of Object.entries(attrs)) {
|
||||
if (!attrMap.has(key)) {
|
||||
attrMap.set(key, []);
|
||||
}
|
||||
attrMap.get(key)!.push(value);
|
||||
}
|
||||
}
|
||||
|
||||
// Build the result attribute set
|
||||
const result: Record<string, NixValue> = {};
|
||||
|
||||
for (const [name, values] of attrMap.entries()) {
|
||||
// Apply f to name and values list
|
||||
// f is curried: f name values
|
||||
const fWithName = forceFunction(f)(name);
|
||||
result[name] = forceFunction(fWithName)(values);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -72,15 +72,15 @@ export const select = (obj: NixValue, key: NixValue): NixValue => {
|
||||
*
|
||||
* @param obj - Attribute set to select from
|
||||
* @param key - Key to select
|
||||
* @param default_val - Value to return if key not found
|
||||
* @returns obj[key] if exists, otherwise default_val
|
||||
* @param default_val - Value to return if key not found (will be forced if it's a thunk)
|
||||
* @returns obj[key] if exists, otherwise force(default_val)
|
||||
*/
|
||||
export const selectWithDefault = (obj: NixValue, key: NixValue, default_val: NixValue): NixValue => {
|
||||
const attrs = forceAttrs(obj);
|
||||
const forced_key = forceString(key);
|
||||
|
||||
if (!(forced_key in attrs)) {
|
||||
return default_val;
|
||||
return force(default_val);
|
||||
}
|
||||
|
||||
return attrs[forced_key];
|
||||
|
||||
@@ -8,8 +8,6 @@ mod nar;
|
||||
pub use cache::FetcherCache;
|
||||
pub use download::Downloader;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_core::op2;
|
||||
use serde::Serialize;
|
||||
|
||||
@@ -245,7 +245,8 @@ impl<Ctx: DowngradeContext> Downgrade<Ctx> for ast::Select {
|
||||
let expr = self.expr().unwrap().downgrade(ctx)?;
|
||||
let attrpath = downgrade_attrpath(self.attrpath().unwrap(), ctx)?;
|
||||
let default = if let Some(default) = self.default_expr() {
|
||||
Some(default.downgrade(ctx)?)
|
||||
let default_expr = default.downgrade(ctx)?;
|
||||
Some(ctx.new_expr(Ir::Thunk(default_expr)))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
@@ -99,3 +99,21 @@ fn logical_not() {
|
||||
assert_eq!(eval("!true"), Value::Bool(false));
|
||||
assert_eq!(eval("!false"), Value::Bool(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_with_default_lazy_evaluation() {
|
||||
assert_eq!(eval("{ a = 1; }.a or (1 / 0)"), Value::Int(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_with_default_nested_lazy() {
|
||||
assert_eq!(
|
||||
eval("{ a.b = 42; }.a.b or (builtins.abort \"should not evaluate\")"),
|
||||
Value::Int(42)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn select_with_default_fallback() {
|
||||
assert_eq!(eval("{ a = 1; }.b or 999"), Value::Int(999));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user