« get me outta code hell

data: move composite utilities related to wiki data into thing.js - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/data/things/thing.js
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2023-09-07 09:55:29 -0300
committer(quasar) nebula <qznebula@protonmail.com>2023-09-07 09:55:29 -0300
commitc86de8a2be3867c14ca92c8e6799fd9b325305ec (patch)
treeae4c6d4662bb35fe52e2e881954a76df37f14595 /src/data/things/thing.js
parent78d293d5f4eea7ed6ee6f3cddd3ffcf73c5056a0 (diff)
data: move composite utilities related to wiki data into thing.js
Diffstat (limited to 'src/data/things/thing.js')
-rw-r--r--src/data/things/thing.js166
1 files changed, 160 insertions, 6 deletions
diff --git a/src/data/things/thing.js b/src/data/things/thing.js
index 0716931a..1077a652 100644
--- a/src/data/things/thing.js
+++ b/src/data/things/thing.js
@@ -5,16 +5,14 @@ import {inspect} from 'node:util';
 
 import {color} from '#cli';
 import find from '#find';
-import {empty} from '#sugar';
-import {getKebabCase} from '#wiki-data';
+import {empty, stitchArrays} from '#sugar';
+import {filterMultipleArrays, getKebabCase} from '#wiki-data';
 
 import {
   from as compositeFrom,
+  earlyExitWithoutDependency,
   exposeDependency,
-  withReverseReferenceList,
-  withResolvedContribs,
-  withResolvedReference,
-  withResolvedReferenceList,
+  raiseWithoutDependency,
 } from '#composite';
 
 import {
@@ -332,3 +330,159 @@ export default class Thing extends CacheableObject {
     return `${thing.constructor[Thing.referenceType]}:${thing.directory}`;
   }
 }
+
+// Resolves the contribsByRef contained in the provided dependency,
+// providing (named by the second argument) the result. "Resolving"
+// means mapping the "who" reference of each contribution to an artist
+// object, and filtering out those whose "who" doesn't match any artist.
+export function withResolvedContribs({from, to}) {
+  return compositeFrom(`withResolvedContribs`, [
+    raiseWithoutDependency(from, {
+      mode: 'empty',
+      map: {to},
+      raise: {to: []},
+    }),
+
+    {
+      mapDependencies: {from},
+      compute: ({from}, continuation) =>
+        continuation({
+          '#whoByRef': from.map(({who}) => who),
+          '#what': from.map(({what}) => what),
+        }),
+    },
+
+    withResolvedReferenceList({
+      list: '#whoByRef',
+      data: 'artistData',
+      to: '#who',
+      find: find.artist,
+      notFoundMode: 'null',
+    }),
+
+    {
+      dependencies: ['#who', '#what'],
+      mapContinuation: {to},
+      compute({'#who': who, '#what': what}, continuation) {
+        filterMultipleArrays(who, what, (who, _what) => who);
+        return continuation({
+          to: stitchArrays({who, what}),
+        });
+      },
+    },
+  ]);
+}
+
+// Resolves a reference by using the provided find function to match it
+// within the provided thingData dependency. This will early exit if the
+// data dependency is null, or, if earlyExitIfNotFound is set to true,
+// if the find function doesn't match anything for the reference.
+// Otherwise, the data object is provided on the output dependency;
+// or null, if the reference doesn't match anything or itself was null
+// to begin with.
+export function withResolvedReference({
+  ref,
+  data,
+  find: findFunction,
+  to = '#resolvedReference',
+  earlyExitIfNotFound = false,
+}) {
+  return compositeFrom(`withResolvedReference`, [
+    raiseWithoutDependency(ref, {map: {to}, raise: {to: null}}),
+    earlyExitWithoutDependency(data),
+
+    {
+      options: {findFunction, earlyExitIfNotFound},
+      mapDependencies: {ref, data},
+      mapContinuation: {match: to},
+
+      compute({ref, data, '#options': {findFunction, earlyExitIfNotFound}}, continuation) {
+        const match = findFunction(ref, data, {mode: 'quiet'});
+
+        if (match === null && earlyExitIfNotFound) {
+          return continuation.exit(null);
+        }
+
+        return continuation.raise({match});
+      },
+    },
+  ]);
+}
+
+// Resolves a list of references, with each reference matched with provided
+// data in the same way as withResolvedReference. This will early exit if the
+// data dependency is null (even if the reference list is empty). By default
+// it will filter out references which don't match, but this can be changed
+// to early exit ({notFoundMode: 'exit'}) or leave null in place ('null').
+export function withResolvedReferenceList({
+  list,
+  data,
+  find: findFunction,
+  to = '#resolvedReferenceList',
+  notFoundMode = 'filter',
+}) {
+  if (!['filter', 'exit', 'null'].includes(notFoundMode)) {
+    throw new TypeError(`Expected notFoundMode to be filter, exit, or null`);
+  }
+
+  return compositeFrom(`withResolvedReferenceList`, [
+    earlyExitWithoutDependency(data, {value: []}),
+
+    raiseWithoutDependency(list, {
+      map: {to},
+      raise: {to: []},
+      mode: 'empty',
+    }),
+
+    {
+      options: {findFunction, notFoundMode},
+      mapDependencies: {list, data},
+      mapContinuation: {matches: to},
+
+      compute({list, data, '#options': {findFunction, notFoundMode}}, continuation) {
+        let matches =
+          list.map(ref => findFunction(ref, data, {mode: 'quiet'}));
+
+        if (!matches.includes(null)) {
+          return continuation.raise({matches});
+        }
+
+        switch (notFoundMode) {
+          case 'filter':
+            matches = matches.filter(value => value !== null);
+            return continuation.raise({matches});
+
+          case 'exit':
+            return continuation.exit([]);
+
+          case 'null':
+            return continuation.raise({matches});
+        }
+      },
+    },
+  ]);
+}
+
+// Check out the info on Thing.common.reverseReferenceList!
+// This is its composable form.
+export function withReverseReferenceList({
+  data,
+  list: refListProperty,
+  to = '#reverseReferenceList',
+}) {
+  return compositeFrom(`Thing.common.reverseReferenceList`, [
+    earlyExitWithoutDependency(data, {value: []}),
+
+    {
+      dependencies: ['this'],
+      mapDependencies: {data},
+      mapContinuation: {to},
+      options: {refListProperty},
+
+      compute: ({this: thisThing, data, '#options': {refListProperty}}, continuation) =>
+        continuation({
+          to: data.filter(thing => thing[refListProperty].includes(thisThing)),
+        }),
+    },
+  ]);
+}