Browse Source

Extract collection logic into separate utils - fixes #45 (#46)

iss58
Sam Verschueren 7 years ago
committed by Sindre Sorhus
parent
commit
ad4b9d4ac5
  1. 6
      source/index.ts
  2. 81
      source/lib/predicates/map.ts
  3. 4
      source/lib/predicates/predicate.ts
  4. 41
      source/lib/predicates/set.ts
  5. 21
      source/lib/predicates/weak-map.ts
  6. 21
      source/lib/predicates/weak-set.ts
  7. 27
      source/lib/utils/has-items.ts
  8. 21
      source/lib/utils/of-type.ts

6
source/index.ts

@ -156,9 +156,11 @@ export interface Ow {
const main = <T>(value: T, predicate: Predicate<T>) => {
for (const {validator, message} of predicate[validatorSymbol]) {
if (!validator(value)) {
const result = validator(value);
if (typeof result !== 'boolean' || !result) {
// TODO: Modify the stack output to show the original `ow()` call instead of this `throw` statement
throw new ArgumentError(message(value), main);
throw new ArgumentError(message(value, result), main);
}
}
};

81
source/lib/predicates/map.ts

@ -1,6 +1,7 @@
import * as isEqual from 'lodash.isequal';
import ow from '../..';
import {Predicate, Context} from './predicate';
import hasItems from '../utils/has-items';
import ofType from '../utils/of-type';
export class MapPredicate extends Predicate<Map<any, any>> {
constructor(context?: Context) {
@ -49,25 +50,9 @@ export class MapPredicate extends Predicate<Map<any, any>> {
* @param keys The keys that should be a key in the Map.
*/
hasKeys(...keys: any[]) {
const missingKeys: any[] = [];
return this.addValidator({
message: () => `Expected Map to have keys \`${JSON.stringify(missingKeys)}\``,
validator: map => {
for (const key of keys) {
if (map.has(key)) {
continue;
}
missingKeys.push(key);
if (missingKeys.length === 5) {
return false;
}
}
return missingKeys.length === 0;
}
message: (_, missingKeys) => `Expected Map to have keys \`${JSON.stringify(missingKeys)}\``,
validator: map => hasItems(map, keys)
});
}
@ -89,27 +74,9 @@ export class MapPredicate extends Predicate<Map<any, any>> {
* @param values The values that should be a value in the Map.
*/
hasValues(...values: any[]) {
const missingValues: any[] = [];
return this.addValidator({
message: () => `Expected Map to have values \`${JSON.stringify(missingValues)}\``,
validator: map => {
const valueSet = new Set(map.values());
for (const value of values) {
if (valueSet.has(value)) {
continue;
}
missingValues.push(value);
if (missingValues.length === 5) {
return false;
}
}
return missingValues.length === 0;
}
message: (_, missingValues) => `Expected Map to have values \`${JSON.stringify(missingValues)}\``,
validator: map => hasItems(new Set(map.values()), values)
});
}
@ -135,23 +102,9 @@ export class MapPredicate extends Predicate<Map<any, any>> {
* @param predicate The predicate that should be applied against every key in the Map.
*/
keysOfType<T>(predicate: Predicate<T>) {
let error: string;
return this.addValidator({
message: () => error,
validator: map => {
try {
for (const item of map.keys()) {
ow(item, predicate);
}
return true;
} catch (err) {
error = err.message;
return false;
}
}
message: (_, error) => error,
validator: map => ofType(map.keys(), predicate)
});
}
@ -161,23 +114,9 @@ export class MapPredicate extends Predicate<Map<any, any>> {
* @param predicate The predicate that should be applied against every value in the Map.
*/
valuesOfType<T>(predicate: Predicate<T>) {
let error: string;
return this.addValidator({
message: () => error,
validator: map => {
try {
for (const item of map.values()) {
ow(item, predicate);
}
return true;
} catch (err) {
error = err.message;
return false;
}
}
message: (_, error) => error,
validator: map => ofType(map.values(), predicate)
});
}

4
source/lib/predicates/predicate.ts

@ -5,8 +5,8 @@ import {not} from '../operators/not';
* @hidden
*/
export interface Validator<T> {
message(value: T): string;
validator(value: T): boolean;
message(value: T, result?: any): string;
validator(value: T): any;
}
/**

41
source/lib/predicates/set.ts

@ -1,6 +1,7 @@
import * as isEqual from 'lodash.isequal';
import ow from '../..';
import {Predicate, Context} from './predicate';
import hasItems from '../utils/has-items';
import ofType from '../utils/of-type';
export class SetPredicate extends Predicate<Set<any>> {
constructor(context?: Context) {
@ -49,25 +50,9 @@ export class SetPredicate extends Predicate<Set<any>> {
* @param items The items that should be a item in the Set.
*/
has(...items: any[]) {
const missingItems: any[] = [];
return this.addValidator({
message: () => `Expected Set to have items \`${JSON.stringify(missingItems)}\``,
validator: set => {
for (const item of items) {
if (set.has(item)) {
continue;
}
missingItems.push(item);
if (missingItems.length === 5) {
return false;
}
}
return missingItems.length === 0;
}
message: (_, missingItems) => `Expected Set to have items \`${JSON.stringify(missingItems)}\``,
validator: set => hasItems(set, items)
});
}
@ -89,23 +74,9 @@ export class SetPredicate extends Predicate<Set<any>> {
* @param predicate The predicate that should be applied against every item in the Set.
*/
ofType<T>(predicate: Predicate<T>) {
let error: string;
return this.addValidator({
message: () => error,
validator: set => {
try {
for (const item of set.keys()) {
ow(item, predicate);
}
return true;
} catch (err) {
error = err.message;
return false;
}
}
message: (_, error) => error,
validator: set => ofType(set, predicate)
});
}

21
source/lib/predicates/weak-map.ts

@ -1,4 +1,5 @@
import {Predicate, Context} from './predicate';
import hasItems from '../utils/has-items';
export class WeakMapPredicate extends Predicate<WeakMap<any, any>> {
constructor(context?: Context) {
@ -11,25 +12,9 @@ export class WeakMapPredicate extends Predicate<WeakMap<any, any>> {
* @param keys The keys that should be a key in the WeakMap.
*/
hasKeys(...keys: any[]) {
const missingKeys: any[] = [];
return this.addValidator({
message: () => `Expected WeakMap to have keys \`${JSON.stringify(missingKeys)}\``,
validator: map => {
for (const key of keys) {
if (map.has(key)) {
continue;
}
missingKeys.push(key);
if (missingKeys.length === 5) {
return false;
}
}
return missingKeys.length === 0;
}
message: (_, missingKeys) => `Expected WeakMap to have keys \`${JSON.stringify(missingKeys)}\``,
validator: map => hasItems(map, keys)
});
}

21
source/lib/predicates/weak-set.ts

@ -1,4 +1,5 @@
import {Predicate, Context} from './predicate';
import hasItems from '../utils/has-items';
export class WeakSetPredicate extends Predicate<WeakSet<any>> {
constructor(context?: Context) {
@ -11,25 +12,9 @@ export class WeakSetPredicate extends Predicate<WeakSet<any>> {
* @param items The items that should be a item in the WeakSet.
*/
has(...items: any[]) {
const missingItems: any[] = [];
return this.addValidator({
message: () => `Expected WeakSet to have items \`${JSON.stringify(missingItems)}\``,
validator: set => {
for (const key of items) {
if (set.has(key)) {
continue;
}
missingItems.push(key);
if (missingItems.length === 5) {
return false;
}
}
return missingItems.length === 0;
}
message: (_, missingItems) => `Expected WeakSet to have items \`${JSON.stringify(missingItems)}\``,
validator: set => hasItems(set, items)
});
}

27
source/lib/utils/has-items.ts

@ -0,0 +1,27 @@
type Collection = Set<any> | Map<any, any> | WeakSet<any> | WeakMap<any, any>;
/**
* Retrieve the missing values in a collection based on an array of items.
*
* @hidden
* @param source Source collection to search through.
* @param items Items to search for.
* @param maxValues Maximum number of values after the search process is stopped. (Default: 5)
*/
export default (source: Collection, items: any[], maxValues = 5) => {
const missingValues: any[] = [];
for (const value of items) {
if (source.has(value)) {
continue;
}
missingValues.push(value);
if (missingValues.length === maxValues) {
return missingValues;
}
}
return missingValues.length === 0 ? true : missingValues;
};

21
source/lib/utils/of-type.ts

@ -0,0 +1,21 @@
import ow from '../..';
import {Predicate} from '../predicates/predicate';
/**
* Test all the values in the collection against a provided predicate.
*
* @hidden
* @param source Source collection to test.
* @param predicate Predicate to test every item in the source collection against.
*/
export default (source: IterableIterator<any> | Set<any>, predicate: Predicate): boolean | string => {
try {
for (const item of source) {
ow(item, predicate);
}
return true;
} catch (err) {
return err.message;
}
};
Loading…
Cancel
Save