All files utils.js

100% Statements 41/41
94.44% Branches 17/18
100% Functions 20/20
100% Lines 27/27
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76  91x     182x 199x         4x 25x 79x   6x     4x 202x   201x   201x   198x       4x 85x     8x 25x 25x   25x     191x       25x   25x               25x       85x               78x     4x     4x     134x  
// data Constructor = { name: String, props: [Type|String] };
export const Constructor = x => x;
 
// prop :: Array -> Object
export const prop = (path, defaultVal) => obj => path.reduce(
    (o, key) => (o || {}).hasOwnProperty(key) ? o[key] : defaultVal,
    obj,
);
 
// normalizeSumType :: Array String | Object [a] -> Constructor
const normalizeSumType = sumType =>
    isList(sumType)
        ? sumType.map(name => Constructor({ name }))
        : Object.keys(sumType)
            .map(name => Constructor({ name, props: sumType[name] }));
 
// match :: EnumTagType -> Pattern -> b
const match = (instance, pattern) => {
    if (!instance || !instance.name) throw new Error('Invalid instance passed');
 
    const action = pattern[instance.name] || pattern._;
 
    if(!action) throw new Error('Non-Exhaustive pattern. You must pass fallback case `_` in the pattern');
 
    return action(...instance.args);
};
 
// listToObject :: (a -> String, a -> b, [a]) -> Object b
const listToObject = (toKey, toValue, list) =>
    list.reduce((obj, item) => ({ ...obj, [toKey(item)]: toValue(item) }), {});
 
// createEnumFactory :: Options -> Array String | Object Type -> Enum
export const createEnumFactory = options => sumTypeBody => {
    const constructors = normalizeSumType(sumTypeBody);
    const { createConstructor } = options;
 
    const typeNames = constructors.map(prop(['name']));
 
    // isConstructor :: String ~> Boolean
    const isConstructor = t => !t ? false :
        typeNames.indexOf(t) !== -1 || typeNames.indexOf(t.name) !== -1;
 
    // cata :: Pattern ~> EnumTagType -> b
    const cata = pattern => instance => match(instance, pattern);
 
    let self = {
        isConstructor,
        match,
        cata,
        caseOf: cata,
        reduce: cata,
    };
 
    return {
        // {String} :: TypeConstructor
        ...listToObject(
            prop(['name']),
            constr => createConstructor(self, constr),
            constructors,
        ),
        ...self,
    };
};
 
// isObjectOfType :: String -> a -> Boolean
const isObjectOfType = typeName => a => ({}).toString.call(a) === `[object ${typeName}]`;
 
// isList :: * -> Boolean
export const isList = isObjectOfType('Array');
 
// isObject:: * -> Boolean[object 
export const isObject = isObjectOfType('Object');
 
// values :: Object a -> [a]
export const values = obj => Object.keys(obj).sort().map(k => obj[k]);