fix: select

This commit is contained in:
2026-01-13 16:22:12 +08:00
parent 6cd87aa653
commit b4e0b53cde
2 changed files with 51 additions and 71 deletions

View File

@@ -46,44 +46,50 @@ export const resolvePath = (path: NixValue): string => {
return Deno.core.ops.op_resolve_path(path_str); return Deno.core.ops.op_resolve_path(path_str);
}; };
/** export const select = (obj: NixValue, attrpath: NixValue[]): NixValue => {
* Select an attribute from an attribute set let attrs = forceAttrs(obj);
* Used by codegen for attribute access (e.g., obj.key)
*
* @param obj - Attribute set to select from
* @param key - Key to select
* @returns The value at obj[key]
* @throws Error if obj is null/undefined or key not found
*/
export const select = (obj: NixValue, key: NixValue): NixValue => {
const forced_obj = forceAttrs(obj);
const forced_key = forceString(key);
if (!(forced_key in forced_obj)) { for (const attr of attrpath.slice(0, -1)) {
throw new Error(`Attribute '${forced_key}' not found`); const key = forceString(attr)
if (!(key in attrs)) {
throw new Error(`Attribute '${key}' not found`);
}
const cur = force(attrs[forceString(attr)]);
if (!isAttrs(cur)) {
// throw new Error(`Attribute '${forced_key}' not found`);
// FIXME: error
throw new Error(`Attribute not found`);
}
attrs = cur;
} }
return forced_obj[forced_key]; const last = forceString(attrpath[attrpath.length - 1])
if (!(last in attrs)) {
throw new Error(`Attribute '${last}' not found`);
}
return attrs[last];
}; };
/** export const selectWithDefault = (obj: NixValue, attrpath: NixValue[], default_val: NixValue): NixValue => {
* Select an attribute with a default value let attrs = forceAttrs(obj);
* Used for Nix's `obj.key or default` syntax
*
* @param obj - Attribute set to select from
* @param key - Key to select
* @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)) { for (const attr of attrpath.slice(0, -1)) {
return force(default_val); const key = forceString(attr)
if (!(key in attrs)) {
return default_val
}
const cur = force(attrs[key]);
if (!isAttrs(cur)) {
return default_val;
}
attrs = cur;
} }
return attrs[forced_key]; const last = forceString(attrpath[attrpath.length - 1]);
if (last in attrs) {
return attrs[last]
}
return default_val;
}; };
export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => { export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
@@ -93,14 +99,14 @@ export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
let attrs = obj; let attrs = obj;
for (const attr of attrpath.slice(0, -1)) { for (const attr of attrpath.slice(0, -1)) {
const cur = attrs[forceString(attr)]; const cur = force(attrs[forceString(attr)]);
if (!isAttrs(cur)) { if (!isAttrs(cur)) {
return false; return false;
} }
attrs = cur; attrs = cur;
} }
return true; return forceString(attrpath[attrpath.length - 1]) in attrs;
}; };
/** /**

View File

@@ -225,46 +225,20 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Let {
impl<Ctx: CodegenContext> Compile<Ctx> for Select { impl<Ctx: CodegenContext> Compile<Ctx> for Select {
fn compile(&self, ctx: &Ctx) -> String { fn compile(&self, ctx: &Ctx) -> String {
let expr = ctx.get_ir(self.expr).compile(ctx); let lhs = ctx.get_ir(self.expr).compile(ctx);
let attrpath = self
let mut result = expr; .attrpath
let attr_count = self.attrpath.len(); .iter()
.map(|attr| match attr {
for (i, attr) in self.attrpath.iter().enumerate() { Attr::Str(sym) => ctx.get_sym(*sym).escape_quote(),
let is_last = i == attr_count - 1; Attr::Dynamic(expr_id) => ctx.get_ir(*expr_id).compile(ctx),
result = match attr { })
Attr::Str(sym) => { .join(",");
let key = ctx.get_sym(*sym).escape_quote(); if let Some(default) = self.default {
if let Some(default) = self.default format!("Nix.selectWithDefault({lhs}, [{attrpath}], {})", ctx.get_ir(default).compile(ctx))
&& is_last } else {
{ format!("Nix.select({lhs}, [{attrpath}])")
let default_val = ctx.get_ir(default).compile(ctx);
format!(
"Nix.selectWithDefault({}, {}, {})",
result, key, default_val
)
} else {
format!("Nix.select({}, {})", result, key)
}
}
Attr::Dynamic(expr_id) => {
let key = ctx.get_ir(*expr_id).compile(ctx);
if let Some(default) = self.default
&& is_last
{
let default_val = ctx.get_ir(default).compile(ctx);
format!(
"Nix.selectWithDefault({}, {}, {})",
result, key, default_val
)
} else {
format!("Nix.select({}, {})", result, key)
}
}
};
} }
result
} }
} }