const { isArray } = Array;
import isObject from './functional/isObject.js';
import isString from './functional/isString.js';
import isUndefined from './functional/isUndefined.js';
import property from './functional/property.js';
import { isLensClass, at_maybe } from '../src-cjs/constants.js';
export const isLens = property(isLensClass);
export const $iterator = property(Symbol.iterator);
export function index_maybe(subject, key) {
return isObject(subject) ? subject[at_maybe](key) : {};
}
export function getIterator(val) {
if (isString(val)) {
return;
}
return $iterator(val);
}
/**
* @typedef {Array} FoundPair
* @property {*} 0 Value of found item
* @property {number|string} [1] Index or key of found item
*/
/**
* @generator
* @function module:natural-lenses#eachFound
* @summary Iterate over Maybe monad contained value(s)
* @param {Maybe.<*>} maybe_val Maybe monad of value or iterable value
* @yields {FoundPair} Pairs of value and key
* @see {@link module:natural-lenses#maybeDo}
*
* @description
* When called on the result of [getting a multifocal into a Maybe monad]{@link AbstractNFocal#get_maybe},
* this function iterates found values (and only the found values), yielding the
* pair of the value and index for each, like the arguments to the callback of
* `Array.prototype.forEach` if it used "rest" syntax, e.g.
* ```js
* values.forEach((...pair) => {
* const [value, index] = pair;
* // do something with index and value
* });
* ```
* When called on the `get_maybe`-result of an {@link ArrayNFocal}, the index 1
* value of each yielded Array will be an integer index; with an {@link ObjectNFocal},
* the index 1 value of each yeilded Array will be a string key.
*
* This function can also be applied to a {@link Maybe} monad value obtained from
* a monofocal optic (e.g. a {@link Lens}), in which case it yields either a
* single element array containing the value if the input holds a `just` property
* and does not yield, otherwise. It is more flexible to apply
* [maybeDo]{@link module:natural-lenses#maybeDo} or {@link Optic#getting}, as
* these allow separate handling of the *Nothing* case.
*/
export function eachFound(maybe_val) {
if (!('just' in maybe_val)) {
return makeIterable(() => ({done: true}));
}
const val = maybe_val.just;
if (!maybe_val.multiFocal) {
let i = 0;
return makeIterable(() => {
if (i === 0) {
++i;
return {done: false, value: [val]};
} else {
return {done: true};
}
});
}
if (isArray(val)) {
let i = 0;
return makeIterable(() => {
while (i < val.length && !(i in val)) {
++i;
}
if (i < val.length) {
const iterVal = [val[i], i];
++i;
return {done: false, value: iterVal};
} else {
return {done: true};
}
});
} else /* istanbul ignore else: unsupported case */ if (isObject(val)) {
const entries = Object.entries(val);
let i = 0;
return makeIterable(() => {
if (i >= entries.length) {
return {done: true};
}
const iterVal = {done: false, value: entries[i].reverse()};
++i;
return iterVal;
});
} else {
let i = 0;
return makeIterable(() => {
if (i === 0) {
++i;
return {done: false, value: [val]};
} else {
return {done: true};
}
});
}
}
function makeIterable(next) {
return {[Symbol.iterator]: () => ({ next })};
}
/**
* @template T, U
* @function module:natural-lenses#maybeDo
* @summary Conditionally execute a function based on the construction of a {@link Maybe}
* @param {Maybe.<T>} maybe The input value determining which of the following arguments is invoked
* @param {function(T): U} then The function executed with the `just` value of *maybe*, if present
* @param {function(): U} [orElse] The function executed if *maybe* contains no `just` property
* @returns {U} The result type of the invoked function
* @see Optic#getting
*
* @description
* This function resembles an `if` statement around the "*Just*-ness" of a {@link Maybe}
* value: the *then* Function gets called if *maybe* has a `just` and the *orElse*
* Function if not. Because of the usual intent of this conditional situation,
* `maybe.just` value is passed to *then* if *then* is called.
*
* Whichever of *then* or *orElse* is called, its return value becomes the return
* value of this function call.
*/
export function maybeDo(maybe, then, orElse) {
return ('just' in maybe) ? then(maybe.just) : (orElse ? orElse() : undefined);
}
export const lensCap = {
[isLensClass]: true,
get: function () {},
get_maybe: function() {return {};}
};
export function incorporateStdlibSupport(targetClass, methods) {
const classProto = targetClass.prototype;
methods.forEach(([sym, method]) => {
if (!classProto.hasOwnProperty(sym)) {
Object.defineProperty(classProto, sym, {
configurable: true,
writable: true,
value: method,
});
}
});
}
export function handleNoniterableValue(excVal, maybeVal) {
if (isUndefined(excVal) || !('just' in maybeVal)) {
return;
}
if (isObject(excVal)) {
excVal.noniterableValue = maybeVal.just;
}
throw excVal;
}