Files
nix-js/nix-js/runtime-ts/src/helpers.ts
2026-01-10 22:04:23 +08:00

120 lines
3.2 KiB
TypeScript

/**
* Helper functions for nix-js runtime
*/
import type { NixValue, NixAttrs, NixBool } from "./types";
import { forceAttrs, forceString } from "./type-assert";
import { isAttrs } from "./builtins/type-check";
/**
* Resolve a path (handles both absolute and relative paths)
* For relative paths, resolves against current import stack
*
* @param path - Path string (may be relative or absolute)
* @returns Absolute path string
*/
export const resolvePath = (path: NixValue): string => {
const path_str = forceString(path);
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);
if (!(forced_key in forced_obj)) {
throw new Error(`Attribute '${forced_key}' not found`);
}
return forced_obj[forced_key];
};
/**
* 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
* @returns obj[key] if exists, otherwise 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 attrs[forced_key];
};
export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
if (!isAttrs(obj)) {
return false;
}
let attrs = obj;
for (const attr of attrpath.slice(0, -1)) {
const cur = attrs[forceString(attr)];
if (!isAttrs(cur)) {
return false;
}
attrs = cur;
}
return true;
};
/**
* Validate function parameters
* Used for pattern matching in function parameters
*
* Example: { a, b ? 1, ... }: ...
* - required: ["a"]
* - allowed: ["a", "b"] (or null if ellipsis "..." present)
*
* @param arg - Argument object to validate
* @param required - Array of required parameter names (or null)
* @param allowed - Array of allowed parameter names (or null for ellipsis)
* @returns The forced argument object
* @throws Error if required param missing or unexpected param present
*/
export const validateParams = (
arg: NixValue,
required: string[] | null,
allowed: string[] | null,
): NixAttrs => {
const forced_arg = forceAttrs(arg);
// Check required parameters
if (required) {
for (const key of required) {
if (!Object.hasOwn(forced_arg, key)) {
throw new Error(`Function called without required argument '${key}'`);
}
}
}
// Check allowed parameters (if not using ellipsis)
if (allowed) {
const allowed_set = new Set(allowed);
for (const key in forced_arg) {
if (!allowed_set.has(key)) {
throw new Error(`Function called with unexpected argument '${key}'`);
}
}
}
return forced_arg;
};