import { callIfFunction , getDeepClone , getFullPath , getMergedObject , getValueAtPath , getNewEmptyObject , isCloneable , isEmptyPath , isSameValueZero , splice , throwInvalidFnError , } from './utils' ; const { isArray } = Array ; const slice = Function . prototype . bind . call ( Function . prototype . call , Array . prototype . slice ) ; function description param isWithHandler returns export function createCall < IsWith extends true | false > ( isWithHandler ) ; export function createCall < IsWith extends true | false > ( isWithHandler ) { if ( isWithHandler ) { return function callWith ( fn , path , parameters , object , context = object ) { if ( typeof fn !== 'function' ) { throwInvalidFnError ( ) ; } const extraArgs = slice ( arguments , 5 ) ; if ( isEmptyPath ( path ) ) { return callIfFunction ( fn ( object , ... extraArgs ) , context , parameters ) ; } const value = getValueAtPath ( path , object ) ; if ( value === void 0 ) { return ; } const result = fn ( value , ... extraArgs ) ; return callIfFunction ( result , context , parameters ) ; } ; } return function call ( path , parameters , object , context = object ) { const callable = isEmptyPath ( path ) ? object : getValueAtPath ( path , object ) ; return callIfFunction ( callable , context , parameters ) ; } ; } function description param isWithHandler returns export function createGet < IsWith extends true | false > ( isWithHandler ) ; export function createGet < IsWith extends true | false > ( isWithHandler ) { if ( isWithHandler ) { return function getWith ( fn , path , object ) { if ( typeof fn !== 'function' ) { throwInvalidFnError ( ) ; } const extraArgs = slice ( arguments , 4 ) ; if ( isEmptyPath ( path ) ) { return fn ( object , ... extraArgs ) ; } const value = getValueAtPath ( path , object ) ; return value === void 0 ? value : fn ( value , ... extraArgs ) ; } ; } return function get ( path , object ) { return isEmptyPath ( path ) ? object : getValueAtPath ( path , object ) ; } ; } function description param isWithHandler returns export function createGetOr < IsWith extends true | false > ( isWithHandler ) ; export function createGetOr < IsWith extends true | false > ( isWithHandler ) { if ( isWithHandler ) { return function getWithOr ( fn , noMatchValue , path , object ) { if ( typeof fn !== 'function' ) { throwInvalidFnError ( ) ; } const extraArgs = slice ( arguments , 4 ) ; if ( isEmptyPath ( path ) ) { return fn ( object , ... extraArgs ) ; } const value = getValueAtPath ( path , object ) ; return value === void 0 ? noMatchValue : fn ( value , ... extraArgs ) ; } ; } return function getOr ( noMatchValue , path , object ) { return isEmptyPath ( path ) ? object : getValueAtPath ( path , object , noMatchValue ) ; } ; } function description param isWithHandler returns export function createHas < IsWith extends true | false > ( isWithHandler ) ; export function createHas < IsWith extends true | false > ( isWithHandler ) { if ( isWithHandler ) { return function hasWith ( fn , path , object ) { if ( typeof fn !== 'function' ) { throwInvalidFnError ( ) ; } const extraArgs = slice ( arguments , 3 ) ; if ( isEmptyPath ( path ) ) { return ! ! fn ( object , ... extraArgs ) ; } const value = getValueAtPath ( path , object ) ; return value !== void 0 && ! ! fn ( value , ... extraArgs ) ; } ; } return function has ( path , object ) { return isEmptyPath ( path ) ? object != null : getValueAtPath ( path , object ) !== void 0 ; } ; } function description param isWithHandler returns export function createIs < IsWith extends true | false > ( isWithHandler ) ; export function createIs < IsWith extends true | false > ( isWithHandler ) { if ( isWithHandler ) { return function isWith ( fn , path , value , object ) { if ( typeof fn !== 'function' ) { throwInvalidFnError ( ) ; } const extraArgs = slice ( arguments , 4 ) ; if ( isEmptyPath ( path ) ) { return isSameValueZero ( fn ( object , ... extraArgs ) , value ) ; } return isSameValueZero ( fn ( getValueAtPath ( path , object ) , ... extraArgs ) , value ) ; } ; } return function is ( path , value , object ) { const _path = isEmptyPath ( path ) ? object : getValueAtPath ( path , object ) ; return isSameValueZero ( _path , value ) ; } ; } function description param isWithHandler param isDeep returns export function createMerge < IsWith extends true | false > ( isWithHandler , isDeep ) ; export function createMerge < IsWith extends true | false > ( isWithHandler , isDeep ) { if ( isWithHandler ) { return function mergeWith ( fn , path , object ) : unchanged . Unchangeable { if ( typeof fn !== 'function' ) { throwInvalidFnError ( ) ; } const extraArgs = slice ( arguments , 3 ) ; if ( ! isCloneable ( object ) ) { return fn ( object , ... extraArgs ) ; } if ( isEmptyPath ( path ) ) { const objectToMerge = fn ( object , ... extraArgs ) ; return objectToMerge ? getMergedObject ( object , objectToMerge , isDeep ) : object ; } let hasChanged = false ; const result = getDeepClone ( path , object , ( ref , key ) : void => { const objectToMerge = fn ( ref [ key ] , ... extraArgs ) ; if ( objectToMerge ) { ref [ key ] = getMergedObject ( ref [ key ] , objectToMerge , isDeep ) ; hasChanged = true ; } } ) ; return hasChanged ? result : object ; } ; } return function merge ( path , objectToMerge , object ) : unchanged . Unchangeable { if ( ! isCloneable ( object ) ) { return objectToMerge ; } return isEmptyPath ( path ) ? getMergedObject ( object , objectToMerge , true ) : getDeepClone ( path , object , ( ref , key ) : void => { ref [ key ] = getMergedObject ( ref [ key ] , objectToMerge , isDeep ) ; } ) ; } ; } function description param isWithHandler returns export function createNot < IsWith extends true | false > ( isWithHandler ) ; export function createNot < IsWith extends true | false > ( isWithHandler ) { const is = createIs ( isWithHandler ) ; return function not ( this ) { return ! is . apply ( this , arguments ) ; } ; } function description param isWithHandler returns export function createRemove < IsWith extends true | false > ( isWithHandler ) ; export function createRemove < IsWith extends true | false > ( isWithHandler ) { if ( isWithHandler ) { return function removeWith ( fn , path , object ) : unchanged . Unchangeable { if ( typeof fn !== 'function' ) { throwInvalidFnError ( ) ; } const extraArgs = slice ( arguments , 3 ) ; if ( isEmptyPath ( path ) ) { const emptyObject = getNewEmptyObject ( object ) ; return fn ( emptyObject , ... extraArgs ) ? emptyObject : object ; } const value = getValueAtPath ( path , object ) ; return value !== void 0 && fn ( value , ... extraArgs ) ? getDeepClone ( path , object , ( ref , key ) : void => { if ( isArray ( ref ) ) { splice ( ref , key as number ) ; } else { delete ref [ key ] ; } } ) : object ; } ; } return function remove ( path , object ) : unchanged . Unchangeable { if ( isEmptyPath ( path ) ) { return getNewEmptyObject ( object ) ; } return getValueAtPath ( path , object ) !== void 0 ? getDeepClone ( path , object , ( ref , key ) : void => { if ( isArray ( ref ) ) { splice ( ref , key as number ) ; } else { delete ref [ key ] ; } } ) : object ; } ; } function description param isWithHandler returns export function createSet < IsWith extends true | false > ( isWithHandler ) ; export function createSet < IsWith extends true | false > ( isWithHandler ) { if ( isWithHandler ) { return function setWith ( fn , path , object ) : unchanged . Unchangeable { if ( typeof fn !== 'function' ) { throwInvalidFnError ( ) ; } const extraArgs = slice ( arguments , 3 ) ; return isEmptyPath ( path ) ? fn ( object , ... extraArgs ) : getDeepClone ( path , object , ( ref , key ) : void => { ref [ key ] = fn ( ref [ key ] , ... extraArgs ) ; } ) ; } ; } return function set ( path , value , object ) : unchanged . Unchangeable { return isEmptyPath ( path ) ? value : getDeepClone ( path , object , ( ref , key ) : void => { ref [ key ] = value ; } ) ; } ; } function description param isWithHandler returns export function createAdd < IsWith extends true | false > ( isWithHandler ) ; export function createAdd < IsWith extends true | false > ( isWithHandler ) { const _add = createSet ( isWithHandler ) ; if ( isWithHandler ) { return function addWith ( this , fn , path , object ) : unchanged . Unchangeable { return _add . apply ( this , [ fn , getFullPath ( path , object , fn ) , object ] . concat ( slice ( arguments , 3 ) ) ) ; } ; } return function add ( path , value , object ) : unchanged . Unchangeable { return _add ( getFullPath ( path , object ) , value , object ) ; } ; }