import type { IsAnyFunc, IsArray, IsPlainObj } from '@rimbu/base';
import { type Patch } from './internal.cjs';
import type { Tuple } from './tuple.cjs';
export declare namespace Path {
    /**
     * A string representing a path into an (nested) object of type T.
     * @typeparam T - the object type to select in
     * @example
     * ```ts
     * const p: Path.Get<{ a: { b: { c : 5 } } }> = 'a.b'
     * ```
     */
    type Get<T> = Path.Internal.Generic<T, false, false, true>;
    /**
     * A string representing a path into an (nested) object of type T.
     * @typeparam T - the object type to select in
     * @example
     * ```ts
     * const p: Path.Set<{ a: { b: { c : 5 } } }> = 'a.b'
     * ```
     */
    type Set<T> = Path.Internal.Generic<T, true, false, true>;
    namespace Internal {
        /**
         * Determines the allowed paths into a value of type `T`.
         * @typeparam T - the source type
         * @typeparam Write - if true the path should be writable (no optional chaining)
         * @typeparam Maybe - if true the value at the current path is optional
         * @typeparam First - if true this is the root call
         * @note type is mapped as template literal to prevent non-string types to leak through
         */
        type Generic<T, Write extends boolean, Maybe extends boolean, First extends boolean = false> = `${IsAnyFunc<T> extends true ? '' : // empty string is always an option
        '' | Path.Internal.NonEmpty<T, Write, Maybe, First>}`;
        /**
         * Determines the allowed non-empty paths into a value of type `T`.
         * @typeparam T - the source type
         * @typeparam Write - if true the path should be writable (no optional chaining)
         * @typeparam Maybe - if true the value at the current path is optional
         * @typeparam First - if true this is the root call
         */
        type NonEmpty<T, Write extends boolean, Maybe extends boolean, First extends boolean> = Path.Internal.IsOptional<T> extends true ? Write extends false ? Path.Internal.Generic<Exclude<T, undefined | null>, Write, true> : never : `${Path.Internal.Separator<First, Maybe, IsArray<T>>}${Path.Internal.NonOptional<T, Write, Maybe>}`;
        /**
         * Determines the allowed paths into a non-optional value of type `T`.
         * @typeparam T - the source type
         * @typeparam Write - if true the path should be writable (no optional chaining)
         * @typeparam Maybe - if true the value at the current path is optional
         */
        type NonOptional<T, Write extends boolean, Maybe extends boolean> = Tuple.IsTuple<T> extends true ? Path.Internal.Tup<T, Write, Maybe> : T extends readonly any[] ? Write extends false ? Path.Internal.Arr<T> : never : IsPlainObj<T> extends true ? Path.Internal.Obj<T, Write, Maybe> : never;
        /**
         * Determines the allowed paths for a tuple. Since tuples have fixed types, they do not
         * need to be optional, in contrast to arrays.
         * @typeparam T - the input tuple type
         * @typeparam Write - if true the path should be writable (no optional chaining)
         * @typeparam Maybe - if true the value at the current path is optional
         */
        type Tup<T, Write extends boolean, Maybe extends boolean> = {
            [K in Tuple.KeysOf<T>]: `[${K}]${Path.Internal.Generic<T[K], Write, Maybe>}`;
        }[Tuple.KeysOf<T>];
        /**
         * Determines the allowed paths for an array.
         * @typeparam T - the input array type
         */
        type Arr<T extends readonly any[]> = `[${number}]${Path.Internal.Generic<T[number], false, true>}`;
        /**
         * Determines the allowed paths for an object.
         * @typeparam T - the input object type
         * @typeparam Write - if true the path should be writable (no optional chaining)
         * @typeparam Maybe - if true the value at the current path is optional
         */
        type Obj<T, Write extends boolean, Maybe extends boolean> = {
            [K in keyof T]: `${K & string}${Path.Internal.Generic<T[K], Write, Write extends true ? false : Path.Internal.IsOptional<T[K], true, Maybe>>}`;
        }[keyof T];
        /**
         * Determines the allowed path part separator based on the input types.
         * @typeparam First - if true, this is the first call
         * @typeparam Maybe - if true, the value is optional
         * @typeparam IsArray - if true, the value is an array
         */
        type Separator<First extends boolean, Maybe extends boolean, IsArray extends boolean> = Maybe extends true ? First extends true ? never : '?.' : First extends true ? '' : IsArray extends true ? '' : '.';
        /**
         * Determines whether the given type `T` is optional, that is, whether it can be null or undefined.
         * @typeparam T - the input type
         * @typeparam True - the value to return if `T` is optional
         * @typeparam False - the value to return if `T` is mandatory
         */
        type IsOptional<T, True = true, False = false> = undefined extends T ? True : null extends T ? True : False;
        /**
         * Returns type `T` if `Maybe` is false, `T | undefined` otherwise.
         * @typeparam T - the input type
         * @typeparam Maybe - if true, the return type value should be optional
         */
        type MaybeValue<T, Maybe extends boolean> = Maybe extends true ? T | undefined : T;
        /**
         * Utility type to only add non-empty string types to a string array.
         * @typeparam A - the input string array
         * @typeparam T - the string value to optionally add
         */
        type AppendIfNotEmpty<A extends string[], T extends string> = T extends '' ? A : [
            ...A,
            T
        ];
    }
    /**
     * The result type when selecting from object type T a path with type P.
     * @typeparam T - the object type to select in
     * @typeparam P - a Path in object type T
     * @example
     * ```ts
     * let r!: Path.Result<{ a: { b: { c: number } } }, 'a.b'>;
     * // => type of r: { c: number }
     * ```
     */
    type Result<T, P extends string> = Path.Result.For<T, Path.Result.Tokenize<P>, false>;
    namespace Result {
        /**
         * Determines the result type for an array of tokens representing subpaths in type `T`.
         * @typeparam T - the current source type
         * @typeparam Tokens - an array of elements indicating a path into the source type
         * @typeparam Maybe - if true indicates that the path may be undefined
         */
        type For<T, Tokens, Maybe extends boolean = Path.Internal.IsOptional<T>> = Tokens extends [] ? Path.Internal.MaybeValue<T, Maybe> : Path.Internal.IsOptional<T> extends true ? Path.Result.For<Exclude<T, undefined | null>, Tokens, Maybe> : Tokens extends ['?.', infer Key, ...infer Rest] ? Path.Result.For<Path.Result.Part<T, Key, Maybe>, Rest, true> : Tokens extends ['.', infer Key, ...infer Rest] ? Path.Result.For<Path.Result.Part<T, Key, false>, Rest, Maybe> : Tokens extends [infer Key, ...infer Rest] ? Path.Result.For<Path.Result.Part<T, Key, false>, Rest, Maybe> : never;
        /**
         * Determines the result of getting the property/index `K` from type `T`, taking into
         * account that the value may be optional.
         * @typeparam T - the current source type
         * @typeparam K - the key to get from the source type
         * @typeparam Maybe - if true indicates that the path may be undefined
         */
        type Part<T, K, Maybe extends boolean> = IsArray<T> extends true ? Path.Internal.MaybeValue<T[K & keyof T], Tuple.IsTuple<T> extends true ? Maybe : true> : Path.Internal.MaybeValue<T[K & keyof T], Maybe>;
        /**
         * Converts a path string into separate tokens in a string array.
         * @typeparam P - the literal string path type
         * @typeparam Token - the token currently being produced
         * @typeparam Res - the resulting literal string token array
         */
        type Tokenize<P extends string, Token extends string = '', Res extends string[] = []> = P extends '' ? Path.Internal.AppendIfNotEmpty<Res, Token> : P extends `[${infer Index}]${infer Rest}` ? Tokenize<Rest, '', [
            ...Path.Internal.AppendIfNotEmpty<Res, Token>,
            Index
        ]> : P extends `?.${infer Rest}` ? Tokenize<Rest, '', [
            ...Path.Internal.AppendIfNotEmpty<Res, Token>,
            '?.'
        ]> : P extends `.${infer Rest}` ? Tokenize<Rest, '', [
            ...Path.Internal.AppendIfNotEmpty<Res, Token>,
            '.'
        ]> : P extends `${infer First}${infer Rest}` ? Tokenize<Rest, `${Token}${First}`, Res> : never;
    }
    /**
     * Regular expression used to split a path string into tokens.
     */
    const stringSplitRegex: RegExp;
    /**
     * The allowed values of a split path.
     */
    type StringSplit = (string | number | undefined)[];
    /**
     * Return the given `path` string split into an array of subpaths.
     * @param path - the input string path
     */
    function stringSplit(path: string): Path.StringSplit;
}
/**
 * Returns the value resulting from selecting the given `path` in the given `source` object.
 * It supports optional chaining for nullable values or values that may be undefined, and also
 * for accessing objects inside an array.
 * There is currently no support for forcing non-null (the `!` operator).
 * @typeparam T - the object type to select in
 * @typeparam P - a Path in object type T
 * @param source - the object to select in
 * @param path - the path into the object
 * @example
 * ```ts
 * const value = { a: { b: { c: [{ d: 5 }, { d: 6 }] } } }
 * Deep.getAt(value, 'a.b');
 * // => { c: [{ d: 5 }, { d: 6 }] }
 * Deep.getAt(value, 'a.b.c');
 * // => [{ d: 5 }, { d: 6 }]
 * Deep.getAt(value, 'a.b.c[1]');
 * // => { d: 6 }
 * Deep.getAt(value, 'a.b.c[1]?.d');
 * // => 6
 * ```
 */
export declare function getAt<T, P extends Path.Get<T>>(source: T, path: P): Path.Result<T, P>;
/**
 * Patches the value at the given path in the source to the given value.
 * Because the path to update must exist in the `source` object, optional
 * chaining and array indexing is not allowed.
 * @param source - the object to update
 * @param path - the path in the object to update
 * @param patchItem - the patch for the value at the given path
 * @example
 * ```ts
 * const value = { a: { b: { c: 5 } } };
 * Deep.patchAt(value, 'a.b.c', v => v + 5);
 * // => { a: { b: { c: 6 } } }
 * ```
 */
export declare function patchAt<T, P extends Path.Set<T>, C = Path.Result<T, P>>(source: T, path: P, patchItem: Patch<Path.Result<T, P>, Path.Result<T, P> & C>): T;
