« get me outta code hell

reverse.js « src - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src/reverse.js
blob: 9cab5ef802f616338b4db51bfc0ea615c797711d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import * as fr from './find-reverse.js';

import {sortByDate} from '#sort';
import {stitchArrays} from '#sugar';

function reverseHelper(spec) {
  const cache = new WeakMap();

  return (thing, data) => {
    // Check for an existing cache record which corresponds to this data.
    // If it exists, query it for the requested thing, and return that;
    // if it doesn't, create it and put it where it needs to be.

    if (cache.has(data)) {
      return cache.get(data).get(thing) ?? [];
    }

    const cacheRecord = new WeakMap();
    cache.set(data, cacheRecord);

    // Get the referencing and referenced things. This is the meat of how
    // one reverse spec is different from another.

    const referencingThings =
      data.flatMap(thing => spec.referencing(thing));

    const referencedThings =
      referencingThings.map(thing => spec.referenced(thing));

    // Actually fill in the cache record. Since we're building up a *reverse*
    // reference list, track connections in terms of the referenced thing.
    // Also gather all referenced things into a set, for sorting purposes.

    const allReferencedThings = new Set();

    stitchArrays({
      referencingThing: referencingThings,
      referencedThings: referencedThings,
    }).forEach(({referencingThing, referencedThings}) => {
        for (const referencedThing of referencedThings) {
          if (cacheRecord.has(referencedThing)) {
            cacheRecord.get(referencedThing).push(referencingThing);
          } else {
            cacheRecord.set(referencedThing, [referencingThing]);
            allReferencedThings.add(referencedThing);
          }
        }
      });

    // Sort the entries in the cache records, too, just by date. The rest of
    // sorting should be handled externally - either preceding the reverse
    // call (changing the data input) or following (sorting the output).

    for (const referencedThing of allReferencedThings) {
      if (cacheRecord.has(referencedThing)) {
        const referencingThings = cacheRecord.get(referencedThing);
        sortByDate(referencingThings);
      }
    }

    // Then just pluck out the requested thing from the now-filled
    // cache record!

    return cacheRecord.get(thing) ?? [];
  };
}

const hardcodedReverseSpecs = {};

const findReverseHelperConfig = {
  word: `reverse`,
  constructorKey: Symbol.for('Thing.reverseSpecs'),

  hardcodedSpecs: hardcodedReverseSpecs,
  postprocessSpec: postprocessReverseSpec,
};

export function postprocessReverseSpec(spec, {thingConstructor}) {
  const newSpec = {...spec};

  void thingConstructor;

  return newSpec;
}

export function getAllReverseSpecs() {
  return fr.getAllSpecs(findReverseHelperConfig);
}

export function findReverseSpec(key) {
  return fr.findSpec(key, findReverseHelperConfig);
}

export default fr.tokenProxy({
  findSpec: findReverseSpec,
  prepareBehavior: reverseHelper,
});

export function bindReverse(wikiData, opts) {
  return fr.bind(wikiData, opts, {
    getAllSpecs: getAllReverseSpecs,
    prepareBehavior: reverseHelper,
  });
}