rubico
Version:
[a]synchronous functional programming
186 lines (176 loc) • 5.27 kB
JavaScript
const promiseAll = require('./_internal/promiseAll')
const isPromise = require('./_internal/isPromise')
const areAnyValuesPromises = require('./_internal/areAnyValuesPromises')
const __ = require('./_internal/placeholder')
const curry2 = require('./_internal/curry2')
const curry3 = require('./_internal/curry3')
const curryArgs2 = require('./_internal/curryArgs2')
const thunkConditional = require('./_internal/thunkConditional')
const areAllValuesNonfunctions = require('./_internal/areAllValuesNonfunctions')
const thunkify2 = require('./_internal/thunkify2')
const thunkify3 = require('./_internal/thunkify3')
const always = require('./_internal/always')
/**
* @name areAllValuesTruthy
*
* @synopsis
* ```coffeescript [specscript]
* areAllValuesTruthy(predicates Array<value>) -> Promise|boolean
* ```
*/
const areAllValuesTruthy = function (predicates, index) {
const length = predicates.length
while (++index < length) {
const predicate = predicates[index]
if (isPromise(predicate)) {
return predicate.then(curry3(
thunkConditional,
__,
thunkify2(areAllValuesTruthy, predicates, index),
always(false),
))
}
if (!predicate) {
return false
}
}
return true
}
/**
* @name asyncArePredicatesAllTruthy
*
* @synopsis
* ```coffeescript [specscript]
* asyncArePredicatesAllTruthy(
* predicates Array<value=>Promise|boolean>
* args Array,
* index number,
* ) -> allTruthy boolean
* ```
*/
const asyncArePredicatesAllTruthy = async function (args, predicates, index) {
const length = predicates.length
while (++index < length) {
let predicate = predicates[index]
if (typeof predicate == 'function') {
predicate = predicate(...args)
}
if (isPromise(predicate)) {
predicate = await predicate
}
if (!predicate) {
return false
}
}
return true
}
// areAllPredicatesTruthy(args Array, predicates Array<function>) -> Promise|boolean
const areAllPredicatesTruthy = function (args, predicates) {
const length = predicates.length
let index = -1
while (++index < length) {
let predicate = predicates[index]
if (typeof predicate == 'function') {
predicate = predicate(...args)
}
if (isPromise(predicate)) {
return predicate.then(curry3(
thunkConditional,
__,
thunkify3(asyncArePredicatesAllTruthy, args, predicates, index),
always(false),
))
}
if (!predicate) {
return false
}
}
return true
}
/**
* @name and
*
* @synopsis
* ```coffeescript [specscript]
* args Array<any>
* argsOrPromises Array<Promise|any>
*
* type SyncOrAsyncPredicate = (...args)=>Promise|boolean|any
*
* predicatesOrValues Array<SyncOrAsyncPredicate|boolean|any>
*
* and(values Array<boolean|any>) -> result boolean
* and(...argsOrPromises, predicatesOrValues) -> Promise|boolean
* and(predicatesOrValues)(...args) -> Promise|boolean
* ```
*
* @description
* Function equivalent to the [Logical AND](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND) operator. Tests an array of predicate functions, promises, values, or a mix thereof.
*
* If provided an array of boolean values, `and` returns true if all boolean values are true.
*
* ```javascript [playground]
* const oneIsLessThanThree = 1 < 3
* const twoIsGreaterThanOne = 2 > 1
* const threeIsEqualToThree = 3 === 3
*
* const condition = and([
* oneIsLessThanThree,
* twoIsGreaterThanOne,
* threeIsEqualToThree
* ])
* console.log(condition) // true
* ```
*
* If any predicate functions are provided in the array, `and` returns an aggregate predicate function that returns true for a given set of arguments if all provided predicate functions test true. If any provided predicate functions are asynchronous, the aggregate predicate function becomes asynchronous.
*
* ```javascript [playground]
* const isOdd = number => number % 2 == 1
* const isPositive = number => number > 0
* const asyncIsLessThan3 = async number => number < 3
*
* const aggregatePredicate = and([
* true,
* isOdd,
* isPositive,
* asyncIsLessThan3,
* ])
*
* const condition = await aggregatePredicate(1)
* console.log(condition) // true
* ```
*
* Any promises passed in argument position are resolved for their values before further execution. This only applies to the eager version of the API.
*
* ```javascript [playground]
* and(Promise.resolve(5), [
* n => n > 0,
* n => n < 10,
* ]).then(console.log) // true
* ```
*
* See also:
* * [some](/docs/some)
* * [or](/docs/or)
* * [not](/docs/not)
* * [eq](/docs/eq)
*
* @execution series
*
* @note ...args slows down here by an order of magnitude
*/
const and = function (...args) {
const predicatesOrValues = args.pop()
if (areAllValuesNonfunctions(predicatesOrValues)) {
return areAllValuesTruthy(predicatesOrValues, -1)
}
if (args.length == 0) {
return curryArgs2(areAllPredicatesTruthy, __, predicatesOrValues)
}
if (areAnyValuesPromises(args)) {
return promiseAll(args)
.then(curry2(areAllPredicatesTruthy, __, predicatesOrValues))
}
return areAllPredicatesTruthy(args, predicatesOrValues)
}
module.exports = and