120 lines
3.2 KiB
TypeScript
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;
|
|
};
|