UNPKG

@cowwoc/requirements

Version:

A fluent API for enforcing design contracts with automatic message generation.

316 lines 15 kB
import { AbstractValidator, MessageBuilder, Pluralizer, assert, AssertionError, Difference, objectIsNotEmpty, objectIsEmpty, comparableGetBounds } from "../internal.mjs"; /** * @param validator - the collection's validator * @param actualSizeName - the name of the collection's size * @param actualSize - the collection's size * @param relationship - the relationship between the actual and expected sizes (e.g. "must contain less * than") * @param expectedSizeName - an expression representing the expected size of the collection * @param expectedSize - the number of elements that should be in the collection * @param pluralizer - the type of items in the collection * @returns a message for the validation failure */ function collectionContainsSize(validator, actualSizeName, actualSize, relationship, expectedSizeName, expectedSize, pluralizer) { // "actual" must contain exactly expected.size() characters. // actual : "hello world" // actual.size() : 11 // expected.size(): 15 const expectedNameOrSize = validator.getNameOrValue("", expectedSizeName, "", expectedSize); const name = validator.getName(); const messageBuilder = new MessageBuilder(validator, `${MessageBuilder.quoteName(name)} ${relationship} ${expectedNameOrSize} \ ${pluralizer.nameOf(expectedSize, expectedSizeName)}.`); validator.value.undefinedOrNullToInvalid().ifValid(v => messageBuilder.withContext(v, name)); if (actualSize !== null) messageBuilder.withContext(actualSize, actualSizeName); if (expectedSizeName !== null) messageBuilder.withContext(expectedSize, expectedSizeName); return messageBuilder; } /** * @param validator - the collection's validator * @param actualSizeName - the name of the collection's size * @param actualSize - the collection's size * @param minimum - the collection's minimum size * @param minimumInclusive - `true` if minimum size is inclusive * @param maximum - the collection's maximum size * @param maximumInclusive - `true` if maximum size is inclusive * @param pluralizer - the type of items in the collection */ function collectionSizeIsBetween(validator, actualSizeName, actualSize, minimum, minimumInclusive, maximum, maximumInclusive, pluralizer) { assert(maximum >= minimum, undefined, `"minimum: ${minimum}, maximum: ${maximum}`); const bounds = comparableGetBounds(minimum, minimumInclusive, maximum, maximumInclusive, validator.configuration().stringMappers()); const name = validator.getName(); let message = MessageBuilder.quoteName(name); if (actualSize === null) { // The size is null (e.g. the collection is null) // // "actual" must contain [1, 3] elements message += ` must contain ${bounds} ${pluralizer.nameOf(2, null)} .`; return new MessageBuilder(validator, message); } // actual must contain at least 4 characters. // actual : "hey" // actual.length(): 3 // Bounds : [4, 6] let inclusiveMinimum; if (minimumInclusive) inclusiveMinimum = minimum; else inclusiveMinimum = minimum + 1; let exclusiveMaximum; if (maximumInclusive) exclusiveMaximum = maximum - 1; else exclusiveMaximum = maximum; message += " must contain "; if (actualSize < inclusiveMinimum) message += "at least "; else if (actualSize >= exclusiveMaximum) message += "at most "; else { throw new AssertionError(`Value should have been out of bounds. actual: ${actualSize.toString()} bounds: ${bounds}`); } message += `${pluralizer.nameOf(2, null)}.`; return new MessageBuilder(validator, message). withContext(validator.getValue(), name). withContext(actualSize, actualSizeName). withContext(bounds, "bounds"); } /** * @param validator - the validator * @returns a message for the validation failure */ function collectionIsEmpty(validator) { return objectIsEmpty(validator); } /** * @param validator - the validator * @returns a message for the validation failure */ function collectionIsNotEmpty(validator) { return objectIsNotEmpty(validator); } /** * @param validator - the validator * @param expectedName - the name of the expected value * @param expected - the expected value * @returns a message for the validation failure */ function collectionContains(validator, expectedName, expected) { // "actual" must contain the same value as "expected". // actual : 5 // expected: 2 return collectionContainsImpl(validator, "must contain", expectedName, expected); } /** * @param validator - the validator * @param relationship - the relationship between the actual and other value (e.g. "must contain") * @param otherName - the name of the other value * @param other - the other value * @returns a message for the validation failure */ function collectionContainsImpl(validator, relationship, otherName, other) { // "actual" must contain the same value as "expected". // actual: 5 // factor: 2 const otherNameOrValue = validator.getNameOrValue("the same value as ", otherName, "", other); const messageBuilder = new MessageBuilder(validator, `${MessageBuilder.quoteName(validator.getName())} ${relationship} ${otherNameOrValue}.`); if (otherName !== null) messageBuilder.withContext(other, otherName); return messageBuilder; } /** * @param validator - the validator * @param unwantedName - the name of the unwanted value * @param unwanted - the unwanted value * @returns a message for the validation failure */ function collectionDoesNotContain(validator, unwantedName, unwanted) { // "actual" may not contain the same value as "unwanted". // actual : 5 // unwanted: 2 return collectionContainsImpl(validator, "may not contain", unwantedName, unwanted); } /** * @param validator - the validator * @param expectedName - the name of the expected collection * @param expected - the collection of expected values * @param pluralizer - the type of items in the collections * @returns a message for the validation failure */ function collectionContainsAny(validator, expectedName, expected, pluralizer) { // "actual" must contain any of the elements present in "expected". // actual : [1, 2, 3] // expected: [2, 3, 4] const expectedNameOrValue = validator.getNameOrValue("", expectedName, "the set ", expected); const name = validator.getName(); const messageBuilder = new MessageBuilder(validator, `${MessageBuilder.quoteName(name)} must contain any of the ${pluralizer.nameOf(2, null)} \ present in ${expectedNameOrValue}.`); validator.value.undefinedOrNullToInvalid().ifValid(v => messageBuilder.withContext(v, name)); if (expectedName !== null) messageBuilder.withContext(expected, expectedName); return messageBuilder; } /** * @typeParam E - the type of elements in the value * @param validator - the validator * @param difference - the difference between the actual and unwanted values * @param unwantedName - the name of the unwanted collection * @param unwanted - the collection of unwanted elements * @param pluralizer - the type of items in the collections * @returns a message for the validation failure */ function collectionDoesNotContainAny(validator, difference, unwantedName, unwanted, pluralizer) { // "actual" may not contain any of the elements present in "unwanted". // actual : [1, 2, 3] // unwanted: [2, 3, 4] // elementsToRemove: [2, 3, 4] const unwantedNameOrValue = validator.getNameOrValue("", unwantedName, "the set ", unwanted); const name = validator.getName(); const messageBuilder = new MessageBuilder(validator, `${MessageBuilder.quoteName(name)} may not contain any of the ${pluralizer.nameOf(2, null)} \ present in ${unwantedNameOrValue}.`); validator.value.undefinedOrNullToInvalid().ifValid(v => messageBuilder.withContext(v, name)); if (unwantedName !== null) messageBuilder.withContext(unwanted, unwantedName); if (difference !== null) messageBuilder.withContext(difference.common, "elementsToRemove"); return messageBuilder; } /** * @typeParam E - the type of elements in the value * @param validator - the validator * @param difference - the difference between the actual and expected values * @param expectedName - the name of the collection * @param expected - the collection * @param pluralizer - the type of items in the value * @returns a message for the validation failure */ function collectionContainsExactly(validator, difference, expectedName, expected, pluralizer) { // "actual" must consist of the elements [2, 3, 4], regardless of their order. // // or // // "actual" must consist of the same elements as "expected", regardless of their order. // actual : [1, 2, 3] // expected: [2, 3, 4] // missing : [4] // unwanted: [1] const name = validator.getName(); let message = `${MessageBuilder.quoteName(name)} must consist of the `; if (expectedName !== null) message += "same "; message += `${pluralizer.nameOf(2, null)} `; const expectedNameOrValue = validator.getNameOrValue("as ", expectedName, "", expected); message += `${expectedNameOrValue}, regardless of their order.`; const messageBuilder = new MessageBuilder(validator, message); validator.value.undefinedOrNullToInvalid().ifValid(v => messageBuilder.withContext(v, name)); if (expectedName !== null) messageBuilder.withContext(expected, expectedName); if (difference !== null) { messageBuilder.withContext(difference.onlyInOther, "missing"). withContext(difference.onlyInActual, "unwanted"); } return messageBuilder; } /** * @param validator - the validator * @param unwantedName - the name of the collection * @param unwanted - the collection * @param pluralizer - the type of items in the value * @returns a message for the validation failure */ function collectionDoesNotContainExactly(validator, unwantedName, unwanted, pluralizer) { // "actual" may not consist of the elements [2, 3, 4], regardless of their order. // // or // // "actual" may not consist of the same elements as "expected", regardless of their order. // unwanted : [1, 2, 3] let message = `${MessageBuilder.quoteName(validator.getName())} may not consist of the `; if (unwantedName !== null) message += "same "; message += `${pluralizer.nameOf(2, null)} `; const unwantedStringNameOrValue = validator.getNameOrValue("as ", unwantedName, "", unwanted); message += `${unwantedStringNameOrValue}, regardless of their order.`; const messageBuilder = new MessageBuilder(validator, message); if (unwantedName !== null) messageBuilder.withContext(unwanted, unwantedName); return messageBuilder; } /** * @typeParam E - the type of elements in the value * @param validator - the validator * @param difference - the difference between the actual and expected values * @param expectedName - the name of the expected collection * @param expected - the collection of expected values * @param pluralizer - the type of items in the value * @returns a message for the validation failure */ function collectionContainsAll(validator, difference, expectedName, expected, pluralizer) { // "actual" must contain all the elements present in "expected". // actual : [1, 2, 3] // expected: [2, 3, 4] // missing : [4] const expectedNameOrValue = validator.getNameOrValue("", expectedName, "the set ", expected); const name = validator.getName(); const messageBuilder = new MessageBuilder(validator, `${MessageBuilder.quoteName(name)} must contain all the ${pluralizer.nameOf(2, null)} \ present in ${expectedNameOrValue}.`); validator.value.undefinedOrNullToInvalid().ifValid(v => messageBuilder.withContext(v, name)); if (expectedName !== null) messageBuilder.withContext(expected, expectedName); if (difference !== null) messageBuilder.withContext(difference.onlyInOther, "missing"); return messageBuilder; } /** * @param validator - the validator * @param unwantedName - the name of the unwanted collection * @param unwanted - the collection of unwanted values * @param pluralizer - the type of items in the value * @returns a message for the validation failure */ function collectionDoesNotContainAll(validator, unwantedName, unwanted, pluralizer) { // "actual" may not contain some, but not all, the elements present in "unwanted". // actual : [1, 2, 3] // unwanted: [2, 3, 4] const unwantedNameOrValue = validator.getNameOrValue("", unwantedName, "the set ", unwanted); const name = validator.getName(); const messageBuilder = new MessageBuilder(validator, `${MessageBuilder.quoteName(name)} may contain some, but not all, the \ ${pluralizer.nameOf(2, null)} present in ${unwantedNameOrValue}.`); validator.value.undefinedOrNullToInvalid().ifValid(v => messageBuilder.withContext(v, name)); if (unwantedName !== null) messageBuilder.withContext(unwanted, unwantedName); return messageBuilder; } /** * @typeParam E - the type of elements in the value * @param validator - the validator * @param duplicates - the duplicate values in the value being validated * @param pluralizer - the type of items in the value * @returns a message for the validation failure */ function collectionDoesNotContainDuplicates(validator, duplicates, pluralizer) { const name = validator.getName(); const messageBuilder = new MessageBuilder(validator, `${MessageBuilder.quoteName(name)} may not contain any duplicate ${pluralizer.nameOf(2, null)}.`); if (duplicates !== null) messageBuilder.withContext(duplicates, "duplicates"); validator.value.undefinedOrNullToInvalid().ifValid(v => messageBuilder.withContext(v, name)); return messageBuilder; } /** * @param validator - the validator * @param sorted - the sorted representation of the value being validated * @returns a message for the validation failure */ function collectionIsSorted(validator, sorted) { const name = validator.getName(); const messageBuilder = new MessageBuilder(validator, `${MessageBuilder.quoteName(name)} must be sorted.`); validator.value.undefinedOrNullToInvalid().ifValid(v => messageBuilder.withContext(v, name)); if (sorted !== null) messageBuilder.withContext(sorted, "expected"); return messageBuilder; } export { collectionContainsSize, collectionSizeIsBetween, collectionIsEmpty, collectionIsNotEmpty, collectionContains, collectionDoesNotContain, collectionContainsExactly, collectionDoesNotContainExactly, collectionContainsAny, collectionDoesNotContainAny, collectionContainsAll, collectionDoesNotContainAll, collectionDoesNotContainDuplicates, collectionIsSorted }; //# sourceMappingURL=CollectionMessages.mjs.map