optimize: use Map to represent NixAttrs
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"check": "tsc --noEmit && npx eslint && biome check",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"build": "node build.mjs",
|
||||
"dev": "npm run typecheck && npm run build"
|
||||
},
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { mkPos } from "../helpers";
|
||||
import { mkPos, select } from "../helpers";
|
||||
import { createThunk } from "../thunk";
|
||||
import { forceAttrs, forceFunction, forceList, forceStringValue } from "../type-assert";
|
||||
import { ATTR_POSITIONS, type NixAttrs, type NixList, type NixValue } from "../types";
|
||||
|
||||
export const attrNames = (set: NixValue): string[] => Object.keys(forceAttrs(set)).sort();
|
||||
export const attrNames = (set: NixValue): string[] => Array.from(forceAttrs(set).keys()).sort();
|
||||
|
||||
export const attrValues = (set: NixValue): NixValue[] =>
|
||||
Object.entries(forceAttrs(set))
|
||||
Array.from(forceAttrs(set).entries())
|
||||
.sort(([a], [b]) => {
|
||||
if (a < b) {
|
||||
return -1;
|
||||
@@ -21,21 +21,24 @@ export const attrValues = (set: NixValue): NixValue[] =>
|
||||
export const getAttr =
|
||||
(s: NixValue) =>
|
||||
(set: NixValue): NixValue =>
|
||||
forceAttrs(set)[forceStringValue(s)];
|
||||
select(forceAttrs(set), [s]);
|
||||
|
||||
export const hasAttr =
|
||||
(s: NixValue) =>
|
||||
(set: NixValue): boolean =>
|
||||
Object.hasOwn(forceAttrs(set), forceStringValue(s));
|
||||
forceAttrs(set).has(forceStringValue(s));
|
||||
|
||||
export const mapAttrs =
|
||||
(f: NixValue) =>
|
||||
(attrs: NixValue): NixAttrs => {
|
||||
const forcedAttrs = forceAttrs(attrs);
|
||||
const forcedF = forceFunction(f);
|
||||
const newAttrs: NixAttrs = {};
|
||||
for (const key in forcedAttrs) {
|
||||
newAttrs[key] = createThunk(() => forceFunction(forcedF(key))(forcedAttrs[key]), "created by mapAttrs");
|
||||
const newAttrs: NixAttrs = new Map();
|
||||
for (const [key, val] of forcedAttrs) {
|
||||
newAttrs.set(
|
||||
key,
|
||||
createThunk(() => forceFunction(forcedF(key))(val), "created by mapAttrs"),
|
||||
);
|
||||
}
|
||||
return newAttrs;
|
||||
};
|
||||
@@ -43,25 +46,20 @@ export const mapAttrs =
|
||||
export const removeAttrs =
|
||||
(attrs: NixValue) =>
|
||||
(list: NixValue): NixAttrs => {
|
||||
const newAttrs: NixAttrs = {};
|
||||
const forcedAttrs = forceAttrs(attrs);
|
||||
const newAttrs: NixAttrs = new Map(forceAttrs(attrs));
|
||||
const forcedList = forceList(list);
|
||||
const keysToRemove = new Set(forcedList.map(forceStringValue));
|
||||
|
||||
for (const key in forcedAttrs) {
|
||||
if (!keysToRemove.has(key)) {
|
||||
newAttrs[key] = forcedAttrs[key];
|
||||
}
|
||||
for (const item of forcedList) {
|
||||
newAttrs.delete(forceStringValue(item));
|
||||
}
|
||||
return newAttrs;
|
||||
};
|
||||
|
||||
export const listToAttrs = (e: NixValue): NixAttrs => {
|
||||
const attrs: NixAttrs = {};
|
||||
const attrs: NixAttrs = new Map();
|
||||
const forcedE = [...forceList(e)].reverse();
|
||||
for (const obj of forcedE) {
|
||||
const item = forceAttrs(obj);
|
||||
attrs[forceStringValue(item.name)] = item.value;
|
||||
attrs.set(forceStringValue(select(item, ["name"])), select(item, ["value"]));
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
@@ -71,21 +69,17 @@ export const intersectAttrs =
|
||||
(e2: NixValue): NixAttrs => {
|
||||
const f1 = forceAttrs(e1);
|
||||
const f2 = forceAttrs(e2);
|
||||
const attrs: NixAttrs = {};
|
||||
const k1 = Object.keys(f1);
|
||||
const k2 = Object.keys(f2);
|
||||
if (k1.length < k2.length) {
|
||||
for (let i = 0; i < k1.length; i++) {
|
||||
const key = k1[i];
|
||||
if (key in f2) {
|
||||
attrs[key] = f2[key];
|
||||
const attrs: NixAttrs = new Map();
|
||||
if (f1.size < f2.size) {
|
||||
for (const [key] of f1) {
|
||||
if (f2.has(key)) {
|
||||
attrs.set(key, f2.get(key) as NixValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < k2.length; i++) {
|
||||
const key = k2[i];
|
||||
if (key in f1) {
|
||||
attrs[key] = f2[key];
|
||||
for (const [key] of f2) {
|
||||
if (f1.has(key)) {
|
||||
attrs.set(key, f2.get(key) as NixValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,20 +91,20 @@ export const catAttrs =
|
||||
(list: NixValue): NixList => {
|
||||
const key = forceStringValue(attr);
|
||||
return forceList(list)
|
||||
.map((set) => forceAttrs(set)[key])
|
||||
.filter((val) => val !== undefined);
|
||||
.map((set) => forceAttrs(set).get(key))
|
||||
.filter((val) => val !== undefined) as NixList;
|
||||
};
|
||||
|
||||
export const groupBy =
|
||||
(f: NixValue) =>
|
||||
(list: NixValue): NixAttrs => {
|
||||
const attrs: NixAttrs = {};
|
||||
const attrs: NixAttrs = new Map();
|
||||
const forcedF = forceFunction(f);
|
||||
const forcedList = forceList(list);
|
||||
for (const elem of forcedList) {
|
||||
const key = forceStringValue(forcedF(elem));
|
||||
if (!attrs[key]) attrs[key] = [];
|
||||
(attrs[key] as NixList).push(elem);
|
||||
if (!attrs.has(key)) attrs.set(key, []);
|
||||
(attrs.get(key) as NixList).push(elem);
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
@@ -125,7 +119,7 @@ export const zipAttrsWith =
|
||||
for (const item of listForced) {
|
||||
const attrs = forceAttrs(item);
|
||||
|
||||
for (const [key, value] of Object.entries(attrs)) {
|
||||
for (const [key, value] of attrs) {
|
||||
if (!attrMap.has(key)) {
|
||||
attrMap.set(key, []);
|
||||
}
|
||||
@@ -133,10 +127,13 @@ export const zipAttrsWith =
|
||||
}
|
||||
}
|
||||
|
||||
const result: Record<string, NixValue> = {};
|
||||
const result: NixAttrs = new Map();
|
||||
|
||||
for (const [name, values] of attrMap.entries()) {
|
||||
result[name] = createThunk(() => forceFunction(forceFunction(f)(name))(values));
|
||||
result.set(
|
||||
name,
|
||||
createThunk(() => forceFunction(forceFunction(f)(name))(values)),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -148,7 +145,7 @@ export const unsafeGetAttrPos =
|
||||
const name = forceStringValue(attrName);
|
||||
const attrs = forceAttrs(attrSet);
|
||||
|
||||
if (!(name in attrs)) {
|
||||
if (!attrs.has(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,20 +113,20 @@ export const getContext = (value: NixValue): NixAttrs => {
|
||||
const context = getStringContext(s);
|
||||
|
||||
const infoMap = parseContextToInfoMap(context);
|
||||
const result: NixAttrs = {};
|
||||
const result: NixAttrs = new Map();
|
||||
|
||||
for (const [path, info] of infoMap) {
|
||||
const attrs: NixAttrs = {};
|
||||
const attrs: NixAttrs = new Map();
|
||||
if (info.path) {
|
||||
attrs.path = true;
|
||||
attrs.set("path", true);
|
||||
}
|
||||
if (info.allOutputs) {
|
||||
attrs.allOutputs = true;
|
||||
attrs.set("allOutputs", true);
|
||||
}
|
||||
if (info.outputs.length > 0) {
|
||||
attrs.outputs = info.outputs;
|
||||
attrs.set("outputs", info.outputs);
|
||||
}
|
||||
result[path] = attrs;
|
||||
result.set(path, attrs);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -154,22 +154,22 @@ export const appendContext =
|
||||
const ctxAttrs = forceAttrs(ctxValue);
|
||||
const newContext: NixStringContext = new Set(existingContext);
|
||||
|
||||
for (const [path, infoVal] of Object.entries(ctxAttrs)) {
|
||||
for (const [path, infoVal] of ctxAttrs) {
|
||||
if (!path.startsWith("/nix/store/")) {
|
||||
throw new Error(`context key '${path}' is not a store path`);
|
||||
}
|
||||
|
||||
const info = forceAttrs(infoVal);
|
||||
const info = forceAttrs(infoVal as NixValue);
|
||||
|
||||
if ("path" in info) {
|
||||
const pathVal = force(info.path);
|
||||
if (info.has("path")) {
|
||||
const pathVal = force(info.get("path") as NixValue);
|
||||
if (pathVal === true) {
|
||||
newContext.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
if ("allOutputs" in info) {
|
||||
const allOutputs = force(info.allOutputs);
|
||||
if (info.has("allOutputs")) {
|
||||
const allOutputs = force(info.get("allOutputs") as NixValue);
|
||||
if (allOutputs === true) {
|
||||
if (!path.endsWith(".drv")) {
|
||||
throw new Error(
|
||||
@@ -180,8 +180,8 @@ export const appendContext =
|
||||
}
|
||||
}
|
||||
|
||||
if ("outputs" in info) {
|
||||
const outputs = forceList(info.outputs);
|
||||
if (info.has("outputs")) {
|
||||
const outputs = forceList(info.get("outputs") as NixValue);
|
||||
if (outputs.length > 0 && !path.endsWith(".drv")) {
|
||||
throw new Error(
|
||||
`tried to add derivation output context of ${path}, which is not a derivation, to a string`,
|
||||
|
||||
@@ -118,37 +118,31 @@ export const coerceToString = (
|
||||
}
|
||||
|
||||
if (typeof v === "object" && v !== null && !Array.isArray(v)) {
|
||||
// First, try the __toString method if present
|
||||
// This allows custom types to define their own string representation
|
||||
if ("__toString" in v) {
|
||||
// Force the method in case it's a thunk
|
||||
const toStringMethod = forceFunction(v.__toString);
|
||||
const result = force(toStringMethod(v));
|
||||
// Recursively coerceToString
|
||||
return coerceToString(result, mode, copyToStore, outContext);
|
||||
}
|
||||
|
||||
// If no __toString, try outPath (used for derivations and store paths)
|
||||
// This allows derivation objects like { outPath = "/nix/store/..."; } to be coerced
|
||||
if ("outPath" in v) {
|
||||
// Recursively coerce the outPath value
|
||||
const outPath = coerceToString(v.outPath, mode, copyToStore, outContext);
|
||||
if ("type" in v && v.type === "derivation" && "drvPath" in v && outContext) {
|
||||
const drvPathValue = force(v.drvPath);
|
||||
const drvPathStr = isStringWithContext(drvPathValue)
|
||||
? drvPathValue.value
|
||||
: typeof drvPathValue === "string"
|
||||
? drvPathValue
|
||||
: null;
|
||||
if (drvPathStr) {
|
||||
const outputName = "outputName" in v ? String(force(v.outputName)) : "out";
|
||||
addBuiltContext(outContext, drvPathStr, outputName);
|
||||
}
|
||||
if (v instanceof Map) {
|
||||
if (v.has("__toString")) {
|
||||
const toStringMethod = forceFunction(v.get("__toString") as NixValue);
|
||||
const result = force(toStringMethod(v));
|
||||
return coerceToString(result, mode, copyToStore, outContext);
|
||||
}
|
||||
|
||||
if (v.has("outPath")) {
|
||||
const outPath = coerceToString(v.get("outPath") as NixValue, mode, copyToStore, outContext);
|
||||
if (v.has("type") && v.get("type") === "derivation" && v.has("drvPath") && outContext) {
|
||||
const drvPathValue = force(v.get("drvPath") as NixValue);
|
||||
const drvPathStr = isStringWithContext(drvPathValue)
|
||||
? drvPathValue.value
|
||||
: typeof drvPathValue === "string"
|
||||
? drvPathValue
|
||||
: null;
|
||||
if (drvPathStr) {
|
||||
const outputName = v.has("outputName") ? String(force(v.get("outputName") as NixValue)) : "out";
|
||||
addBuiltContext(outContext, drvPathStr, outputName);
|
||||
}
|
||||
}
|
||||
return outPath;
|
||||
}
|
||||
return outPath;
|
||||
}
|
||||
|
||||
// Attribute sets without __toString or outPath cannot be coerced
|
||||
throw new TypeError(`cannot coerce ${typeOf(v)} to a string`);
|
||||
}
|
||||
|
||||
@@ -259,8 +253,8 @@ export const coerceToPath = (value: NixValue, outContext: NixStringContext): str
|
||||
if (isPath(forced)) {
|
||||
return forced.value;
|
||||
}
|
||||
if (isAttrs(forced) && Object.hasOwn(forced, "__toString")) {
|
||||
const toStringFunc = forceFunction(forced.__toString);
|
||||
if (isAttrs(forced) && forced.has("__toString")) {
|
||||
const toStringFunc = forceFunction(forced.get("__toString") as NixValue);
|
||||
return coerceToPath(toStringFunc(forced), outContext);
|
||||
}
|
||||
|
||||
@@ -339,30 +333,33 @@ export const nixValueToJson = (
|
||||
return v.map((item) => nixValueToJson(item, strict, outContext, copyToStore, seen));
|
||||
}
|
||||
|
||||
// NixAttrs
|
||||
if ("__toString" in v && typeof force(v.__toString) === "function") {
|
||||
const toStringMethod = force(v.__toString) as (self: typeof v) => NixValue;
|
||||
const result = force(toStringMethod(v));
|
||||
if (typeof result === "string") {
|
||||
return result;
|
||||
}
|
||||
if (isStringWithContext(result)) {
|
||||
for (const elem of result.context) {
|
||||
outContext.add(elem);
|
||||
if (v instanceof Map) {
|
||||
if (v.has("__toString") && typeof force(v.get("__toString") as NixValue) === "function") {
|
||||
const toStringMethod = force(v.get("__toString") as NixValue) as (self: typeof v) => NixValue;
|
||||
const result = force(toStringMethod(v));
|
||||
if (typeof result === "string") {
|
||||
return result;
|
||||
}
|
||||
return result.value;
|
||||
if (isStringWithContext(result)) {
|
||||
for (const elem of result.context) {
|
||||
outContext.add(elem);
|
||||
}
|
||||
return result.value;
|
||||
}
|
||||
return nixValueToJson(result, strict, outContext, copyToStore, seen);
|
||||
}
|
||||
return nixValueToJson(result, strict, outContext, copyToStore, seen);
|
||||
|
||||
if (v.has("outPath")) {
|
||||
return nixValueToJson(v.get("outPath") as NixValue, strict, outContext, copyToStore, seen);
|
||||
}
|
||||
|
||||
const result: Record<string, unknown> = {};
|
||||
const keys = Array.from(v.keys()).sort();
|
||||
for (const key of keys) {
|
||||
result[key] = nixValueToJson(v.get(key) as NixValue, strict, outContext, copyToStore, seen);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if ("outPath" in v) {
|
||||
return nixValueToJson(v.outPath, strict, outContext, copyToStore, seen);
|
||||
}
|
||||
|
||||
const result: Record<string, unknown> = {};
|
||||
const keys = Object.keys(v).sort();
|
||||
for (const key of keys) {
|
||||
result[key] = nixValueToJson(v[key], strict, outContext, copyToStore, seen);
|
||||
}
|
||||
return result;
|
||||
throw new Error(`cannot convert ${typeof v} to JSON`);
|
||||
};
|
||||
|
||||
@@ -116,10 +116,10 @@ export const generateAtermModulo = (drv: DerivationData, inputDrvHashes: Map<str
|
||||
return `Derive([${outputs}],[${inputDrvs}],[${inputSrcs}],${quoteString(drv.platform)},${escapeString(drv.builder)},[${args}],[${envs}])`;
|
||||
};
|
||||
const validateName = (attrs: NixAttrs): string => {
|
||||
if (!("name" in attrs)) {
|
||||
if (!attrs.has("name")) {
|
||||
throw new Error("derivation: missing required attribute 'name'");
|
||||
}
|
||||
const name = forceStringValue(attrs.name);
|
||||
const name = forceStringValue(attrs.get("name") as NixValue);
|
||||
if (!name) {
|
||||
throw new Error("derivation: 'name' cannot be empty");
|
||||
}
|
||||
@@ -130,17 +130,17 @@ const validateName = (attrs: NixAttrs): string => {
|
||||
};
|
||||
|
||||
const validateBuilder = (attrs: NixAttrs, outContext: NixStringContext): string => {
|
||||
if (!("builder" in attrs)) {
|
||||
if (!attrs.has("builder")) {
|
||||
throw new Error("derivation: missing required attribute 'builder'");
|
||||
}
|
||||
return coerceToString(attrs.builder, StringCoercionMode.ToString, true, outContext);
|
||||
return coerceToString(attrs.get("builder") as NixValue, StringCoercionMode.ToString, true, outContext);
|
||||
};
|
||||
|
||||
const validateSystem = (attrs: NixAttrs): string => {
|
||||
if (!("system" in attrs)) {
|
||||
if (!attrs.has("system")) {
|
||||
throw new Error("derivation: missing required attribute 'system'");
|
||||
}
|
||||
return forceStringValue(attrs.system);
|
||||
return forceStringValue(attrs.get("system") as NixValue);
|
||||
};
|
||||
|
||||
const validateOutputs = (outputs: string[]): void => {
|
||||
@@ -162,17 +162,25 @@ const validateOutputs = (outputs: string[]): void => {
|
||||
};
|
||||
|
||||
const extractOutputs = (attrs: NixAttrs, structuredAttrs: boolean): string[] => {
|
||||
if (!("outputs" in attrs)) {
|
||||
if (!attrs.has("outputs")) {
|
||||
return ["out"];
|
||||
}
|
||||
|
||||
let outputs: string[];
|
||||
if (structuredAttrs) {
|
||||
const outputsList = forceList(attrs.outputs);
|
||||
const outputsList = forceList(attrs.get("outputs") as NixValue);
|
||||
outputs = outputsList.map((o) => forceStringValue(o));
|
||||
} else {
|
||||
const outputsStr = coerceToString(attrs.outputs, StringCoercionMode.ToString, false, new Set());
|
||||
outputs = outputsStr.split(/\s+/).filter((s) => s.length > 0);
|
||||
const outputsStr = coerceToString(
|
||||
attrs.get("outputs") as NixValue,
|
||||
StringCoercionMode.ToString,
|
||||
false,
|
||||
new Set(),
|
||||
);
|
||||
outputs = outputsStr
|
||||
.trim()
|
||||
.split(/\s+/)
|
||||
.filter((s) => s.length > 0);
|
||||
}
|
||||
|
||||
validateOutputs(outputs);
|
||||
@@ -180,10 +188,10 @@ const extractOutputs = (attrs: NixAttrs, structuredAttrs: boolean): string[] =>
|
||||
};
|
||||
|
||||
const extractArgs = (attrs: NixAttrs, outContext: NixStringContext): string[] => {
|
||||
if (!("args" in attrs)) {
|
||||
if (!attrs.has("args")) {
|
||||
return [];
|
||||
}
|
||||
const argsList = forceList(attrs.args);
|
||||
const argsList = forceList(attrs.get("args") as NixValue);
|
||||
return argsList.map((a) => coerceToString(a, StringCoercionMode.ToString, true, outContext));
|
||||
};
|
||||
|
||||
@@ -217,13 +225,13 @@ const extractEnv = (
|
||||
|
||||
if (structuredAttrs) {
|
||||
const jsonAttrs: Record<string, unknown> = {};
|
||||
for (const [key, value] of Object.entries(attrs)) {
|
||||
for (const [key, value] of attrs) {
|
||||
if (!structuredAttrsExcludedKeys.has(key)) {
|
||||
const forcedValue = force(value);
|
||||
const forcedValue = force(value as NixValue);
|
||||
if (ignoreNulls && forcedValue === null) {
|
||||
continue;
|
||||
}
|
||||
jsonAttrs[key] = nixValueToJson(value, true, outContext, true);
|
||||
jsonAttrs[key] = nixValueToJson(value as NixValue, true, outContext, true);
|
||||
}
|
||||
|
||||
if (key === "allowedReferences") {
|
||||
@@ -271,13 +279,13 @@ const extractEnv = (
|
||||
}
|
||||
env.set("__json", sortedJsonStringify(jsonAttrs));
|
||||
} else {
|
||||
for (const [key, value] of Object.entries(attrs)) {
|
||||
for (const [key, value] of attrs) {
|
||||
if (!specialAttrs.has(key)) {
|
||||
const forcedValue = force(value);
|
||||
const forcedValue = force(value as NixValue);
|
||||
if (ignoreNulls && forcedValue === null) {
|
||||
continue;
|
||||
}
|
||||
env.set(key, coerceToString(value, StringCoercionMode.ToString, true, outContext));
|
||||
env.set(key, coerceToString(value as NixValue, StringCoercionMode.ToString, true, outContext));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,29 +300,29 @@ interface FixedOutputInfo {
|
||||
}
|
||||
|
||||
const extractFixedOutputInfo = (attrs: NixAttrs, ignoreNulls: boolean): FixedOutputInfo | null => {
|
||||
if (!("outputHash" in attrs)) {
|
||||
if (!attrs.has("outputHash")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hashValue = force(attrs.outputHash);
|
||||
const hashValue = force(attrs.get("outputHash") as NixValue);
|
||||
if (ignoreNulls && hashValue === null) {
|
||||
return null;
|
||||
}
|
||||
const hashRaw = forceStringNoCtx(attrs.outputHash);
|
||||
const hashRaw = forceStringNoCtx(hashValue);
|
||||
|
||||
let hashAlgo = null;
|
||||
if ("outputHashAlgo" in attrs) {
|
||||
const algoValue = force(attrs.outputHashAlgo);
|
||||
if (attrs.has("outputHashAlgo")) {
|
||||
const algoValue = force(attrs.get("outputHashAlgo") as NixValue);
|
||||
if (!(ignoreNulls && algoValue === null)) {
|
||||
hashAlgo = forceStringNoCtx(attrs.outputHashAlgo);
|
||||
hashAlgo = forceStringNoCtx(algoValue);
|
||||
}
|
||||
}
|
||||
|
||||
let hashMode = "flat";
|
||||
if ("outputHashMode" in attrs) {
|
||||
const modeValue = force(attrs.outputHashMode);
|
||||
if (attrs.has("outputHashMode")) {
|
||||
const modeValue = force(attrs.get("outputHashMode") as NixValue);
|
||||
if (!(ignoreNulls && modeValue === null)) {
|
||||
hashMode = forceStringValue(attrs.outputHashMode);
|
||||
hashMode = forceStringValue(modeValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -341,18 +349,22 @@ export const derivationStrict = (args: NixValue): NixAttrs => {
|
||||
const builder = validateBuilder(attrs, collectedContext);
|
||||
const platform = validateSystem(attrs);
|
||||
|
||||
const structuredAttrs = "__structuredAttrs" in attrs ? force(attrs.__structuredAttrs) === true : false;
|
||||
const ignoreNulls = "__ignoreNulls" in attrs ? force(attrs.__ignoreNulls) === true : false;
|
||||
const structuredAttrs = attrs.has("__structuredAttrs")
|
||||
? force(attrs.get("__structuredAttrs") as NixValue) === true
|
||||
: false;
|
||||
const ignoreNulls = attrs.has("__ignoreNulls")
|
||||
? force(attrs.get("__ignoreNulls") as NixValue) === true
|
||||
: false;
|
||||
|
||||
const outputs = extractOutputs(attrs, structuredAttrs);
|
||||
const fixedOutputInfo = extractFixedOutputInfo(attrs, ignoreNulls);
|
||||
validateFixedOutputConstraints(fixedOutputInfo, outputs);
|
||||
|
||||
if ("__contentAddressed" in attrs && force(attrs.__contentAddressed) === true) {
|
||||
if (attrs.has("__contentAddressed") && force(attrs.get("__contentAddressed") as NixValue) === true) {
|
||||
throw new Error("ca derivations are not supported");
|
||||
}
|
||||
|
||||
if ("__impure" in attrs && force(attrs.__impure) === true) {
|
||||
if (attrs.has("__impure") && force(attrs.get("__impure") as NixValue) === true) {
|
||||
throw new Error("impure derivations are not supported");
|
||||
}
|
||||
|
||||
@@ -376,16 +388,16 @@ export const derivationStrict = (args: NixValue): NixAttrs => {
|
||||
fixedOutput: fixedOutputInfo,
|
||||
});
|
||||
|
||||
const result: NixAttrs = {};
|
||||
const result: NixAttrs = new Map();
|
||||
|
||||
const drvPathContext = new Set<string>();
|
||||
addDrvDeepContext(drvPathContext, rustResult.drvPath);
|
||||
result.drvPath = mkStringWithContext(rustResult.drvPath, drvPathContext);
|
||||
result.set("drvPath", mkStringWithContext(rustResult.drvPath, drvPathContext));
|
||||
|
||||
for (const [outputName, outputPath] of rustResult.outputs) {
|
||||
const outputContext = new Set<string>();
|
||||
addBuiltContext(outputContext, rustResult.drvPath, outputName);
|
||||
result[outputName] = mkStringWithContext(outputPath, outputContext);
|
||||
result.set(outputName, mkStringWithContext(outputPath, outputContext));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { select } from "../helpers";
|
||||
import { forceAttrs, forceStringNoCtx, forceStringValue } from "../type-assert";
|
||||
import type { NixValue } from "../types";
|
||||
import { realisePath } from "./io";
|
||||
@@ -20,14 +21,14 @@ export const hashString =
|
||||
|
||||
export const convertHash = (args: NixValue): string => {
|
||||
const attrs = forceAttrs(args);
|
||||
const hash = forceStringNoCtx(attrs.hash);
|
||||
const hash = forceStringNoCtx(select(attrs, ["hash"]));
|
||||
|
||||
let hashAlgo: string | null = null;
|
||||
if ("hashAlgo" in attrs) {
|
||||
hashAlgo = forceStringNoCtx(attrs.hashAlgo);
|
||||
if (attrs.has("hashAlgo")) {
|
||||
hashAlgo = forceStringNoCtx(select(attrs, ["hashAlgo"]));
|
||||
}
|
||||
|
||||
const toHashFormat = forceStringNoCtx(attrs.toHashFormat);
|
||||
const toHashFormat = forceStringNoCtx(select(attrs, ["toHashFormat"]));
|
||||
|
||||
return Deno.core.ops.op_convert_hash({
|
||||
hash,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createThunk, force } from "../thunk";
|
||||
import type { NixValue } from "../types";
|
||||
import type { NixAttrs, NixValue } from "../types";
|
||||
import * as arithmetic from "./arithmetic";
|
||||
import * as attrs from "./attrs";
|
||||
import * as conversion from "./conversion";
|
||||
@@ -74,149 +74,151 @@ export const getPrimopMetadata = (func: unknown): PrimopMetadata | undefined =>
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const builtins: Record<string, NixValue> = {
|
||||
add: mkPrimop(arithmetic.add, "add", 2),
|
||||
sub: mkPrimop(arithmetic.sub, "sub", 2),
|
||||
mul: mkPrimop(arithmetic.mul, "mul", 2),
|
||||
div: mkPrimop(arithmetic.div, "div", 2),
|
||||
bitAnd: mkPrimop(arithmetic.bitAnd, "bitAnd", 2),
|
||||
bitOr: mkPrimop(arithmetic.bitOr, "bitOr", 2),
|
||||
bitXor: mkPrimop(arithmetic.bitXor, "bitXor", 2),
|
||||
lessThan: mkPrimop(arithmetic.lessThan, "lessThan", 2),
|
||||
export const builtins: NixAttrs = new Map<string, NixValue>(
|
||||
Object.entries({
|
||||
add: mkPrimop(arithmetic.add, "add", 2),
|
||||
sub: mkPrimop(arithmetic.sub, "sub", 2),
|
||||
mul: mkPrimop(arithmetic.mul, "mul", 2),
|
||||
div: mkPrimop(arithmetic.div, "div", 2),
|
||||
bitAnd: mkPrimop(arithmetic.bitAnd, "bitAnd", 2),
|
||||
bitOr: mkPrimop(arithmetic.bitOr, "bitOr", 2),
|
||||
bitXor: mkPrimop(arithmetic.bitXor, "bitXor", 2),
|
||||
lessThan: mkPrimop(arithmetic.lessThan, "lessThan", 2),
|
||||
|
||||
ceil: mkPrimop(math.ceil, "ceil", 1),
|
||||
floor: mkPrimop(math.floor, "floor", 1),
|
||||
ceil: mkPrimop(math.ceil, "ceil", 1),
|
||||
floor: mkPrimop(math.floor, "floor", 1),
|
||||
|
||||
isAttrs: mkPrimop((e: NixValue) => typeCheck.isAttrs(force(e)), "isAttrs", 1),
|
||||
isBool: mkPrimop((e: NixValue) => typeCheck.isBool(force(e)), "isBool", 1),
|
||||
isFloat: mkPrimop((e: NixValue) => typeCheck.isFloat(force(e)), "isFloat", 1),
|
||||
isFunction: mkPrimop((e: NixValue) => typeCheck.isFunction(force(e)), "isFunction", 1),
|
||||
isInt: mkPrimop((e: NixValue) => typeCheck.isInt(force(e)), "isInt", 1),
|
||||
isList: mkPrimop((e: NixValue) => typeCheck.isList(force(e)), "isList", 1),
|
||||
isNull: mkPrimop((e: NixValue) => typeCheck.isNull(force(e)), "isNull", 1),
|
||||
isPath: mkPrimop((e: NixValue) => typeCheck.isPath(force(e)), "isPath", 1),
|
||||
isString: mkPrimop((e: NixValue) => typeCheck.isString(force(e)), "isString", 1),
|
||||
typeOf: mkPrimop((e: NixValue) => typeCheck.typeOf(force(e)), "typeOf", 1),
|
||||
isAttrs: mkPrimop((e: NixValue) => typeCheck.isAttrs(force(e)), "isAttrs", 1),
|
||||
isBool: mkPrimop((e: NixValue) => typeCheck.isBool(force(e)), "isBool", 1),
|
||||
isFloat: mkPrimop((e: NixValue) => typeCheck.isFloat(force(e)), "isFloat", 1),
|
||||
isFunction: mkPrimop((e: NixValue) => typeCheck.isFunction(force(e)), "isFunction", 1),
|
||||
isInt: mkPrimop((e: NixValue) => typeCheck.isInt(force(e)), "isInt", 1),
|
||||
isList: mkPrimop((e: NixValue) => typeCheck.isList(force(e)), "isList", 1),
|
||||
isNull: mkPrimop((e: NixValue) => typeCheck.isNull(force(e)), "isNull", 1),
|
||||
isPath: mkPrimop((e: NixValue) => typeCheck.isPath(force(e)), "isPath", 1),
|
||||
isString: mkPrimop((e: NixValue) => typeCheck.isString(force(e)), "isString", 1),
|
||||
typeOf: mkPrimop((e: NixValue) => typeCheck.typeOf(force(e)), "typeOf", 1),
|
||||
|
||||
map: mkPrimop(list.map, "map", 2),
|
||||
filter: mkPrimop(list.filter, "filter", 2),
|
||||
length: mkPrimop(list.length, "length", 1),
|
||||
head: mkPrimop(list.head, "head", 1),
|
||||
tail: mkPrimop(list.tail, "tail", 1),
|
||||
elem: mkPrimop(list.elem, "elem", 2),
|
||||
elemAt: mkPrimop(list.elemAt, "elemAt", 2),
|
||||
concatLists: mkPrimop(list.concatLists, "concatLists", 1),
|
||||
concatMap: mkPrimop(list.concatMap, "concatMap", 2),
|
||||
"foldl'": mkPrimop(list.foldlPrime, "foldl'", 3),
|
||||
sort: mkPrimop(list.sort, "sort", 2),
|
||||
partition: mkPrimop(list.partition, "partition", 2),
|
||||
genList: mkPrimop(list.genList, "genList", 2),
|
||||
all: mkPrimop(list.all, "all", 2),
|
||||
any: mkPrimop(list.any, "any", 2),
|
||||
map: mkPrimop(list.map, "map", 2),
|
||||
filter: mkPrimop(list.filter, "filter", 2),
|
||||
length: mkPrimop(list.length, "length", 1),
|
||||
head: mkPrimop(list.head, "head", 1),
|
||||
tail: mkPrimop(list.tail, "tail", 1),
|
||||
elem: mkPrimop(list.elem, "elem", 2),
|
||||
elemAt: mkPrimop(list.elemAt, "elemAt", 2),
|
||||
concatLists: mkPrimop(list.concatLists, "concatLists", 1),
|
||||
concatMap: mkPrimop(list.concatMap, "concatMap", 2),
|
||||
"foldl'": mkPrimop(list.foldlPrime, "foldl'", 3),
|
||||
sort: mkPrimop(list.sort, "sort", 2),
|
||||
partition: mkPrimop(list.partition, "partition", 2),
|
||||
genList: mkPrimop(list.genList, "genList", 2),
|
||||
all: mkPrimop(list.all, "all", 2),
|
||||
any: mkPrimop(list.any, "any", 2),
|
||||
|
||||
attrNames: mkPrimop(attrs.attrNames, "attrNames", 1),
|
||||
attrValues: mkPrimop(attrs.attrValues, "attrValues", 1),
|
||||
getAttr: mkPrimop(attrs.getAttr, "getAttr", 2),
|
||||
hasAttr: mkPrimop(attrs.hasAttr, "hasAttr", 2),
|
||||
mapAttrs: mkPrimop(attrs.mapAttrs, "mapAttrs", 2),
|
||||
removeAttrs: mkPrimop(attrs.removeAttrs, "removeAttrs", 2),
|
||||
listToAttrs: mkPrimop(attrs.listToAttrs, "listToAttrs", 1),
|
||||
intersectAttrs: mkPrimop(attrs.intersectAttrs, "intersectAttrs", 2),
|
||||
catAttrs: mkPrimop(attrs.catAttrs, "catAttrs", 2),
|
||||
groupBy: mkPrimop(attrs.groupBy, "groupBy", 2),
|
||||
zipAttrsWith: mkPrimop(attrs.zipAttrsWith, "zipAttrsWith", 2),
|
||||
unsafeGetAttrPos: mkPrimop(attrs.unsafeGetAttrPos, "unsafeGetAttrPos", 2),
|
||||
attrNames: mkPrimop(attrs.attrNames, "attrNames", 1),
|
||||
attrValues: mkPrimop(attrs.attrValues, "attrValues", 1),
|
||||
getAttr: mkPrimop(attrs.getAttr, "getAttr", 2),
|
||||
hasAttr: mkPrimop(attrs.hasAttr, "hasAttr", 2),
|
||||
mapAttrs: mkPrimop(attrs.mapAttrs, "mapAttrs", 2),
|
||||
removeAttrs: mkPrimop(attrs.removeAttrs, "removeAttrs", 2),
|
||||
listToAttrs: mkPrimop(attrs.listToAttrs, "listToAttrs", 1),
|
||||
intersectAttrs: mkPrimop(attrs.intersectAttrs, "intersectAttrs", 2),
|
||||
catAttrs: mkPrimop(attrs.catAttrs, "catAttrs", 2),
|
||||
groupBy: mkPrimop(attrs.groupBy, "groupBy", 2),
|
||||
zipAttrsWith: mkPrimop(attrs.zipAttrsWith, "zipAttrsWith", 2),
|
||||
unsafeGetAttrPos: mkPrimop(attrs.unsafeGetAttrPos, "unsafeGetAttrPos", 2),
|
||||
|
||||
stringLength: mkPrimop(string.stringLength, "stringLength", 1),
|
||||
substring: mkPrimop(string.substring, "substring", 3),
|
||||
concatStringsSep: mkPrimop(string.concatStringsSep, "concatStringsSep", 2),
|
||||
baseNameOf: mkPrimop(pathOps.baseNameOf, "baseNameOf", 1),
|
||||
dirOf: mkPrimop(pathOps.dirOf, "dirOf", 1),
|
||||
toPath: mkPrimop(pathOps.toPath, "toPath", 1),
|
||||
match: mkPrimop(string.match, "match", 2),
|
||||
split: mkPrimop(string.split, "split", 2),
|
||||
stringLength: mkPrimop(string.stringLength, "stringLength", 1),
|
||||
substring: mkPrimop(string.substring, "substring", 3),
|
||||
concatStringsSep: mkPrimop(string.concatStringsSep, "concatStringsSep", 2),
|
||||
baseNameOf: mkPrimop(pathOps.baseNameOf, "baseNameOf", 1),
|
||||
dirOf: mkPrimop(pathOps.dirOf, "dirOf", 1),
|
||||
toPath: mkPrimop(pathOps.toPath, "toPath", 1),
|
||||
match: mkPrimop(string.match, "match", 2),
|
||||
split: mkPrimop(string.split, "split", 2),
|
||||
|
||||
seq: mkPrimop(functional.seq, "seq", 2),
|
||||
deepSeq: mkPrimop(functional.deepSeq, "deepSeq", 2),
|
||||
abort: mkPrimop(functional.abort, "abort", 1),
|
||||
throw: mkPrimop(functional.throwFunc, "throw", 1),
|
||||
trace: mkPrimop(functional.trace, "trace", 2),
|
||||
warn: mkPrimop(functional.warn, "warn", 2),
|
||||
break: mkPrimop(functional.breakFunc, "break", 1),
|
||||
seq: mkPrimop(functional.seq, "seq", 2),
|
||||
deepSeq: mkPrimop(functional.deepSeq, "deepSeq", 2),
|
||||
abort: mkPrimop(functional.abort, "abort", 1),
|
||||
throw: mkPrimop(functional.throwFunc, "throw", 1),
|
||||
trace: mkPrimop(functional.trace, "trace", 2),
|
||||
warn: mkPrimop(functional.warn, "warn", 2),
|
||||
break: mkPrimop(functional.breakFunc, "break", 1),
|
||||
|
||||
derivation: mkPrimop(derivation.derivationStub, "derivation", 1),
|
||||
derivationStrict: mkPrimop(derivation.derivationStrict, "derivationStrict", 1),
|
||||
derivation: mkPrimop(derivation.derivationStub, "derivation", 1),
|
||||
derivationStrict: mkPrimop(derivation.derivationStrict, "derivationStrict", 1),
|
||||
|
||||
import: mkPrimop(io.importFunc, "import", 1),
|
||||
scopedImport: mkPrimop(io.scopedImport, "scopedImport", 2),
|
||||
storePath: mkPrimop(io.storePath, "storePath", 1),
|
||||
fetchClosure: mkPrimop(io.fetchClosure, "fetchClosure", 1),
|
||||
fetchMercurial: mkPrimop(io.fetchMercurial, "fetchMercurial", 1),
|
||||
fetchGit: mkPrimop(io.fetchGit, "fetchGit", 1),
|
||||
fetchTarball: mkPrimop(io.fetchTarball, "fetchTarball", 1),
|
||||
fetchTree: mkPrimop(io.fetchTree, "fetchTree", 1),
|
||||
fetchurl: mkPrimop(io.fetchurl, "fetchurl", 1),
|
||||
readDir: mkPrimop(io.readDir, "readDir", 1),
|
||||
readFile: mkPrimop(io.readFile, "readFile", 1),
|
||||
readFileType: mkPrimop(io.readFileType, "readFileType", 1),
|
||||
pathExists: mkPrimop(io.pathExists, "pathExists", 1),
|
||||
path: mkPrimop(io.path, "path", 1),
|
||||
toFile: mkPrimop(io.toFile, "toFile", 2),
|
||||
filterSource: mkPrimop(io.filterSource, "filterSource", 2),
|
||||
findFile: mkPrimop(io.findFile, "findFile", 2),
|
||||
getEnv: mkPrimop(io.getEnv, "getEnv", 1),
|
||||
import: mkPrimop(io.importFunc, "import", 1),
|
||||
scopedImport: mkPrimop(io.scopedImport, "scopedImport", 2),
|
||||
storePath: mkPrimop(io.storePath, "storePath", 1),
|
||||
fetchClosure: mkPrimop(io.fetchClosure, "fetchClosure", 1),
|
||||
fetchMercurial: mkPrimop(io.fetchMercurial, "fetchMercurial", 1),
|
||||
fetchGit: mkPrimop(io.fetchGit, "fetchGit", 1),
|
||||
fetchTarball: mkPrimop(io.fetchTarball, "fetchTarball", 1),
|
||||
fetchTree: mkPrimop(io.fetchTree, "fetchTree", 1),
|
||||
fetchurl: mkPrimop(io.fetchurl, "fetchurl", 1),
|
||||
readDir: mkPrimop(io.readDir, "readDir", 1),
|
||||
readFile: mkPrimop(io.readFile, "readFile", 1),
|
||||
readFileType: mkPrimop(io.readFileType, "readFileType", 1),
|
||||
pathExists: mkPrimop(io.pathExists, "pathExists", 1),
|
||||
path: mkPrimop(io.path, "path", 1),
|
||||
toFile: mkPrimop(io.toFile, "toFile", 2),
|
||||
filterSource: mkPrimop(io.filterSource, "filterSource", 2),
|
||||
findFile: mkPrimop(io.findFile, "findFile", 2),
|
||||
getEnv: mkPrimop(io.getEnv, "getEnv", 1),
|
||||
|
||||
fromJSON: mkPrimop(conversion.fromJSON, "fromJSON", 1),
|
||||
fromTOML: mkPrimop(conversion.fromTOML, "fromTOML", 1),
|
||||
toJSON: mkPrimop(conversion.toJSON, "toJSON", 1),
|
||||
toXML: mkPrimop(conversion.toXML, "toXML", 1),
|
||||
toString: mkPrimop(conversion.toStringFunc, "toString", 1),
|
||||
fromJSON: mkPrimop(conversion.fromJSON, "fromJSON", 1),
|
||||
fromTOML: mkPrimop(conversion.fromTOML, "fromTOML", 1),
|
||||
toJSON: mkPrimop(conversion.toJSON, "toJSON", 1),
|
||||
toXML: mkPrimop(conversion.toXML, "toXML", 1),
|
||||
toString: mkPrimop(conversion.toStringFunc, "toString", 1),
|
||||
|
||||
hashFile: mkPrimop(hash.hashFile, "hashFile", 2),
|
||||
hashString: mkPrimop(hash.hashString, "hashString", 2),
|
||||
convertHash: mkPrimop(hash.convertHash, "convertHash", 2),
|
||||
hashFile: mkPrimop(hash.hashFile, "hashFile", 2),
|
||||
hashString: mkPrimop(hash.hashString, "hashString", 2),
|
||||
convertHash: mkPrimop(hash.convertHash, "convertHash", 2),
|
||||
|
||||
flakeRefToString: mkPrimop(flake.flakeRefToString, "flakeRefToString", 1),
|
||||
getFlake: mkPrimop(flake.getFlake, "getFlake", 1),
|
||||
parseFlakeName: mkPrimop(flake.parseFlakeName, "parseFlakeName", 1),
|
||||
parseFlakeRef: mkPrimop(flake.parseFlakeRef, "parseFlakeRef", 1),
|
||||
flakeRefToString: mkPrimop(flake.flakeRefToString, "flakeRefToString", 1),
|
||||
getFlake: mkPrimop(flake.getFlake, "getFlake", 1),
|
||||
parseFlakeName: mkPrimop(flake.parseFlakeName, "parseFlakeName", 1),
|
||||
parseFlakeRef: mkPrimop(flake.parseFlakeRef, "parseFlakeRef", 1),
|
||||
|
||||
addErrorContext: mkPrimop(misc.addErrorContext, "addErrorContext", 1),
|
||||
appendContext: mkPrimop(misc.appendContext, "appendContext", 1),
|
||||
getContext: mkPrimop(misc.getContext, "getContext", 1),
|
||||
hasContext: mkPrimop(misc.hasContext, "hasContext", 1),
|
||||
unsafeDiscardOutputDependency: mkPrimop(
|
||||
misc.unsafeDiscardOutputDependency,
|
||||
"unsafeDiscardOutputDependency",
|
||||
1,
|
||||
),
|
||||
unsafeDiscardStringContext: mkPrimop(misc.unsafeDiscardStringContext, "unsafeDiscardStringContext", 1),
|
||||
addDrvOutputDependencies: mkPrimop(misc.addDrvOutputDependencies, "addDrvOutputDependencies", 2),
|
||||
compareVersions: mkPrimop(misc.compareVersions, "compareVersions", 2),
|
||||
functionArgs: mkPrimop(misc.functionArgs, "functionArgs", 1),
|
||||
genericClosure: mkPrimop(misc.genericClosure, "genericClosure", 1),
|
||||
outputOf: mkPrimop(misc.outputOf, "outputOf", 2),
|
||||
parseDrvName: mkPrimop(misc.parseDrvName, "parseDrvName", 1),
|
||||
placeholder: mkPrimop(misc.placeholder, "placeholder", 1),
|
||||
replaceStrings: mkPrimop(misc.replaceStrings, "replaceStrings", 3),
|
||||
splitVersion: mkPrimop(misc.splitVersion, "splitVersion", 1),
|
||||
traceVerbose: mkPrimop(misc.traceVerbose, "traceVerbose", 2),
|
||||
tryEval: mkPrimop(misc.tryEval, "tryEval", 1),
|
||||
addErrorContext: mkPrimop(misc.addErrorContext, "addErrorContext", 1),
|
||||
appendContext: mkPrimop(misc.appendContext, "appendContext", 1),
|
||||
getContext: mkPrimop(misc.getContext, "getContext", 1),
|
||||
hasContext: mkPrimop(misc.hasContext, "hasContext", 1),
|
||||
unsafeDiscardOutputDependency: mkPrimop(
|
||||
misc.unsafeDiscardOutputDependency,
|
||||
"unsafeDiscardOutputDependency",
|
||||
1,
|
||||
),
|
||||
unsafeDiscardStringContext: mkPrimop(misc.unsafeDiscardStringContext, "unsafeDiscardStringContext", 1),
|
||||
addDrvOutputDependencies: mkPrimop(misc.addDrvOutputDependencies, "addDrvOutputDependencies", 2),
|
||||
compareVersions: mkPrimop(misc.compareVersions, "compareVersions", 2),
|
||||
functionArgs: mkPrimop(misc.functionArgs, "functionArgs", 1),
|
||||
genericClosure: mkPrimop(misc.genericClosure, "genericClosure", 1),
|
||||
outputOf: mkPrimop(misc.outputOf, "outputOf", 2),
|
||||
parseDrvName: mkPrimop(misc.parseDrvName, "parseDrvName", 1),
|
||||
placeholder: mkPrimop(misc.placeholder, "placeholder", 1),
|
||||
replaceStrings: mkPrimop(misc.replaceStrings, "replaceStrings", 3),
|
||||
splitVersion: mkPrimop(misc.splitVersion, "splitVersion", 1),
|
||||
traceVerbose: mkPrimop(misc.traceVerbose, "traceVerbose", 2),
|
||||
tryEval: mkPrimop(misc.tryEval, "tryEval", 1),
|
||||
|
||||
builtins: createThunk(() => builtins, "builtins"),
|
||||
currentSystem: createThunk(() => {
|
||||
return "x86_64-linux";
|
||||
}, "currentSystem"),
|
||||
currentTime: createThunk(() => Date.now(), "currentTime"),
|
||||
builtins: createThunk(() => builtins, "builtins"),
|
||||
currentSystem: createThunk(() => {
|
||||
return "x86_64-linux";
|
||||
}, "currentSystem"),
|
||||
currentTime: createThunk(() => Date.now(), "currentTime"),
|
||||
|
||||
false: false,
|
||||
true: true,
|
||||
null: null,
|
||||
false: false,
|
||||
true: true,
|
||||
null: null,
|
||||
|
||||
langVersion: 6,
|
||||
nixPath: [],
|
||||
nixVersion: "2.31.2",
|
||||
storeDir: createThunk(() => {
|
||||
throw new Error("stub storeDir evaluated");
|
||||
langVersion: 6,
|
||||
nixPath: [],
|
||||
nixVersion: "2.31.2",
|
||||
storeDir: createThunk(() => {
|
||||
throw new Error("stub storeDir evaluated");
|
||||
}),
|
||||
}),
|
||||
};
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { select } from "../helpers";
|
||||
import { getPathValue } from "../path";
|
||||
import type { NixStringContext, StringWithContext } from "../string-context";
|
||||
import { addOpaqueContext, decodeContextElem, mkStringWithContext } from "../string-context";
|
||||
@@ -59,7 +60,7 @@ export const scopedImport =
|
||||
(scope: NixValue) =>
|
||||
(path: NixValue): NixValue => {
|
||||
const scopeAttrs = forceAttrs(scope);
|
||||
const scopeKeys = Object.keys(scopeAttrs);
|
||||
const scopeKeys = Array.from(scopeAttrs.keys());
|
||||
|
||||
const pathStr = realisePath(path);
|
||||
|
||||
@@ -112,25 +113,23 @@ const normalizeUrlInput = (
|
||||
return { url: forced };
|
||||
}
|
||||
const attrs = forceAttrs(args);
|
||||
const url = forceStringValue(attrs.url);
|
||||
const hash =
|
||||
"sha256" in attrs
|
||||
? forceStringValue(attrs.sha256)
|
||||
: "hash" in attrs
|
||||
? forceStringValue(attrs.hash)
|
||||
: undefined;
|
||||
const name = "name" in attrs ? forceStringValue(attrs.name) : undefined;
|
||||
const executable = "executable" in attrs ? forceBool(attrs.executable) : false;
|
||||
const url = forceStringValue(select(attrs, ["url"]));
|
||||
const hash = attrs.has("sha256")
|
||||
? forceStringValue(attrs.get("sha256") as NixValue)
|
||||
: attrs.has("hash")
|
||||
? forceStringValue(attrs.get("hash") as NixValue)
|
||||
: undefined;
|
||||
const name = attrs.has("name") ? forceStringValue(attrs.get("name") as NixValue) : undefined;
|
||||
const executable = attrs.has("executable") ? forceBool(attrs.get("executable") as NixValue) : false;
|
||||
return { url, hash, name, executable };
|
||||
};
|
||||
|
||||
const normalizeTarballInput = (args: NixValue): { url: string; sha256?: string; name?: string } => {
|
||||
const forced = force(args);
|
||||
if (isAttrs(forced)) {
|
||||
const url = resolvePseudoUrl(forceStringNoCtx(forced.url));
|
||||
const sha256 = "sha256" in forced ? forceStringNoCtx(forced.sha256) : undefined;
|
||||
const nameRaw = "name" in forced ? forceStringNoCtx(forced.name) : undefined;
|
||||
// FIXME: extract baseNameOfRaw
|
||||
const url = resolvePseudoUrl(forceStringNoCtx(select(forced, ["url"])));
|
||||
const sha256 = forced.has("sha256") ? forceStringNoCtx(forced.get("sha256") as NixValue) : undefined;
|
||||
const nameRaw = forced.has("name") ? forceStringNoCtx(forced.get("name") as NixValue) : undefined;
|
||||
const name = nameRaw === "" ? (baseNameOf(nameRaw) as string) : nameRaw;
|
||||
return { url, sha256, name };
|
||||
} else {
|
||||
@@ -175,25 +174,25 @@ export const fetchGit = (args: NixValue): NixAttrs => {
|
||||
const result = Deno.core.ops.op_fetch_git(url, null, null, false, false, false, null);
|
||||
const outContext: NixStringContext = new Set();
|
||||
addOpaqueContext(outContext, result.out_path);
|
||||
return {
|
||||
outPath: mkStringWithContext(result.out_path, outContext),
|
||||
rev: result.rev,
|
||||
shortRev: result.short_rev,
|
||||
revCount: BigInt(result.rev_count),
|
||||
lastModified: BigInt(result.last_modified),
|
||||
lastModifiedDate: result.last_modified_date,
|
||||
submodules: result.submodules,
|
||||
narHash: result.nar_hash,
|
||||
};
|
||||
return new Map<string, NixValue>([
|
||||
["outPath", mkStringWithContext(result.out_path, outContext)],
|
||||
["rev", result.rev],
|
||||
["shortRev", result.short_rev],
|
||||
["revCount", BigInt(result.rev_count)],
|
||||
["lastModified", BigInt(result.last_modified)],
|
||||
["lastModifiedDate", result.last_modified_date],
|
||||
["submodules", result.submodules],
|
||||
["narHash", result.nar_hash],
|
||||
]);
|
||||
}
|
||||
const attrs = forceAttrs(args);
|
||||
const url = forceStringValue(attrs.url);
|
||||
const gitRef = "ref" in attrs ? forceStringValue(attrs.ref) : null;
|
||||
const rev = "rev" in attrs ? forceStringValue(attrs.rev) : null;
|
||||
const shallow = "shallow" in attrs ? forceBool(attrs.shallow) : false;
|
||||
const submodules = "submodules" in attrs ? forceBool(attrs.submodules) : false;
|
||||
const allRefs = "allRefs" in attrs ? forceBool(attrs.allRefs) : false;
|
||||
const name = "name" in attrs ? forceStringValue(attrs.name) : null;
|
||||
const url = forceStringValue(select("attrs", ["url"]));
|
||||
const gitRef = attrs.has("ref") ? forceStringValue(attrs.get("ref") as NixValue) : null;
|
||||
const rev = attrs.has("rev") ? forceStringValue(attrs.get("rev") as NixValue) : null;
|
||||
const shallow = attrs.has("shallow") ? forceBool(attrs.get("shallow") as NixValue) : false;
|
||||
const submodules = attrs.has("submodules") ? forceBool(attrs.get("submodules") as NixValue) : false;
|
||||
const allRefs = attrs.has("allRefs") ? forceBool(attrs.get("allRefs") as NixValue) : false;
|
||||
const name = attrs.has("name") ? forceStringValue(attrs.get("name") as NixValue) : null;
|
||||
|
||||
const result: FetchGitResult = Deno.core.ops.op_fetch_git(
|
||||
url,
|
||||
@@ -207,16 +206,16 @@ export const fetchGit = (args: NixValue): NixAttrs => {
|
||||
|
||||
const outContext: NixStringContext = new Set();
|
||||
addOpaqueContext(outContext, result.out_path);
|
||||
return {
|
||||
outPath: mkStringWithContext(result.out_path, outContext),
|
||||
rev: result.rev,
|
||||
shortRev: result.short_rev,
|
||||
revCount: BigInt(result.rev_count),
|
||||
lastModified: BigInt(result.last_modified),
|
||||
lastModifiedDate: result.last_modified_date,
|
||||
submodules: result.submodules,
|
||||
narHash: result.nar_hash,
|
||||
};
|
||||
return new Map<string, NixValue>([
|
||||
["outPath", mkStringWithContext(result.out_path, outContext)],
|
||||
["rev", result.rev],
|
||||
["shortRev", result.short_rev],
|
||||
["revCount", BigInt(result.rev_count)],
|
||||
["lastModified", BigInt(result.last_modified)],
|
||||
["lastModifiedDate", result.last_modified_date],
|
||||
["submodules", result.submodules],
|
||||
["narHash", result.nar_hash],
|
||||
]);
|
||||
};
|
||||
|
||||
export const fetchMercurial = (_args: NixValue): NixAttrs => {
|
||||
@@ -225,7 +224,7 @@ export const fetchMercurial = (_args: NixValue): NixAttrs => {
|
||||
|
||||
export const fetchTree = (args: NixValue): NixAttrs => {
|
||||
const attrs = forceAttrs(args);
|
||||
const type = "type" in attrs ? forceStringValue(attrs.type) : "auto";
|
||||
const type = attrs.has("type") ? forceStringValue(attrs.get("type") as NixValue) : "auto";
|
||||
|
||||
switch (type) {
|
||||
case "git":
|
||||
@@ -234,12 +233,12 @@ export const fetchTree = (args: NixValue): NixAttrs => {
|
||||
case "mercurial":
|
||||
return fetchMercurial(args);
|
||||
case "tarball":
|
||||
return { outPath: fetchTarball(args) };
|
||||
return new Map<string, NixValue>([["outPath", fetchTarball(args)]]);
|
||||
case "file":
|
||||
return { outPath: fetchurl(args) };
|
||||
return new Map<string, NixValue>([["outPath", fetchurl(args)]]);
|
||||
case "path": {
|
||||
const path = forceStringValue(attrs.path);
|
||||
return { outPath: path };
|
||||
const path = forceStringValue(select(attrs, ["path"]));
|
||||
return new Map<string, NixValue>([["outPath", path]]);
|
||||
}
|
||||
case "github":
|
||||
case "gitlab":
|
||||
@@ -251,11 +250,14 @@ export const fetchTree = (args: NixValue): NixAttrs => {
|
||||
};
|
||||
|
||||
const fetchGitForge = (forge: string, attrs: NixAttrs): NixAttrs => {
|
||||
const owner = forceStringValue(attrs.owner);
|
||||
const repo = forceStringValue(attrs.repo);
|
||||
const rev =
|
||||
"rev" in attrs ? forceStringValue(attrs.rev) : "ref" in attrs ? forceStringValue(attrs.ref) : "HEAD";
|
||||
const host = "host" in attrs ? forceStringValue(attrs.host) : undefined;
|
||||
const owner = forceStringValue(select(forge, ["owner"]));
|
||||
const repo = forceStringValue(select(forge, ["repo"]));
|
||||
const rev = attrs.has("rev")
|
||||
? forceStringValue(attrs.get("rev") as NixValue)
|
||||
: attrs.has("ref")
|
||||
? forceStringValue(attrs.get("ref") as NixValue)
|
||||
: "HEAD";
|
||||
const host = attrs.has("host") ? forceStringValue(attrs.get("host") as NixValue) : undefined;
|
||||
|
||||
let tarballUrl: string;
|
||||
switch (forge) {
|
||||
@@ -278,17 +280,17 @@ const fetchGitForge = (forge: string, attrs: NixAttrs): NixAttrs => {
|
||||
throw new Error(`Unknown forge type: ${forge}`);
|
||||
}
|
||||
|
||||
const outPath = fetchTarball({ url: tarballUrl, ...attrs });
|
||||
const outPath = fetchTarball(new Map<string, NixValue>([["url", tarballUrl], ...attrs]));
|
||||
|
||||
return {
|
||||
outPath,
|
||||
rev,
|
||||
shortRev: rev.substring(0, 7),
|
||||
};
|
||||
return new Map<string, NixValue>([
|
||||
["outPath", outPath],
|
||||
["rev", rev],
|
||||
["shortRev", rev.substring(0, 7)],
|
||||
]);
|
||||
};
|
||||
|
||||
const autoDetectAndFetch = (attrs: NixAttrs): NixAttrs => {
|
||||
const url = forceStringValue(attrs.url);
|
||||
const url = forceStringValue(select(attrs, ["url"]));
|
||||
if (url.endsWith(".git") || url.includes("github.com") || url.includes("gitlab.com")) {
|
||||
return fetchGit(attrs);
|
||||
}
|
||||
@@ -298,17 +300,17 @@ const autoDetectAndFetch = (attrs: NixAttrs): NixAttrs => {
|
||||
url.endsWith(".tar.bz2") ||
|
||||
url.endsWith(".tgz")
|
||||
) {
|
||||
return { outPath: fetchTarball(attrs) };
|
||||
return new Map<string, NixValue>([["outPath", fetchTarball(attrs)]]);
|
||||
}
|
||||
return { outPath: fetchurl(attrs) };
|
||||
return new Map<string, NixValue>([["outPath", fetchurl(attrs)]]);
|
||||
};
|
||||
|
||||
export const readDir = (path: NixValue): NixAttrs => {
|
||||
const pathStr = realisePath(path);
|
||||
const entries: Record<string, string> = Deno.core.ops.op_read_dir(pathStr);
|
||||
const result: NixAttrs = {};
|
||||
const result: NixAttrs = new Map();
|
||||
for (const [name, type] of Object.entries(entries)) {
|
||||
result[name] = type;
|
||||
result.set(name, type);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -348,11 +350,11 @@ export const pathExists = (path: NixValue): boolean => {
|
||||
export const path = (args: NixValue): NixString => {
|
||||
const attrs = forceAttrs(args);
|
||||
|
||||
if (!("path" in attrs)) {
|
||||
if (!attrs.has("path")) {
|
||||
throw new TypeError("builtins.path: 'path' attribute is required");
|
||||
}
|
||||
|
||||
const pathValue = force(attrs.path);
|
||||
const pathValue = force(attrs.get("path") as NixValue);
|
||||
let pathStr: string;
|
||||
|
||||
if (isNixPath(pathValue)) {
|
||||
@@ -361,14 +363,14 @@ export const path = (args: NixValue): NixString => {
|
||||
pathStr = forceStringValue(pathValue);
|
||||
}
|
||||
|
||||
const name = "name" in attrs ? forceStringValue(attrs.name) : null;
|
||||
const recursive = "recursive" in attrs ? forceBool(attrs.recursive) : true;
|
||||
const sha256 = "sha256" in attrs ? forceStringValue(attrs.sha256) : null;
|
||||
const name = attrs.has("name") ? forceStringValue(attrs.get("name") as NixValue) : null;
|
||||
const recursive = attrs.has("recursive") ? forceBool(attrs.get("recursive") as NixValue) : true;
|
||||
const sha256 = attrs.has("sha256") ? forceStringValue(attrs.get("sha256") as NixValue) : null;
|
||||
|
||||
let storePath: string;
|
||||
|
||||
if ("filter" in attrs) {
|
||||
const filterFn = forceFunction(attrs.filter);
|
||||
if (attrs.has("filter")) {
|
||||
const filterFn = forceFunction(attrs.get("filter") as NixValue);
|
||||
|
||||
const entries: [string, string][] = Deno.core.ops.op_walk_dir(pathStr);
|
||||
|
||||
@@ -445,9 +447,9 @@ export const findFile =
|
||||
for (const item of forcedSearchPath) {
|
||||
const attrs = forceAttrs(item);
|
||||
|
||||
const prefix = "prefix" in attrs ? forceStringNoCtx(attrs.prefix) : "";
|
||||
const prefix = attrs.has("prefix") ? forceStringNoCtx(attrs.get("prefix") as NixValue) : "";
|
||||
|
||||
if (!("path" in attrs)) {
|
||||
if (!attrs.has("path")) {
|
||||
throw new Error("findFile: search path element is missing 'path' attribute");
|
||||
}
|
||||
|
||||
@@ -457,7 +459,12 @@ export const findFile =
|
||||
}
|
||||
|
||||
const context: NixStringContext = new Set();
|
||||
const pathVal = coerceToString(attrs.path, StringCoercionMode.Interpolation, false, context);
|
||||
const pathVal = coerceToString(
|
||||
attrs.get("path") as NixValue,
|
||||
StringCoercionMode.Interpolation,
|
||||
false,
|
||||
context,
|
||||
);
|
||||
|
||||
if (context.size > 0) {
|
||||
throw new Error("findFile: path with string context is not yet supported");
|
||||
|
||||
@@ -95,18 +95,19 @@ export const partition =
|
||||
(list: NixValue): NixAttrs => {
|
||||
const forcedList = forceList(list);
|
||||
const forcedPred = forceFunction(pred);
|
||||
const attrs = {
|
||||
right: [] as NixList,
|
||||
wrong: [] as NixList,
|
||||
};
|
||||
const right: NixList = [];
|
||||
const wrong: NixList = [];
|
||||
for (const elem of forcedList) {
|
||||
if (force(forcedPred(elem))) {
|
||||
attrs.right.push(elem);
|
||||
right.push(elem);
|
||||
} else {
|
||||
attrs.wrong.push(elem);
|
||||
wrong.push(elem);
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
return new Map<string, NixValue>([
|
||||
["right", right],
|
||||
["wrong", wrong],
|
||||
]);
|
||||
};
|
||||
|
||||
export const genList =
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { OrderedSet } from "js-sdsl";
|
||||
import { select } from "../helpers";
|
||||
import { compareValues } from "../operators";
|
||||
import {
|
||||
getStringContext,
|
||||
@@ -15,7 +16,7 @@ import {
|
||||
forceStringNoCtx,
|
||||
forceStringValue,
|
||||
} from "../type-assert";
|
||||
import type { NixAttrs, NixBool, NixStrictValue, NixValue } from "../types";
|
||||
import type { NixAttrs, NixStrictValue, NixValue } from "../types";
|
||||
import { ATTR_POSITIONS, CatchableError } from "../types";
|
||||
import * as context from "./context";
|
||||
import { isBool, isFloat, isInt, isList, isString, typeOf } from "./type-check";
|
||||
@@ -137,24 +138,20 @@ function componentsLt(c1: string, c2: string): boolean {
|
||||
export const functionArgs = (f: NixValue): NixAttrs => {
|
||||
const func = forceFunction(f);
|
||||
if (func.args) {
|
||||
const ret: NixAttrs = {};
|
||||
const ret: NixAttrs = new Map();
|
||||
for (const key of func.args.required) {
|
||||
ret[key] = false;
|
||||
ret.set(key, false);
|
||||
}
|
||||
for (const key of func.args.optional) {
|
||||
ret[key] = true;
|
||||
ret.set(key, true);
|
||||
}
|
||||
const positions = func.args.positions;
|
||||
if (positions && Object.keys(positions).length > 0) {
|
||||
Object.defineProperty(ret, ATTR_POSITIONS, {
|
||||
value: positions,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
});
|
||||
ret[ATTR_POSITIONS] = positions;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return {};
|
||||
return new Map();
|
||||
};
|
||||
|
||||
const checkComparable = (value: NixStrictValue): void => {
|
||||
@@ -166,7 +163,8 @@ const checkComparable = (value: NixStrictValue): void => {
|
||||
|
||||
export const genericClosure = (args: NixValue): NixValue => {
|
||||
const forcedArgs = forceAttrs(args);
|
||||
const { startSet, operator } = forcedArgs;
|
||||
const startSet = select(forcedArgs, ["startSet"]);
|
||||
const operator = select(forcedArgs, ["operator"]);
|
||||
|
||||
const initialList = forceList(startSet);
|
||||
const opFunction = forceFunction(operator);
|
||||
@@ -177,7 +175,7 @@ export const genericClosure = (args: NixValue): NixValue => {
|
||||
|
||||
for (const item of initialList) {
|
||||
const itemAttrs = forceAttrs(item);
|
||||
const key = force(itemAttrs.key);
|
||||
const key = force(select(itemAttrs, ["key"]));
|
||||
checkComparable(key);
|
||||
if (resultSet.find(key).equals(resultSet.end())) {
|
||||
resultSet.insert(key);
|
||||
@@ -193,7 +191,7 @@ export const genericClosure = (args: NixValue): NixValue => {
|
||||
|
||||
for (const newItem of newItems) {
|
||||
const newItemAttrs = forceAttrs(newItem);
|
||||
const key = force(newItemAttrs.key);
|
||||
const key = force(select(newItemAttrs, ["key"]));
|
||||
checkComparable(key);
|
||||
if (resultSet.find(key).equals(resultSet.end())) {
|
||||
resultSet.insert(key);
|
||||
@@ -223,10 +221,10 @@ export const parseDrvName = (s: NixValue): NixAttrs => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
name,
|
||||
version,
|
||||
};
|
||||
return new Map<string, NixValue>([
|
||||
["name", name],
|
||||
["version", version],
|
||||
]);
|
||||
};
|
||||
|
||||
export const placeholder = (output: NixValue): NixValue => {
|
||||
@@ -322,21 +320,21 @@ export const splitVersion = (s: NixValue): NixValue => {
|
||||
|
||||
export const traceVerbose = (_e1: NixValue, e2: NixValue): NixStrictValue => {
|
||||
// TODO: implement traceVerbose
|
||||
return force(e2)
|
||||
return force(e2);
|
||||
};
|
||||
|
||||
export const tryEval = (e: NixValue): { success: NixBool; value: NixStrictValue } => {
|
||||
export const tryEval = (e: NixValue): NixAttrs => {
|
||||
try {
|
||||
return {
|
||||
success: true,
|
||||
value: force(e),
|
||||
};
|
||||
return new Map<string, NixValue>([
|
||||
["success", true],
|
||||
["value", force(e)],
|
||||
]);
|
||||
} catch (err) {
|
||||
if (err instanceof CatchableError) {
|
||||
return {
|
||||
success: false,
|
||||
value: false,
|
||||
};
|
||||
return new Map<string, NixValue>([
|
||||
["success", false],
|
||||
["value", false],
|
||||
]);
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import {
|
||||
HAS_CONTEXT,
|
||||
isNixPath,
|
||||
isStringWithContext,
|
||||
type NixAttrs,
|
||||
@@ -19,10 +18,7 @@ export const isNixString = (v: NixStrictValue): v is NixString => {
|
||||
};
|
||||
|
||||
export const isAttrs = (e: NixStrictValue): e is NixAttrs => {
|
||||
const val = e;
|
||||
return (
|
||||
typeof val === "object" && !Array.isArray(val) && val !== null && !(HAS_CONTEXT in val) && !isPath(val)
|
||||
);
|
||||
return e instanceof Map;
|
||||
};
|
||||
|
||||
export const isBool = (e: NixStrictValue): e is NixBool => typeof e === "boolean";
|
||||
@@ -60,7 +56,7 @@ export const typeOf = (e: NixStrictValue): NixType => {
|
||||
if (isNixString(e)) return "string";
|
||||
if (isNixPath(e)) return "path";
|
||||
if (Array.isArray(e)) return "list";
|
||||
if (typeof e === "object") return "set";
|
||||
if (e instanceof Map) return "set";
|
||||
if (typeof e === "function") return "lambda";
|
||||
|
||||
throw new TypeError(`Unknown Nix type: ${typeof e}`);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { isAttrs, typeOf } from "./builtins/type-check";
|
||||
import { mkPath } from "./path";
|
||||
import { isStringWithContext, mkStringWithContext, type NixStringContext } from "./string-context";
|
||||
import { force } from "./thunk";
|
||||
import { forceAttrs, forceBool, forceFunction, forceStringValue } from "./type-assert";
|
||||
import { forceAttrs, forceBool, forceFunction, forceStringNoCtx, forceStringValue } from "./type-assert";
|
||||
import type { NixAttrs, NixBool, NixPath, NixString, NixValue } from "./types";
|
||||
import { CatchableError, isNixPath } from "./types";
|
||||
|
||||
@@ -169,18 +169,18 @@ function selectImpl(obj: NixValue, attrpath: NixValue[]): NixValue {
|
||||
|
||||
for (const attr of attrpath.slice(0, -1)) {
|
||||
const key = forceStringValue(attr);
|
||||
if (!(key in attrs)) {
|
||||
if (!attrs.has(key)) {
|
||||
throw new Error(`Attribute '${key}' not found`);
|
||||
}
|
||||
const cur = forceAttrs(attrs[forceStringValue(attr)]);
|
||||
const cur = forceAttrs(attrs.get(key) as NixValue);
|
||||
attrs = cur;
|
||||
}
|
||||
|
||||
const last = forceStringValue(attrpath[attrpath.length - 1]);
|
||||
if (!(last in attrs)) {
|
||||
if (!attrs.has(last)) {
|
||||
throw new Error(`Attribute '${last}' not found`);
|
||||
}
|
||||
return attrs[last];
|
||||
return attrs.get(last) as NixValue;
|
||||
}
|
||||
|
||||
export const selectWithDefault = (
|
||||
@@ -218,10 +218,10 @@ function selectWithDefaultImpl(obj: NixValue, attrpath: NixValue[], defaultVal:
|
||||
|
||||
for (const attr of attrpath.slice(0, -1)) {
|
||||
const key = forceStringValue(attr);
|
||||
if (!(key in attrs)) {
|
||||
if (!attrs.has(key)) {
|
||||
return defaultVal;
|
||||
}
|
||||
const cur = force(attrs[key]);
|
||||
const cur = force(attrs.get(key) as NixValue);
|
||||
if (!isAttrs(cur)) {
|
||||
return defaultVal;
|
||||
}
|
||||
@@ -229,8 +229,8 @@ function selectWithDefaultImpl(obj: NixValue, attrpath: NixValue[], defaultVal:
|
||||
}
|
||||
|
||||
const last = forceStringValue(attrpath[attrpath.length - 1]);
|
||||
if (last in attrs) {
|
||||
return attrs[last];
|
||||
if (attrs.has(last)) {
|
||||
return attrs.get(last) as NixValue;
|
||||
}
|
||||
return defaultVal;
|
||||
}
|
||||
@@ -243,14 +243,18 @@ export const hasAttr = (obj: NixValue, attrpath: NixValue[]): NixBool => {
|
||||
let attrs = forced;
|
||||
|
||||
for (const attr of attrpath.slice(0, -1)) {
|
||||
const cur = force(attrs[forceStringValue(attr)]);
|
||||
const key = forceStringNoCtx(attr);
|
||||
if (!attrs.has(key)) {
|
||||
return false;
|
||||
}
|
||||
const cur = force(attrs.get(key) as NixValue);
|
||||
if (!isAttrs(cur)) {
|
||||
return false;
|
||||
}
|
||||
attrs = cur;
|
||||
}
|
||||
|
||||
return forceStringValue(attrpath[attrpath.length - 1]) in attrs;
|
||||
return attrs.has(forceStringValue(attrpath[attrpath.length - 1]));
|
||||
};
|
||||
|
||||
export const call = (func: NixValue, arg: NixValue, span?: string): NixValue => {
|
||||
@@ -277,13 +281,8 @@ function callImpl(func: NixValue, arg: NixValue): NixValue {
|
||||
forcedFunc.args?.check(arg);
|
||||
return forcedFunc(arg);
|
||||
}
|
||||
if (
|
||||
typeof forcedFunc === "object" &&
|
||||
!Array.isArray(forcedFunc) &&
|
||||
forcedFunc !== null &&
|
||||
"__functor" in forcedFunc
|
||||
) {
|
||||
const functor = forceFunction(forcedFunc.__functor);
|
||||
if (forcedFunc instanceof Map && forcedFunc.has("__functor")) {
|
||||
const functor = forceFunction(forcedFunc.get("__functor") as NixValue);
|
||||
return call(functor(forcedFunc), arg);
|
||||
}
|
||||
throw new Error(`attempt to call something which is not a function but ${typeOf(forcedFunc)}`);
|
||||
@@ -307,7 +306,7 @@ export const ifFunc = (cond: NixValue, consq: NixValue, alter: NixValue) => {
|
||||
};
|
||||
|
||||
export const mkPos = (span: string): NixAttrs => {
|
||||
return Deno.core.ops.op_decode_span(span);
|
||||
return new Map(Object.entries(Deno.core.ops.op_decode_span(span)));
|
||||
};
|
||||
|
||||
interface WithScope {
|
||||
@@ -319,8 +318,8 @@ export const lookupWith = (name: string, withScope: WithScope | null): NixValue
|
||||
let current = withScope;
|
||||
while (current !== null) {
|
||||
const attrs = forceAttrs(current.env);
|
||||
if (name in attrs) {
|
||||
return attrs[name];
|
||||
if (attrs.has(name)) {
|
||||
return attrs.get(name) as NixValue;
|
||||
}
|
||||
current = current.last;
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ import { isNixPath } from "./types";
|
||||
const canCoerceToString = (v: NixValue): boolean => {
|
||||
const forced = force(v);
|
||||
if (isNixString(forced)) return true;
|
||||
if (typeof forced === "object" && forced !== null && !Array.isArray(forced)) {
|
||||
if ("outPath" in forced || "__toString" in forced) return true;
|
||||
if (forced instanceof Map) {
|
||||
if (forced.has("outPath") || forced.has("__toString")) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -219,27 +219,24 @@ export const op = {
|
||||
const attrsA = av as NixAttrs;
|
||||
const attrsB = bv as NixAttrs;
|
||||
|
||||
// Derivation comparison: compare outPaths only
|
||||
// Safe to force 'type' because it's always a string literal, never a computed value
|
||||
if ("type" in attrsA && "type" in attrsB) {
|
||||
const typeValA = force(attrsA.type);
|
||||
const typeValB = force(attrsB.type);
|
||||
if (attrsA.has("type") && attrsB.has("type")) {
|
||||
const typeValA = force(attrsA.get("type") as NixValue);
|
||||
const typeValB = force(attrsB.get("type") as NixValue);
|
||||
if (typeValA === "derivation" && typeValB === "derivation") {
|
||||
if ("outPath" in attrsA && "outPath" in attrsB) {
|
||||
return op.eq(attrsA.outPath, attrsB.outPath);
|
||||
if (attrsA.has("outPath") && attrsB.has("outPath")) {
|
||||
return op.eq(attrsA.get("outPath") as NixValue, attrsB.get("outPath") as NixValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, compare attributes one by one
|
||||
const keysA = Object.keys(attrsA).sort();
|
||||
const keysB = Object.keys(attrsB).sort();
|
||||
const keysA = Array.from(attrsA.keys()).sort();
|
||||
const keysB = Array.from(attrsB.keys()).sort();
|
||||
|
||||
if (keysA.length !== keysB.length) return false;
|
||||
|
||||
for (let i = 0; i < keysA.length; i++) {
|
||||
if (keysA[i] !== keysB[i]) return false;
|
||||
if (!op.eq(attrsA[keysA[i]], attrsB[keysB[i]])) return false;
|
||||
if (!op.eq(attrsA.get(keysA[i]) as NixValue, attrsB.get(keysB[i]) as NixValue)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -270,5 +267,13 @@ export const op = {
|
||||
return forceList(a).concat(forceList(b));
|
||||
},
|
||||
|
||||
update: (a: NixValue, b: NixValue): NixAttrs => ({ ...forceAttrs(a), ...forceAttrs(b) }),
|
||||
update: (a: NixValue, b: NixValue): NixAttrs => {
|
||||
const mapA = forceAttrs(a);
|
||||
const mapB = forceAttrs(b);
|
||||
const result: NixAttrs = new Map(mapA);
|
||||
for (const [k, v] of mapB) {
|
||||
result.set(k, v);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -14,12 +14,11 @@ const canonicalizePath = (path: string): string => {
|
||||
const component = path.slice(i, j);
|
||||
i = j;
|
||||
|
||||
if (component === ".") {
|
||||
} else if (component === "..") {
|
||||
if (component === "..") {
|
||||
if (parts.length > 0) {
|
||||
parts.pop();
|
||||
}
|
||||
} else {
|
||||
} else if (component !== ".") {
|
||||
parts.push(component);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { isAttrs, isList } from "./builtins/type-check";
|
||||
import { HAS_CONTEXT } from "./string-context";
|
||||
import type { NixStrictValue, NixThunkInterface, NixValue } from "./types";
|
||||
import type { NixAttrs, NixStrictValue, NixThunkInterface, NixValue } from "./types";
|
||||
import { IS_PATH } from "./types";
|
||||
|
||||
export const IS_THUNK = Symbol("is_thunk");
|
||||
@@ -117,7 +117,7 @@ export const createThunk = (func: () => NixValue, label?: string): NixThunkInter
|
||||
};
|
||||
|
||||
export const IS_CYCLE = Symbol("is_cycle");
|
||||
export const CYCLE_MARKER = { [IS_CYCLE]: true };
|
||||
export const CYCLE_MARKER = { [IS_CYCLE]: true as const };
|
||||
|
||||
/**
|
||||
* Deeply force a value, handling cycles by returning a special marker.
|
||||
@@ -150,10 +150,10 @@ export const forceDeep = (value: NixValue, seen: WeakSet<object> = new WeakSet()
|
||||
return forced.map((item) => forceDeep(item, seen));
|
||||
}
|
||||
|
||||
if (typeof forced === "object") {
|
||||
const result: Record<string, NixValue> = {};
|
||||
for (const [key, val] of Object.entries(forced)) {
|
||||
result[key] = forceDeep(val, seen);
|
||||
if (forced instanceof Map) {
|
||||
const result: NixAttrs = new Map();
|
||||
for (const [key, val] of forced) {
|
||||
result.set(key, forceDeep(val, seen));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -180,10 +180,10 @@ export const forceShallow = (value: NixValue): NixStrictValue => {
|
||||
}
|
||||
|
||||
if (isAttrs(forced)) {
|
||||
const result: Record<string, NixValue> = {};
|
||||
for (const [key, val] of Object.entries(forced)) {
|
||||
const forcedVal = force(val);
|
||||
result[key] = forcedVal === forced ? CYCLE_MARKER : forcedVal;
|
||||
const result: NixAttrs = new Map();
|
||||
for (const [key, val] of forced) {
|
||||
const forcedVal = force(val as NixValue);
|
||||
result.set(key, forcedVal === forced ? CYCLE_MARKER : forcedVal);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ export const forceFunction = (value: NixValue): NixFunction => {
|
||||
if (isFunction(forced)) {
|
||||
return forced;
|
||||
}
|
||||
if (typeof forced === "object" && !Array.isArray(forced) && forced !== null && "__functor" in forced) {
|
||||
if (forced instanceof Map && forced.has("__functor")) {
|
||||
const functorSet = forced as NixAttrs;
|
||||
const functor = forceFunction(functorSet.__functor);
|
||||
const functor = forceFunction(functorSet.get("__functor") as NixValue);
|
||||
return (arg: NixValue) => forceFunction(functor(functorSet))(arg);
|
||||
}
|
||||
throw new TypeError(`Expected function, got ${typeOf(forced)}`);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { HAS_CONTEXT, isStringWithContext, type StringWithContext } from "./string-context";
|
||||
import { force, IS_THUNK } from "./thunk";
|
||||
import { type CYCLE_MARKER, force, IS_THUNK } from "./thunk";
|
||||
import { forceAttrs, forceStringNoCtx } from "./type-assert";
|
||||
export { HAS_CONTEXT, isStringWithContext };
|
||||
export type { StringWithContext };
|
||||
@@ -22,9 +22,9 @@ export type NixBool = boolean;
|
||||
export type NixString = string | StringWithContext;
|
||||
export type NixNull = null;
|
||||
|
||||
export const ATTR_POSITIONS = Symbol("attrPositions");
|
||||
export type NixList = NixValue[];
|
||||
// FIXME: reject contextful string
|
||||
export type NixAttrs = { [key: string]: NixValue };
|
||||
export type NixAttrs = Map<string, NixValue> & { [ATTR_POSITIONS]?: Record<string, string> };
|
||||
export type NixFunction = ((arg: NixValue) => NixValue) & { args?: NixArgs };
|
||||
export class NixArgs {
|
||||
required: string[];
|
||||
@@ -43,13 +43,13 @@ export class NixArgs {
|
||||
const attrs = forceAttrs(arg);
|
||||
|
||||
for (const key of this.required) {
|
||||
if (!Object.hasOwn(attrs, key)) {
|
||||
if (!attrs.has(key)) {
|
||||
throw new Error(`Function called without required argument '${key}'`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.ellipsis) {
|
||||
for (const key in attrs) {
|
||||
for (const key of attrs.keys()) {
|
||||
if (!this.allowed.has(key)) {
|
||||
throw new Error(`Function called with unexpected argument '${key}'`);
|
||||
}
|
||||
@@ -77,18 +77,18 @@ export const mkAttrs = (attrs: NixAttrs, keys: NixValue[], values: NixValue[]):
|
||||
continue;
|
||||
}
|
||||
const str = forceStringNoCtx(key);
|
||||
attrs[str] = values[i];
|
||||
attrs.set(str, values[i]);
|
||||
}
|
||||
return attrs;
|
||||
};
|
||||
|
||||
export const ATTR_POSITIONS = Symbol("attrPositions");
|
||||
|
||||
export const mkAttrsWithPos = (
|
||||
attrs: NixAttrs,
|
||||
obj: Record<string, NixValue>,
|
||||
positions: Record<string, string>,
|
||||
dyns?: { dynKeys: NixValue[]; dynVals: NixValue[]; dynSpans: string[] },
|
||||
): NixAttrs => {
|
||||
const attrs: NixAttrs = new Map(Object.entries(obj));
|
||||
|
||||
if (dyns) {
|
||||
const len = dyns.dynKeys.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
@@ -97,17 +97,13 @@ export const mkAttrsWithPos = (
|
||||
continue;
|
||||
}
|
||||
const str = forceStringNoCtx(key);
|
||||
attrs[str] = dyns.dynVals[i];
|
||||
attrs.set(str, dyns.dynVals[i]);
|
||||
positions[str] = dyns.dynSpans[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(positions).length > 0) {
|
||||
Object.defineProperty(attrs, ATTR_POSITIONS, {
|
||||
value: positions,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
});
|
||||
attrs[ATTR_POSITIONS] = positions;
|
||||
}
|
||||
|
||||
return attrs;
|
||||
@@ -120,7 +116,14 @@ export interface NixThunkInterface {
|
||||
}
|
||||
|
||||
export type NixPrimitive = NixNull | NixBool | NixInt | NixFloat | NixString;
|
||||
export type NixValue = NixPrimitive | NixPath | NixList | NixAttrs | NixFunction | NixThunkInterface;
|
||||
export type NixValue =
|
||||
| NixPrimitive
|
||||
| NixPath
|
||||
| NixList
|
||||
| NixAttrs
|
||||
| NixFunction
|
||||
| NixThunkInterface
|
||||
| typeof CYCLE_MARKER;
|
||||
export type NixStrictValue = Exclude<NixValue, NixThunkInterface>;
|
||||
|
||||
export class CatchableError extends Error {}
|
||||
|
||||
@@ -263,9 +263,9 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||
}
|
||||
&Ir::Builtin(Builtin { inner: name, .. }) => {
|
||||
code!(buf, ctx;
|
||||
"Nix.builtins["
|
||||
"Nix.builtins.get("
|
||||
ctx.get_sym(name)
|
||||
"]"
|
||||
")"
|
||||
);
|
||||
}
|
||||
Ir::ConcatStrings(x) => x.compile(ctx, buf),
|
||||
@@ -309,9 +309,9 @@ impl<Ctx: CodegenContext> Compile<Ctx> for Ir {
|
||||
}
|
||||
&Ir::ScopedImportBinding(ScopedImportBinding { inner: name, .. }) => {
|
||||
code!(buf, ctx;
|
||||
"__scope["
|
||||
"__scope.get("
|
||||
ctx.get_sym(name)
|
||||
"]"
|
||||
")"
|
||||
);
|
||||
}
|
||||
Ir::WithExpr(x) => x.compile(ctx, buf),
|
||||
@@ -632,7 +632,7 @@ impl<Ctx: CodegenContext> Compile<Ctx> for AttrSet {
|
||||
"})"
|
||||
);
|
||||
} else {
|
||||
code!(buf, ctx; "{}");
|
||||
code!(buf, ctx; "new Map()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ impl Context {
|
||||
let code = self.ctx.compile(source, None)?;
|
||||
self.runtime.eval(
|
||||
format!(
|
||||
"Nix.builtins.derivation = {};Nix.builtins.storeDir=\"{}\"",
|
||||
"Nix.builtins.set('derivation',({}));Nix.builtins.set('storeDir','{}')",
|
||||
code,
|
||||
self.get_store_dir()
|
||||
),
|
||||
|
||||
@@ -364,6 +364,28 @@ fn to_value<'a>(
|
||||
Value::Func
|
||||
}
|
||||
}
|
||||
_ if val.is_map() => {
|
||||
let val = val.try_cast::<v8::Map>().expect("infallible conversion");
|
||||
let size = val.size() as u32;
|
||||
let array = val.as_array(scope);
|
||||
let attrs = (0..size)
|
||||
.map(|i| {
|
||||
let key = array.get_index(scope, i * 2).expect("infallible index operation");
|
||||
let key = key.to_rust_string_lossy(scope);
|
||||
let val = array.get_index(scope, i * 2 + 1).expect("infallible index operation");
|
||||
let val = to_value(
|
||||
val,
|
||||
scope,
|
||||
is_thunk_symbol,
|
||||
primop_metadata_symbol,
|
||||
has_context_symbol,
|
||||
is_path_symbol,
|
||||
is_cycle_symbol,
|
||||
);
|
||||
(Symbol::new(Cow::Owned(key)), val)
|
||||
}).collect();
|
||||
Value::AttrSet(AttrSet::new(attrs))
|
||||
}
|
||||
_ if val.is_object() => {
|
||||
if is_thunk(val, scope, is_thunk_symbol) {
|
||||
return Value::Thunk;
|
||||
|
||||
@@ -745,10 +745,10 @@ impl<'a> deno_core::convert::ToV8<'a> for NixJsonValue {
|
||||
scope: &mut v8::PinScope<'a, 'i>,
|
||||
) -> std::result::Result<v8::Local<'a, v8::Value>, Self::Error> {
|
||||
Ok(match self {
|
||||
Self::Null => v8::null(scope).into() ,
|
||||
Self::Bool(b) => v8::Boolean::new(scope, b).into() ,
|
||||
Self::Int(i) => v8::BigInt::new_from_i64(scope, i).into() ,
|
||||
Self::Float(f) => v8::Number::new(scope, f).into() ,
|
||||
Self::Null => v8::null(scope).into(),
|
||||
Self::Bool(b) => v8::Boolean::new(scope, b).into(),
|
||||
Self::Int(i) => v8::BigInt::new_from_i64(scope, i).into(),
|
||||
Self::Float(f) => v8::Number::new(scope, f).into(),
|
||||
Self::Str(s) => v8::String::new(scope, &s)
|
||||
.map(|s| s.into())
|
||||
.ok_or("failed to create v8 string")?,
|
||||
@@ -757,18 +757,18 @@ impl<'a> deno_core::convert::ToV8<'a> for NixJsonValue {
|
||||
.into_iter()
|
||||
.map(|v| v.to_v8(scope))
|
||||
.collect::<std::result::Result<Vec<_>, _>>()?;
|
||||
v8::Array::new_with_elements(scope, &elements).into()
|
||||
v8::Array::new_with_elements(scope, &elements).into()
|
||||
}
|
||||
Self::Obj(entries) => {
|
||||
let obj = v8::Object::new(scope);
|
||||
let map = v8::Map::new(scope);
|
||||
for (k, v) in entries {
|
||||
let key: v8::Local<v8::Value> = v8::String::new(scope, &k)
|
||||
.ok_or("failed to create v8 string")?
|
||||
.into();
|
||||
let val = v.to_v8(scope)?;
|
||||
obj.set(scope, key, val);
|
||||
map.set(scope, key, val);
|
||||
}
|
||||
obj.into()
|
||||
map.into()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1227,19 +1227,19 @@ impl<'s> XmlCtx<'s> {
|
||||
nix_obj: v8::Local<'s, v8::Object>,
|
||||
) -> Result<Self> {
|
||||
let get_fn = |scope: &v8::PinScope<'s, 'i>, name: &str| {
|
||||
let key = v8::String::new(scope, name).ok_or("v8 string" )?;
|
||||
let key = v8::String::new(scope, name).ok_or("v8 string")?;
|
||||
let val = nix_obj
|
||||
.get(scope, key.into())
|
||||
.ok_or_else(|| format!("no {name}") )?;
|
||||
.ok_or_else(|| format!("no {name}"))?;
|
||||
v8::Local::<v8::Function>::try_from(val)
|
||||
.map_err(|e| format!("{name} not function: {e}") )
|
||||
.map_err(|e| format!("{name} not function: {e}"))
|
||||
};
|
||||
let get_sym = |scope: &v8::PinScope<'s, 'i>, name: &str| {
|
||||
let key = v8::String::new(scope, name).ok_or("v8 string" )?;
|
||||
let key = v8::String::new(scope, name).ok_or("v8 string")?;
|
||||
let val = nix_obj
|
||||
.get(scope, key.into())
|
||||
.ok_or_else(|| format!("no {name}") )?;
|
||||
v8::Local::<v8::Symbol>::try_from(val).map_err(|e| format!("{name} not symbol: {e}") )
|
||||
.ok_or_else(|| format!("no {name}"))?;
|
||||
v8::Local::<v8::Symbol>::try_from(val).map_err(|e| format!("{name} not symbol: {e}"))
|
||||
};
|
||||
Ok(Self {
|
||||
force_fn: get_fn(scope, "force")?,
|
||||
@@ -1378,12 +1378,12 @@ impl XmlWriter {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_derivation<'s>(obj: v8::Local<'s, v8::Object>, scope: &mut v8::PinScope<'s, '_>) -> bool {
|
||||
fn is_derivation<'s>(map: v8::Local<'s, v8::Map>, scope: &mut v8::PinScope<'s, '_>) -> bool {
|
||||
let key = match v8::String::new(scope, "type") {
|
||||
Some(k) => k,
|
||||
None => return false,
|
||||
};
|
||||
match obj.get(scope, key.into()) {
|
||||
match map.get(scope, key.into()) {
|
||||
Some(v) if v.is_string() => v.to_rust_string_lossy(scope) == "derivation",
|
||||
_ => false,
|
||||
}
|
||||
@@ -1414,7 +1414,7 @@ impl XmlWriter {
|
||||
return Ok(());
|
||||
}
|
||||
if val.is_big_int() {
|
||||
let bi = val.to_big_int(scope).ok_or("bigint" )?;
|
||||
let bi = val.to_big_int(scope).ok_or("bigint")?;
|
||||
let (i, _) = bi.i64_value();
|
||||
self.indent(depth);
|
||||
self.buf.push_str("<int value=\"");
|
||||
@@ -1423,7 +1423,7 @@ impl XmlWriter {
|
||||
return Ok(());
|
||||
}
|
||||
if val.is_number() {
|
||||
let n = val.number_value(scope).ok_or("number" )?;
|
||||
let n = val.number_value(scope).ok_or("number")?;
|
||||
self.indent(depth);
|
||||
self.buf.push_str("<float value=\"");
|
||||
self.buf.push_str(&format!("{}", n));
|
||||
@@ -1439,11 +1439,11 @@ impl XmlWriter {
|
||||
return Ok(());
|
||||
}
|
||||
if val.is_array() {
|
||||
let arr = v8::Local::<v8::Array>::try_from(val).map_err(|e| e.to_string() )?;
|
||||
let arr = v8::Local::<v8::Array>::try_from(val).map_err(|e| e.to_string())?;
|
||||
self.indent(depth);
|
||||
self.buf.push_str("<list>\n");
|
||||
for i in 0..arr.length() {
|
||||
let elem = arr.get_index(scope, i).ok_or("array elem" )?;
|
||||
let elem = arr.get_index(scope, i).ok_or("array elem")?;
|
||||
self.write_value(elem, scope, ctx, depth + 1)?;
|
||||
}
|
||||
self.indent(depth);
|
||||
@@ -1453,14 +1453,18 @@ impl XmlWriter {
|
||||
if val.is_function() {
|
||||
return self.write_function(val, scope, ctx, depth);
|
||||
}
|
||||
if val.is_map() {
|
||||
let map = v8::Local::<v8::Map>::try_from(val).map_err(|e| e.to_string())?;
|
||||
return self.write_attrs(map, scope, ctx, depth);
|
||||
}
|
||||
if val.is_object() {
|
||||
let obj = val.to_object(scope).ok_or("to_object" )?;
|
||||
let obj = val.to_object(scope).ok_or("to_object")?;
|
||||
|
||||
if Self::has_sym(obj, scope, ctx.has_context) {
|
||||
let key = v8::String::new(scope, "value").ok_or("v8 str" )?;
|
||||
let key = v8::String::new(scope, "value").ok_or("v8 str")?;
|
||||
let s = obj
|
||||
.get(scope, key.into())
|
||||
.ok_or("value" )?
|
||||
.ok_or("value")?
|
||||
.to_rust_string_lossy(scope);
|
||||
self.collect_context(obj, scope);
|
||||
self.indent(depth);
|
||||
@@ -1470,10 +1474,10 @@ impl XmlWriter {
|
||||
return Ok(());
|
||||
}
|
||||
if Self::has_sym(obj, scope, ctx.is_path) {
|
||||
let key = v8::String::new(scope, "value").ok_or("v8 str" )?;
|
||||
let key = v8::String::new(scope, "value").ok_or("v8 str")?;
|
||||
let s = obj
|
||||
.get(scope, key.into())
|
||||
.ok_or("value" )?
|
||||
.ok_or("value")?
|
||||
.to_rust_string_lossy(scope);
|
||||
self.indent(depth);
|
||||
self.buf.push_str("<path value=\"");
|
||||
@@ -1486,8 +1490,6 @@ impl XmlWriter {
|
||||
self.buf.push_str("<unevaluated />\n");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
return self.write_attrs(obj, scope, ctx, depth);
|
||||
}
|
||||
|
||||
self.indent(depth);
|
||||
@@ -1497,31 +1499,39 @@ impl XmlWriter {
|
||||
|
||||
fn write_attrs<'s>(
|
||||
&mut self,
|
||||
obj: v8::Local<'s, v8::Object>,
|
||||
map: v8::Local<'s, v8::Map>,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
ctx: &XmlCtx<'s>,
|
||||
depth: usize,
|
||||
) -> Result<()> {
|
||||
if Self::is_derivation(obj, scope) {
|
||||
if Self::is_derivation(map, scope) {
|
||||
self.indent(depth);
|
||||
self.buf.push_str("<derivation");
|
||||
|
||||
let drv_path_key = v8::String::new(scope, "drvPath").ok_or("v8 str" )?;
|
||||
let drv_str = if let Some(drv_val) = obj.get(scope, drv_path_key.into()) {
|
||||
let forced = self.force(drv_val, scope, ctx)?;
|
||||
let s = self.extract_str(forced, scope, ctx);
|
||||
if let Some(ref s) = s {
|
||||
self.buf.push_str(" drvPath=\"");
|
||||
self.escape_attr(s);
|
||||
self.buf.push('"');
|
||||
let drv_path_key: v8::Local<v8::Value> =
|
||||
v8::String::new(scope, "drvPath").ok_or("v8 str")?.into();
|
||||
let drv_str = if let Some(drv_val) = map.get(scope, drv_path_key) {
|
||||
if drv_val.is_undefined() {
|
||||
None
|
||||
} else {
|
||||
let forced = self.force(drv_val, scope, ctx)?;
|
||||
let s = self.extract_str(forced, scope, ctx);
|
||||
if let Some(ref s) = s {
|
||||
self.buf.push_str(" drvPath=\"");
|
||||
self.escape_attr(s);
|
||||
self.buf.push('"');
|
||||
}
|
||||
s
|
||||
}
|
||||
s
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let out_path_key = v8::String::new(scope, "outPath").ok_or("v8 str" )?;
|
||||
if let Some(out_val) = obj.get(scope, out_path_key.into()) {
|
||||
let out_path_key: v8::Local<v8::Value> =
|
||||
v8::String::new(scope, "outPath").ok_or("v8 str")?.into();
|
||||
if let Some(out_val) = map.get(scope, out_path_key)
|
||||
&& !out_val.is_undefined()
|
||||
{
|
||||
let forced = self.force(out_val, scope, ctx)?;
|
||||
if let Some(ref s) = self.extract_str(forced, scope, ctx) {
|
||||
self.buf.push_str(" outPath=\"");
|
||||
@@ -1542,7 +1552,7 @@ impl XmlWriter {
|
||||
self.indent(depth + 1);
|
||||
self.buf.push_str("<repeated />\n");
|
||||
} else {
|
||||
self.write_attrs_sorted(obj, scope, ctx, depth + 1)?;
|
||||
self.write_attrs_sorted(map, scope, ctx, depth + 1)?;
|
||||
}
|
||||
|
||||
self.indent(depth);
|
||||
@@ -1550,7 +1560,7 @@ impl XmlWriter {
|
||||
} else {
|
||||
self.indent(depth);
|
||||
self.buf.push_str("<attrs>\n");
|
||||
self.write_attrs_sorted(obj, scope, ctx, depth + 1)?;
|
||||
self.write_attrs_sorted(map, scope, ctx, depth + 1)?;
|
||||
self.indent(depth);
|
||||
self.buf.push_str("</attrs>\n");
|
||||
}
|
||||
@@ -1559,34 +1569,32 @@ impl XmlWriter {
|
||||
|
||||
fn write_attrs_sorted<'s>(
|
||||
&mut self,
|
||||
obj: v8::Local<'s, v8::Object>,
|
||||
map: v8::Local<'s, v8::Map>,
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
ctx: &XmlCtx<'s>,
|
||||
depth: usize,
|
||||
) -> Result<()> {
|
||||
let keys = obj
|
||||
.get_own_property_names(scope, v8::GetPropertyNamesArgsBuilder::new().build())
|
||||
.ok_or("property names" )?;
|
||||
let arr = map.as_array(scope);
|
||||
let len = arr.length();
|
||||
|
||||
let mut key_strings: Vec<String> = Vec::with_capacity(keys.length() as usize);
|
||||
for i in 0..keys.length() {
|
||||
let key = keys.get_index(scope, i).ok_or("key index" )?;
|
||||
key_strings.push(key.to_rust_string_lossy(scope));
|
||||
let mut entries: Vec<(String, v8::Local<'s, v8::Value>)> =
|
||||
Vec::with_capacity((len / 2) as usize);
|
||||
let mut i = 0;
|
||||
while i < len {
|
||||
let key = arr.get_index(scope, i).ok_or("map key")?;
|
||||
let val = arr.get_index(scope, i + 1).ok_or("map value")?;
|
||||
entries.push((key.to_rust_string_lossy(scope), val));
|
||||
i += 2;
|
||||
}
|
||||
key_strings.sort();
|
||||
|
||||
for key_str in &key_strings {
|
||||
let v8_key = v8::String::new(scope, key_str).ok_or("v8 str" )?;
|
||||
let val = obj
|
||||
.get(scope, v8_key.into())
|
||||
.ok_or("attr value" )?;
|
||||
entries.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
for (key_str, val) in &entries {
|
||||
self.indent(depth);
|
||||
self.buf.push_str("<attr name=\"");
|
||||
self.escape_attr(key_str);
|
||||
self.buf.push_str("\">\n");
|
||||
|
||||
self.write_value(val, scope, ctx, depth + 1)?;
|
||||
self.write_value(*val, scope, ctx, depth + 1)?;
|
||||
|
||||
self.indent(depth);
|
||||
self.buf.push_str("</attr>\n");
|
||||
@@ -1601,7 +1609,7 @@ impl XmlWriter {
|
||||
ctx: &XmlCtx<'s>,
|
||||
depth: usize,
|
||||
) -> Result<()> {
|
||||
let obj = val.to_object(scope).ok_or("fn to_object" )?;
|
||||
let obj = val.to_object(scope).ok_or("fn to_object")?;
|
||||
|
||||
if let Some(meta) = obj.get(scope, ctx.primop_meta.into())
|
||||
&& meta.is_object()
|
||||
@@ -1612,16 +1620,16 @@ impl XmlWriter {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let args_key = v8::String::new(scope, "args").ok_or("v8 str" )?;
|
||||
let args_key = v8::String::new(scope, "args").ok_or("v8 str")?;
|
||||
let args_val = obj.get(scope, args_key.into());
|
||||
|
||||
match args_val {
|
||||
Some(args) if args.is_object() && !args.is_null_or_undefined() => {
|
||||
let args_obj = args.to_object(scope).ok_or("args to_object" )?;
|
||||
let args_obj = args.to_object(scope).ok_or("args to_object")?;
|
||||
|
||||
let req_key = v8::String::new(scope, "required").ok_or("v8 str" )?;
|
||||
let opt_key = v8::String::new(scope, "optional").ok_or("v8 str" )?;
|
||||
let ellipsis_key = v8::String::new(scope, "ellipsis").ok_or("v8 str" )?;
|
||||
let req_key = v8::String::new(scope, "required").ok_or("v8 str")?;
|
||||
let opt_key = v8::String::new(scope, "optional").ok_or("v8 str")?;
|
||||
let ellipsis_key = v8::String::new(scope, "ellipsis").ok_or("v8 str")?;
|
||||
|
||||
let mut all_formals: Vec<String> = Vec::new();
|
||||
if let Some(req) = args_obj.get(scope, req_key.into())
|
||||
@@ -1721,12 +1729,12 @@ impl<'a> FromV8<'a> for ToXmlResult {
|
||||
value: v8::Local<'a, v8::Value>,
|
||||
) -> std::result::Result<Self, Self::Error> {
|
||||
let global = scope.get_current_context().global(scope);
|
||||
let nix_key = v8::String::new(scope, "Nix").ok_or("v8 string" )?;
|
||||
let nix_key = v8::String::new(scope, "Nix").ok_or("v8 string")?;
|
||||
let nix_obj = global
|
||||
.get(scope, nix_key.into())
|
||||
.ok_or("no Nix global" )?
|
||||
.ok_or("no Nix global")?
|
||||
.to_object(scope)
|
||||
.ok_or("Nix not object" )?;
|
||||
.ok_or("Nix not object")?;
|
||||
|
||||
let ctx = XmlCtx::new(scope, nix_obj)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user