« 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/validators.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/data/things/validators.js')
-rw-r--r--src/data/things/validators.js75
1 files changed, 66 insertions, 9 deletions
diff --git a/src/data/things/validators.js b/src/data/things/validators.js
index 5748eacf..bdb22058 100644
--- a/src/data/things/validators.js
+++ b/src/data/things/validators.js
@@ -1,7 +1,7 @@
 import {inspect as nodeInspect} from 'node:util';
 
-import {color, ENABLE_COLOR} from '#cli';
-import {withAggregate} from '#sugar';
+import {colors, ENABLE_COLOR} from '#cli';
+import {empty, typeAppearance, withAggregate} from '#sugar';
 
 function inspect(value) {
   return nodeInspect(value, {colors: ENABLE_COLOR});
@@ -9,13 +9,13 @@ function inspect(value) {
 
 // Basic types (primitives)
 
-function a(noun) {
+export function a(noun) {
   return /[aeiou]/.test(noun[0]) ? `an ${noun}` : `a ${noun}`;
 }
 
-function isType(value, type) {
+export function isType(value, type) {
   if (typeof value !== type)
-    throw new TypeError(`Expected ${a(type)}, got ${typeof value}`);
+    throw new TypeError(`Expected ${a(type)}, got ${typeAppearance(value)}`);
 
   return true;
 }
@@ -132,7 +132,7 @@ export function isObject(value) {
 
 export function isArray(value) {
   if (typeof value !== 'object' || value === null || !Array.isArray(value))
-    throw new TypeError(`Expected an array, got ${value}`);
+    throw new TypeError(`Expected an array, got ${typeAppearance(value)}`);
 
   return true;
 }
@@ -174,7 +174,8 @@ function validateArrayItemsHelper(itemValidator) {
         throw new Error(`Expected validator to return true`);
       }
     } catch (error) {
-      error.message = `(index: ${color.yellow(`#${index}`)}, item: ${inspect(item)}) ${error.message}`;
+      error.message = `(index: ${colors.yellow(`${index}`)}, item: ${inspect(item)}) ${error.message}`;
+      error[Symbol.for('hsmusic.decorate.indexInSourceArray')] = index;
       throw error;
     }
   };
@@ -264,7 +265,7 @@ export function validateProperties(spec) {
           try {
             specValidator(value);
           } catch (error) {
-            error.message = `(key: ${color.green(specKey)}, value: ${inspect(value)}) ${error.message}`;
+            error.message = `(key: ${colors.green(specKey)}, value: ${inspect(value)}) ${error.message}`;
             throw error;
           }
         });
@@ -308,7 +309,7 @@ export const isTrackSection = validateProperties({
   color: optional(isColor),
   dateOriginallyReleased: optional(isDate),
   isDefaultTrackSection: optional(isBoolean),
-  tracksByRef: optional(validateReferenceList('track')),
+  tracks: optional(validateReferenceList('track')),
 });
 
 export const isTrackSectionList = validateArrayItems(isTrackSection);
@@ -404,6 +405,62 @@ export function validateReferenceList(type = '') {
   return validateArrayItems(validateReference(type));
 }
 
+export function validateWikiData({
+  referenceType = '',
+  allowMixedTypes = false,
+}) {
+  if (referenceType && allowMixedTypes) {
+    throw new TypeError(`Don't specify both referenceType and allowMixedTypes`);
+  }
+
+  const isArrayOfObjects = validateArrayItems(isObject);
+
+  return (array) => {
+    isArrayOfObjects(array);
+
+    if (empty(array)) {
+      return true;
+    }
+
+    const allRefTypes =
+      new Set(array.map(object =>
+        object.constructor[Symbol.for('Thing.referenceType')]));
+
+    if (allRefTypes.has(undefined)) {
+      if (allRefTypes.size === 1) {
+        throw new TypeError(`Expected array of wiki data objects, got array of other objects`);
+      } else {
+        throw new TypeError(`Expected array of wiki data objects, got mixed items`);
+      }
+    }
+
+    if (allRefTypes.size > 1) {
+      if (allowMixedTypes) {
+        return true;
+      }
+
+      const types = () => Array.from(allRefTypes).join(', ');
+
+      if (referenceType) {
+        if (allRefTypes.has(referenceType)) {
+          allRefTypes.remove(referenceType);
+          throw new TypeError(`Expected array of only ${referenceType}, also got other types: ${types()}`)
+        } else {
+          throw new TypeError(`Expected array of only ${referenceType}, got other types: ${types()}`);
+        }
+      }
+
+      throw new TypeError(`Expected array of unmixed reference types, got multiple: ${types()}`);
+    }
+
+    if (referenceType && !allRefTypes.has(referenceType)) {
+      throw new TypeError(`Expected array of ${referenceType}, got array of ${allRefTypes[0]}`)
+    }
+
+    return true;
+  };
+}
+
 // Compositional utilities
 
 export function oneOf(...checks) {