« get me outta code hell

data: SortingRule: multiple rules, one file (hopefully) - hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
author(quasar) nebula <qznebula@protonmail.com>2025-02-22 20:35:10 -0400
committer(quasar) nebula <qznebula@protonmail.com>2025-02-22 21:12:25 -0400
commit2d836f4c2ab3895d675b5d8a93e36922e38fbf35 (patch)
treefda5e4badbbe9a4e2f83217f2892a5df8c630fb2 /src
parente0bf4a7ae3a6ec2f2278f0a62efac45466397803 (diff)
data: SortingRule: multiple rules, one file (hopefully)
Diffstat (limited to 'src')
-rw-r--r--src/data/things/sorting-rule.js96
-rw-r--r--src/write/build-modes/sort.js19
2 files changed, 97 insertions, 18 deletions
diff --git a/src/data/things/sorting-rule.js b/src/data/things/sorting-rule.js
index d21781e2..a2937157 100644
--- a/src/data/things/sorting-rule.js
+++ b/src/data/things/sorting-rule.js
@@ -4,7 +4,7 @@ import {readFile, writeFile} from 'node:fs/promises';
 import * as path from 'node:path';
 
 import {input} from '#composite';
-import {compareArrays} from '#sugar';
+import {chunkByProperties, compareArrays, unique} from '#sugar';
 import Thing from '#thing';
 import {isStringNonEmpty, strictArrayOf} from '#validators';
 
@@ -60,6 +60,39 @@ export class SortingRule extends Thing {
 
     save: (results) => ({sortingRules: results}),
   });
+
+  check({wikiData}) {
+    return this.constructor.check(this, opts);
+  }
+
+  apply(opts) {
+    return this.constructor.apply(this, opts);
+  }
+
+  static check() {
+    throw new Error(`Not implemented`);
+  }
+
+  static async apply() {
+    throw new Error(`Not implemented`);
+  }
+
+  static async* applyAll() {
+    throw new Error(`Not implemented`);
+  }
+
+  static async* go({dataPath, wikiData}) {
+    const rules = wikiData.sortingRules;
+    const constructors = unique(rules.map(rule => rule.constructor));
+
+    for (const constructor of constructors) {
+      yield* constructor.applyAll(
+        rules
+          .filter(rule => rule.active)
+          .filter(rule => rule.constructor === constructor),
+        {dataPath, wikiData});
+    }
+  }
 }
 
 export class ThingSortingRule extends SortingRule {
@@ -156,11 +189,11 @@ export class DocumentSortingRule extends ThingSortingRule {
     },
   });
 
-  check({wikiData}) {
-    const oldLayout = getThingLayoutForFilename(this.filename, wikiData);
+  static check(rule, {wikiData}) {
+    const oldLayout = getThingLayoutForFilename(rule.filename, wikiData);
     if (!oldLayout) return;
 
-    const newLayout = this.#processLayout(oldLayout);
+    const newLayout = rule.#processLayout(oldLayout);
 
     const oldOrder = flattenThingLayoutToDocumentOrder(oldLayout);
     const newOrder = flattenThingLayoutToDocumentOrder(newLayout);
@@ -168,17 +201,17 @@ export class DocumentSortingRule extends ThingSortingRule {
     return compareArrays(oldOrder, newOrder);
   }
 
-  async apply({wikiData, dataPath}) {
-    const oldLayout = getThingLayoutForFilename(this.filename, wikiData);
+  static async apply(rule, {wikiData, dataPath}) {
+    const oldLayout = getThingLayoutForFilename(rule.filename, wikiData);
     if (!oldLayout) return;
 
-    const newLayout = this.#processLayout(oldLayout);
+    const newLayout = rule.#processLayout(oldLayout);
     const newOrder = flattenThingLayoutToDocumentOrder(newLayout);
 
     const realPath =
       path.join(
         dataPath,
-        this.filename.split(path.posix.sep).join(path.sep));
+        rule.filename.split(path.posix.sep).join(path.sep));
 
     const oldSourceText = await readFile(realPath, 'utf8');
     const newSourceText = reorderDocumentsInYAMLSourceText(oldSourceText, newOrder);
@@ -186,6 +219,53 @@ export class DocumentSortingRule extends ThingSortingRule {
     await writeFile(realPath, newSourceText);
   }
 
+  static async* applyAll(rules, {wikiData, dataPath}) {
+    rules =
+      rules
+        .slice()
+        .sort((a, b) => a.filename.localeCompare(b.filename));
+
+    for (const {chunk, filename} of chunkByProperties(rules, ['filename'])) {
+      const initialLayout = getThingLayoutForFilename(filename, wikiData);
+      if (!initialLayout) continue;
+
+      let currLayout = initialLayout;
+      let prevLayout = initialLayout;
+      let anyChanged = false;
+
+      for (const rule of chunk) {
+        currLayout = rule.#processLayout(currLayout);
+
+        const prevOrder = flattenThingLayoutToDocumentOrder(prevLayout);
+        const currOrder = flattenThingLayoutToDocumentOrder(currLayout);
+
+        if (compareArrays(currOrder, prevOrder)) {
+          yield {rule, changed: false};
+        } else {
+          anyChanged = true;
+          yield {rule, changed: true};
+        }
+
+        prevLayout = currLayout;
+      }
+
+      if (!anyChanged) continue;
+
+      const newLayout = currLayout;
+      const newOrder = flattenThingLayoutToDocumentOrder(newLayout);
+
+      const realPath =
+        path.join(
+          dataPath,
+          filename.split(path.posix.sep).join(path.sep));
+
+      const oldSourceText = await readFile(realPath, 'utf8');
+      const newSourceText = reorderDocumentsInYAMLSourceText(oldSourceText, newOrder);
+
+      await writeFile(realPath, newSourceText);
+    }
+  }
+
   #processLayout(layout) {
     const fresh = {...layout};
 
diff --git a/src/write/build-modes/sort.js b/src/write/build-modes/sort.js
index 0b759c45..df531dfa 100644
--- a/src/write/build-modes/sort.js
+++ b/src/write/build-modes/sort.js
@@ -2,6 +2,7 @@ export const description = `Update data files in-place to satisfy custom sorting
 
 import {logInfo} from '#cli';
 import {empty} from '#sugar';
+import thingConstructors from '#things';
 
 export const config = {
   fileSizes: {
@@ -39,23 +40,21 @@ export async function go({wikiData, dataPath}) {
     return true;
   }
 
+  const {SortingRule} = thingConstructors;
+
   let numUpdated = 0;
   let numActive = 0;
 
-  for (const sortingRule of wikiData.sortingRules) {
-    if (!sortingRule.active) continue;
-
+  for await (const result of SortingRule.go({wikiData, dataPath})) {
     numActive++;
 
-    const niceMessage = `"${sortingRule.message}"`;
-
-    if (sortingRule.check({wikiData})) {
-      logInfo`Already good: ${niceMessage}`;
-    } else {
-      logInfo`Updating to satisfy ${niceMessage}.`;
-      await sortingRule.apply({wikiData, dataPath});
+    const niceMessage = `"${result.rule.message}"`;
 
+    if (result.changed) {
       numUpdated++;
+      logInfo`Updating to satisfy ${niceMessage}.`;
+    } else {
+      logInfo`Already good: ${niceMessage}`;
     }
   }