« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data/composite.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/composite.js')
-rw-r--r--src/data/composite.js175
1 files changed, 168 insertions, 7 deletions
diff --git a/src/data/composite.js b/src/data/composite.js
index 33d69a68..ea7a3480 100644
--- a/src/data/composite.js
+++ b/src/data/composite.js
@@ -758,6 +758,9 @@ export function compositeFrom(description) {
     anyStepsUseUpdateValue ||
     anyStepsUpdate;
 
+  const stepsFirstTimeCalling =
+    Array.from({length: steps.length}).fill(true);
+
   const stepEntries = stitchArrays({
     step: steps,
     stepComposes: stepsCompose,
@@ -977,8 +980,16 @@ export function compositeFrom(description) {
           (expectingTransform
             ? {[input.updateValue()]: valueSoFar}
             : {}),
-        [input.myself()]: initialDependencies?.['this'] ?? null,
-        [input.thisProperty()]: initialDependencies?.['thisProperty'] ?? null,
+
+        [input.myself()]:
+          (initialDependencies && Object.hasOwn(initialDependencies, 'this')
+            ? initialDependencies.this
+            : null),
+
+        [input.thisProperty()]:
+          (initialDependencies && Object.hasOwn(initialDependencies, 'thisProperty')
+            ? initialDependencies.thisProperty
+            : null),
       };
 
       const selectDependencies =
@@ -1028,7 +1039,123 @@ export function compositeFrom(description) {
       const naturalEvaluate = () => {
         const [name, ...argsLayout] = getExpectedEvaluation();
 
-        let args;
+        let args = argsLayout;
+
+        let effectiveDependencies;
+        let reviewAccessedDependencies;
+
+        if (stepsFirstTimeCalling[i]) {
+          const expressedDependencies =
+            selectDependencies;
+
+          const remainingDependencies =
+            new Set(expressedDependencies);
+
+          const unavailableDependencies = [];
+          const accessedDependencies = [];
+
+          effectiveDependencies =
+            new Proxy(filteredDependencies, {
+              get(target, key) {
+                accessedDependencies.push(key);
+                remainingDependencies.delete(key);
+
+                const value = target[key];
+
+                if (value === undefined) {
+                  unavailableDependencies.push(key);
+                }
+
+                return value;
+              },
+            });
+
+          reviewAccessedDependencies = () => {
+            const topAggregate =
+              openAggregate({
+                message: `Errors in accessed dependencies`,
+              });
+
+            const showDependency = dependency =>
+              (isInputToken(dependency)
+                ? getInputTokenShape(dependency) +
+                  `(` +
+                  inspect(getInputTokenValue(dependency), {compact: true}) +
+                  ')'
+                : dependency.toString());
+
+            let anyErrors = false;
+
+            for (const dependency of remainingDependencies) {
+              topAggregate.push(new Error(
+                `Expected to access ${showDependency(dependency)}`));
+
+              anyErrors = true;
+            }
+
+            for (const dependency of unavailableDependencies) {
+              const subAggregate =
+                openAggregate({
+                  message:
+                    `Accessed ${showDependency(dependency)}, which is unavailable`,
+                });
+
+              let reason = false;
+
+              if (!expressedDependencies.includes(dependency)) {
+                subAggregate.push(new Error(
+                  `Missing from step's expressed dependencies`));
+                reason = true;
+              }
+
+              if (filterableDependencies[dependency] === undefined) {
+                subAggregate.push(
+                  new Error(
+                    `Not available` +
+                    (isInputToken(dependency)
+                      ? ` in input()-type dependencies`
+                   : dependency.startsWith('#')
+                      ? ` in local dependencies`
+                      : ` on object dependencies`)));
+                reason = true;
+              }
+
+              if (!reason) {
+                subAggregate.push(new Error(
+                  `Not sure why this is unavailable, sorry!`));
+              }
+
+              topAggregate.call(subAggregate.close);
+
+              anyErrors = true;
+            }
+
+            if (anyErrors) {
+              topAggregate.push(new Error(
+                `These dependencies, in total, were accessed:` +
+                (empty(accessedDependencies)
+                  ? ` (none)`
+               : accessedDependencies.length === 1
+                  ? showDependency(accessedDependencies[0])
+                  : `\n` +
+                    accessedDependencies
+                      .map(showDependency)
+                      .map(line => `  - ${line}`)
+                      .join('\n'))));
+            }
+
+            topAggregate.close();
+          };
+        } else {
+          effectiveDependencies = filteredDependencies;
+          reviewAccessedDependencies = null;
+        }
+
+        args =
+          args.map(arg =>
+            (arg === filteredDependencies
+              ? effectiveDependencies
+              : arg));
 
         if (stepComposes) {
           let continuation;
@@ -1037,17 +1164,50 @@ export function compositeFrom(description) {
             _prepareContinuation(callingTransformForThisStep));
 
           args =
-            argsLayout.map(arg =>
+            args.map(arg =>
               (arg === continuationSymbol
                 ? continuation
                 : arg));
         } else {
           args =
-            argsLayout.filter(arg => arg !== continuationSymbol);
+            args.filter(arg => arg !== continuationSymbol);
         }
 
-        return expose[name](...args);
-      }
+        let stepError;
+        try {
+          return expose[name](...args);
+        } catch (error) {
+          stepError = error;
+        } finally {
+          stepsFirstTimeCalling[i] = false;
+
+          let reviewError;
+          if (reviewAccessedDependencies) {
+            try {
+              reviewAccessedDependencies();
+            } catch (error) {
+              reviewError = error;
+            }
+          }
+
+          const stepPart =
+            `step ${i+1}` +
+            (isBase
+              ? ` (base)`
+              : ` of ${steps.length}`) +
+            (step.annotation ? `, ${step.annotation}` : ``);
+
+          if (stepError && reviewError) {
+            throw new AggregateError(
+              [stepError, reviewError],
+              `Errors in ${stepPart}`);
+          } else if (stepError || reviewError) {
+            throw new Error(
+              `Error in ${stepPart}`,
+              {cause: stepError || reviewError});
+          }
+        }
+      };
 
       switch (step.cache) {
         // Warning! Highly WIP!
@@ -1223,6 +1383,7 @@ export function compositeFrom(description) {
           `Error computing composition` +
           (annotation ? ` ${annotation}` : ''));
         error.cause = thrownError;
+        error[Symbol.for('hsmusic.aggregate.translucent')] = true;
         throw error;
       }
     };