« 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/things/sorting-rule.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/things/sorting-rule.js')
-rw-r--r--src/data/things/sorting-rule.js109
1 files changed, 61 insertions, 48 deletions
diff --git a/src/data/things/sorting-rule.js b/src/data/things/sorting-rule.js
index 18da452e..0ed7fb0f 100644
--- a/src/data/things/sorting-rule.js
+++ b/src/data/things/sorting-rule.js
@@ -6,7 +6,7 @@ import * as path from 'node:path';
 import {input} from '#composite';
 import {chunkByProperties, compareArrays, unique} from '#sugar';
 import Thing from '#thing';
-import {isObject, isStringNonEmpty, strictArrayOf} from '#validators';
+import {isObject, isStringNonEmpty, anyOf, strictArrayOf} from '#validators';
 
 import {
   compareCaseLessSensitive,
@@ -24,6 +24,17 @@ import {
 
 import {flag} from '#composite/wiki-properties';
 
+function isSelectFollowingEntry(value) {
+  isObject(value);
+
+  const {length} = Object.keys(value);
+  if (length !== 1) {
+    throw new Error(`Expected object with 1 key, got ${length}`);
+  }
+
+  return true;
+}
+
 export class SortingRule extends Thing {
   static [Thing.friendlyName] = `Sorting Rule`;
 
@@ -61,7 +72,7 @@ export class SortingRule extends Thing {
     save: (results) => ({sortingRules: results}),
   });
 
-  check({wikiData}) {
+  check(opts) {
     return this.constructor.check(this, opts);
   }
 
@@ -69,19 +80,22 @@ export class SortingRule extends Thing {
     return this.constructor.apply(this, opts);
   }
 
-  static check() {
-    throw new Error(`Not implemented`);
+  static check(rule, opts) {
+    const result = this.apply(rule, {...opts, dry: true});
+    if (!result) return true;
+    if (!result.changed) return true;
+    return false;
   }
 
-  static async apply() {
+  static async apply(_rule, _opts) {
     throw new Error(`Not implemented`);
   }
 
-  static async* applyAll() {
+  static async* applyAll(_rules, _opts) {
     throw new Error(`Not implemented`);
   }
 
-  static async* go({dataPath, wikiData}) {
+  static async* go({dataPath, wikiData, dry}) {
     const rules = wikiData.sortingRules;
     const constructors = unique(rules.map(rule => rule.constructor));
 
@@ -90,7 +104,7 @@ export class SortingRule extends Thing {
         rules
           .filter(rule => rule.active)
           .filter(rule => rule.constructor === constructor),
-        {dataPath, wikiData});
+        {dataPath, wikiData, dry});
     }
   }
 }
@@ -186,16 +200,17 @@ export class DocumentSortingRule extends ThingSortingRule {
       flags: {update: true, expose: true},
 
       update: {
-        validate(value) {
-          isObject(value);
-
-          const {length} = Object.keys(value);
-          if (length !== 1) {
-            throw new Error(`Expected object with 1 key, got ${length}`);
-          }
+        validate:
+          anyOf(
+            isSelectFollowingEntry,
+            strictArrayOf(isSelectFollowingEntry)),
+      },
 
-          return true;
-        },
+      compute: {
+        transform: value =>
+          (Array.isArray(value)
+            ? value
+            : [value]),
       },
     },
 
@@ -220,24 +235,17 @@ export class DocumentSortingRule extends ThingSortingRule {
     ],
   });
 
-  static check(rule, {wikiData}) {
+  static async apply(rule, {wikiData, dataPath, dry}) {
     const oldLayout = getThingLayoutForFilename(rule.filename, wikiData);
-    if (!oldLayout) return;
+    if (!oldLayout) return null;
 
     const newLayout = rule.#processLayout(oldLayout);
 
     const oldOrder = flattenThingLayoutToDocumentOrder(oldLayout);
     const newOrder = flattenThingLayoutToDocumentOrder(newLayout);
+    const changed = compareArrays(oldOrder, newOrder);
 
-    return compareArrays(oldOrder, newOrder);
-  }
-
-  static async apply(rule, {wikiData, dataPath}) {
-    const oldLayout = getThingLayoutForFilename(rule.filename, wikiData);
-    if (!oldLayout) return;
-
-    const newLayout = rule.#processLayout(oldLayout);
-    const newOrder = flattenThingLayoutToDocumentOrder(newLayout);
+    if (dry) return {changed};
 
     const realPath =
       path.join(
@@ -248,9 +256,11 @@ export class DocumentSortingRule extends ThingSortingRule {
     const newSourceText = reorderDocumentsInYAMLSourceText(oldSourceText, newOrder);
 
     await writeFile(realPath, newSourceText);
+
+    return {changed};
   }
 
-  static async* applyAll(rules, {wikiData, dataPath}) {
+  static async* applyAll(rules, {wikiData, dataPath, dry}) {
     rules =
       rules
         .slice()
@@ -281,6 +291,7 @@ export class DocumentSortingRule extends ThingSortingRule {
       }
 
       if (!anyChanged) continue;
+      if (dry) continue;
 
       const newLayout = currLayout;
       const newOrder = flattenThingLayoutToDocumentOrder(newLayout);
@@ -317,31 +328,33 @@ export class DocumentSortingRule extends ThingSortingRule {
     }
 
     if (this.selectDocumentsFollowing) {
-      const [field, value] = Object.entries(this.selectDocumentsFollowing)[0];
+      for (const entry of this.selectDocumentsFollowing) {
+        const [field, value] = Object.entries(entry)[0];
 
-      const after =
-        sortable.findIndex(thing =>
-          thing[Thing.yamlSourceDocument][field] === value);
+        const after =
+          sortable.findIndex(thing =>
+            thing[Thing.yamlSourceDocument][field] === value);
 
-      const different =
-        after +
-        sortable
-          .slice(after)
-          .findIndex(thing =>
-            Object.hasOwn(thing[Thing.yamlSourceDocument], field) &&
-            thing[Thing.yamlSourceDocument][field] !== value);
+        const different =
+          after +
+          sortable
+            .slice(after)
+            .findIndex(thing =>
+              Object.hasOwn(thing[Thing.yamlSourceDocument], field) &&
+              thing[Thing.yamlSourceDocument][field] !== value);
 
-      const before =
-        (different === -1
-          ? sortable.length
-          : different);
+        const before =
+          (different === -1
+            ? sortable.length
+            : different);
 
-      const subsortable =
-        sortable.slice(after + 1, before);
+        const subsortable =
+          sortable.slice(after + 1, before);
 
-      this.sort(subsortable);
+        this.sort(subsortable);
 
-      sortable.splice(after + 1, before - after - 1, ...subsortable);
+        sortable.splice(after + 1, before - after - 1, ...subsortable);
+      }
     } else if (this.selectDocumentsUnder) {
       const field = this.selectDocumentsUnder;