fix: select
This commit is contained in:
@@ -46,44 +46,50 @@ export const resolvePath = (path: NixValue): string => {
|
||||
return Deno.core.ops.op_resolve_path(path_str);
|
||||
};
|
||||
|
||||
/**
|
||||
* Select an attribute from an attribute set
|
||||
* 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);
|
||||
export const select = (obj: NixValue, attrpath: NixValue[]): NixValue => {
|
||||
let attrs = forceAttrs(obj);
|
||||
|
||||
if (!(forced_key in forced_obj)) {
|
||||
throw new Error(`Attribute '${forced_key}' not found`);
|
||||
for (const attr of attrpath.slice(0, -1)) {
|
||||
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];
|
||||
};
|
||||
|
||||
/**
|
||||
* Select an attribute with a default value
|
||||
* 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);
|
||||
export const selectWithDefault = (obj: NixValue, attrpath: NixValue[], default_val: NixValue): NixValue => {
|
||||
let attrs = forceAttrs(obj);
|
||||
|
||||
if (!(forced_key in attrs)) {
|
||||
return force(default_val);
|
||||
for (const attr of attrpath.slice(0, -1)) {
|
||||
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 => {
|
||||
@@ -93,14 +99,14 @@ export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
|
||||
let attrs = obj;
|
||||
|
||||
for (const attr of attrpath.slice(0, -1)) {
|
||||
const cur = attrs[forceString(attr)];
|
||||
const cur = force(attrs[forceString(attr)]);
|
||||
if (!isAttrs(cur)) {
|
||||
return false;
|
||||
}
|
||||
attrs = cur;
|
||||
}
|
||||
|
||||
return true;
|
||||
return forceString(attrpath[attrpath.length - 1]) in attrs;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -225,47 +225,21 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Let {
|
||||
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for Select {
|
||||
fn compile(&self, ctx: &Ctx) -> String {
|
||||
let expr = ctx.get_ir(self.expr).compile(ctx);
|
||||
|
||||
let mut result = expr;
|
||||
let attr_count = self.attrpath.len();
|
||||
|
||||
for (i, attr) in self.attrpath.iter().enumerate() {
|
||||
let is_last = i == attr_count - 1;
|
||||
result = match attr {
|
||||
Attr::Str(sym) => {
|
||||
let key = ctx.get_sym(*sym).escape_quote();
|
||||
if let Some(default) = self.default
|
||||
&& is_last
|
||||
{
|
||||
let default_val = ctx.get_ir(default).compile(ctx);
|
||||
format!(
|
||||
"Nix.selectWithDefault({}, {}, {})",
|
||||
result, key, default_val
|
||||
)
|
||||
let lhs = ctx.get_ir(self.expr).compile(ctx);
|
||||
let attrpath = self
|
||||
.attrpath
|
||||
.iter()
|
||||
.map(|attr| match attr {
|
||||
Attr::Str(sym) => ctx.get_sym(*sym).escape_quote(),
|
||||
Attr::Dynamic(expr_id) => ctx.get_ir(*expr_id).compile(ctx),
|
||||
})
|
||||
.join(",");
|
||||
if let Some(default) = self.default {
|
||||
format!("Nix.selectWithDefault({lhs}, [{attrpath}], {})", ctx.get_ir(default).compile(ctx))
|
||||
} else {
|
||||
format!("Nix.select({}, {})", result, key)
|
||||
format!("Nix.select({lhs}, [{attrpath}])")
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
||||
|
||||
Reference in New Issue
Block a user