import * as Assert from './assert';
import * as Util from './utils';
declare type Processor<T, R = T> = (value: T) => R;
declare type CustomProcessor<T, R = T> = (value: T, path: string) => R;
export declare enum MiddlewareStep {
    Before = 0,
    After = 1
}
/**
 * Predicate over serialization/deserialization target and metadata
 * @param value The value being processed
 * @param typeName The name of the type of the value being processed
 * @param isNonNullable Is the value non nullable
 * @param isBuiltInType Is the type a built-in type (versus a custom, user-defined type)
 */
declare type TestPredicate<T> = (value: T, typeName: string, isNonNullable: boolean, isBuiltInType: boolean) => boolean;
export declare type Serialized<DomObj> = {
    [K in keyof DomObj]: DomObj[K] extends Set<infer T> ? T[] : DomObj[K] extends Map<any, infer B> ? ({
        [key: string]: B;
    } | Map<any, B>) : DomObj[K] extends IObjectAny ? Serialized<DomObj[K]> : DomObj[K];
};
interface IMarshaller {
    /**
     * Restart the marshaller configuration.
     * This will restart all middleware and serializer/deserializer configuration.
     *
     * Primarily meant for usage in tests.
     */
    clear: () => this;
    /**
     * Register a serializer for the type name.
     * The type name is case-insensitive.
     * This function will be used when calling `serialize` with the same type name.
     *
     * Serializers are allowed to (and encouraged to) throw errors if they encounter invalid data
     *
     * @param typeName Type name for which the serializer is being registered
     * @param serializer Function to be called during serialization
     */
    registerSerializer: <T, R = T>(typeName: string, serializer: Processor<T, R>) => this;
    /**
     * Register a deserializer for the type name.
     * The type name is case-insensitive.
     * This function will be used when calling `deserialize` with the same type name.
     *
     * Deserializers are allowed to (and encouraged to) throw errors if they encounter invalid data
     *
     * @param typeName Type name for which the deserializer is being registered
     * @param deserializer Function to be called during deserialization
     */
    registerDeserializer: <T, R = T>(typeName: string, serializer: Processor<T, R>) => this;
    /**
     * Register a middleware function that will be called during serialization.
     * Any number of functions may be registered as middleware, and will be called in the order they were specified in.
     * Middleware will be invoked _before_ any `serialize` function.
     * Middleware will be invoked every time `serialize` is called, but the execution of the processor function can be guarded:
     *  - If the `test` function _is not_ specified, `process` will be invoked on every serialization attempt
     *  - If the `test` function _is_ specified, `process` will be called only when `test` returns `true`
     *
     * @param process Function to be invoked to transform values _before_ serialization. It receives only the value.
     * @param test Optional function to guard against applying the middleware. When not specified, it behaves as though it always returned `true`. It receives the value, the type name, and a boolean indicating whether the value is required.
     */
    registerSerializerMiddleware: <T, R = T>(process: Processor<T, R>, test?: TestPredicate<T>, when?: MiddlewareStep) => this;
    /**
     * Register a middleware function that will be called during deserialization.
     * Any number of functions may be registered as middleware, and will be called in the order they were specified in.
     * Middleware will be invoked _before_ or _after_ any `deserialize` function, depending on the configuration
     * Middleware will be invoked every time `deserialize` is called, but the execution of the processor function can be guarded:
     *  - If the `test` function _is not_ specified, `process` will be invoked on every deserialization attempt
     *  - If the `test` function _is_ specified, `process` will be called only when `test` returns `true`
     *
     * @param process Function to be invoked to transform values _before_ deserialization. It receives only the value.
     * @param test Optional function to guard against applying the middleware. When not specified, it behaves as though it always returned `true`. It receives the value, the type name, and a boolean indicating whether the value is required.
     */
    registerDeserializerMiddleware: <T, R = T>(process: Processor<T, R>, test?: TestPredicate<T>, when?: MiddlewareStep) => this;
    /**
     * Serializes a value for sending it via some communication channel.
     * The serializer will be resolved via `typeName`, in a case-insensitive manner, as defined through `registerSerializer`.
     * If no serializer was registered, an identity function will be used.
     * Before or after serialization, depending on the flag, all serialization middleware are invoked, unless their guard indicates that they should not be.
     *
     * If `value` is undefined/null and `isNonNullable` is `true`, an error will be thrown.
     * If `value` is undefined/null and `isNonNullable` is `false`, `undefined` will be returned.
     * Otherwise, the serializer will be executed.
     * If an error occurs, an error with additional details and `path` will be thrown.
     *
     * @param value The value to serialize
     * @param typeName The name of the type being serializer, and for which to resolve a serializer
     * @param isNonNullable Is the value non nullable
     * @param path The JSON path of the value in the root object, for error reporting
     * @param isBuiltInType Is the type a built-in type (versus custom, user-defined)
     */
    serialize: <T, R = T>(value: T | undefined | null, typeName: string, isNonNullable: boolean, path: string, isBuiltInType: boolean) => R | undefined;
    /**
     * Serializes a value for sending it via some communication channel.
     * The serializer is passed explicitly.
     * Before serialization, all serialization middleware are invoked, unless their guard indicates that they should not be.
     *
     * If `value` is undefined/null and `isNonNullable` is `true`, an error will be thrown.
     * If `value` is undefined/null and `isNonNullable` is `false`, `undefined` will be returned.
     * Otherwise, the serializer will be executed.
     * If an error occurs, an error with additional details and `path` will be thrown.
     *
     * @param value The value to serialize
     * @param typeName The name of the type being serializer, used in guards only
     * @param formatter The function used as a serializer
     * @param isNonNullable Is the value non nullable
     * @param path The JSON path of the value in the root object, for error reporting
     * @param isBuiltInType Is the type a built-in type (versus custom, user-defined)
     */
    serializeWith: <T, R = T>(value: T | undefined | null, typeName: string, formatter: CustomProcessor<T, R>, isNonNullable: boolean, path: string, isBuiltInType: boolean) => R | undefined;
    /**
     * Deserializes a value for receiving via some communication channel.
     * The deserializer will be resolved via `typeName`, in a case-insensitive manner, as defined through `registerDeserializer`.
     * If no deserializer was registered, an identity function will be used.
     * Before deserialization, all deserialization middleware are invoked, unless their guard indicates that they should not be.
     *
     * If `value` is undefined/null and `isNonNullable` is `true`, an error will be thrown.
     * If `value` is undefined/null and `isNonNullable` is `false`, `undefined` will be returned.
     * Otherwise, the deserializer will be executed.
     * If an error occurs, an error with additional details and `path` will be thrown.
     *
     * @param value The value to deserialize
     * @param typeName The name of the type being deserializer, and for which to resolve a deserializer
     * @param isNonNullable Is the value non nullable
     * @param path The JSON path of the value in the root object, for error reporting
     * @param isBuiltInType Is the type a built-in type (versus custom, user-defined)
     */
    deserialize: <T, R = T>(value: T | undefined | null, typeName: string, isNonNullable: boolean, path: string, isBuiltInType: boolean) => R | undefined;
    /**
     * Deserializes a value for receiving via some communication channel.
     * The deserializer is passed explicitly.
     * Before deserialization, all deserialization middleware are invoked, unless their guard indicates that they should not be.
     *
     * If `value` is undefined/null and `isNonNullable` is `true`, an error will be thrown.
     * If `value` is undefined/null and `isNonNullable` is `false`, `undefined` will be returned.
     * Otherwise, the deserializer will be executed.
     * If an error occurs, an error with additional details and `path` will be thrown.
     *
     * @param value The value to deserialize
     * @param typeName The name of the type being deserializer, used in guards only
     * @param formatter The function used as a deserializer
     * @param isNonNullable Is the value non nullable
     * @param path The JSON path of the value in the root object, for error reporting
     * @param isBuiltInType Is the type a built-in type (versus custom, user-defined)
     */
    deserializeWith: <T, R = T>(value: T | undefined | null, typeName: string, formatter: CustomProcessor<T, R>, isNonNullable: boolean, path: string, isBuiltInType: boolean) => R | undefined;
}
export declare class Marshaller implements IMarshaller {
    /**
     * Serialization utilities, re-exported for easier access in generated code
     */
    static Util: typeof Util;
    static Assert: typeof Assert;
    /**
     * Get an instance of the marshaller.
     * The marshaller is a singleton.
     */
    static getInstance(): Marshaller;
    private static instance;
    private serializeMiddleware;
    private deserializeMiddleware;
    private serializerLookup;
    private deserializerLookup;
    /**
     * Marshaller is a singleton, ergo private constructor
     */
    private constructor();
    registerSerializer<T, R = T>(typeName: string, serializer: Processor<T, R>): this;
    registerDeserializer<T, R = T>(typeName: string, deserializer: Processor<T, R>): this;
    /**
     * Shorthand for registering both the serializer and deserializer
     * @param typeName Case-insensitive type name
     * @param formatter Function for formatting and validating the input
     */
    registerFormatter<T, R = T>(typeName: string, formatter: Processor<T, R>): this;
    registerSerializerMiddleware<T, R = T>(process: Processor<T, R>, test?: TestPredicate<T>, when?: MiddlewareStep): this;
    registerDeserializerMiddleware<T, R = T>(process: Processor<T, R>, test?: TestPredicate<T>, when?: MiddlewareStep): this;
    clear(): this;
    serializeWith<T, R = T>(value: T | undefined | null, typeName: string, formatter: CustomProcessor<T, R>, isNonNullable: boolean, path: string, isBuiltInType?: boolean): R | undefined;
    deserializeWith<T, R = T>(value: T | undefined | null, typeName: string, formatter: CustomProcessor<T, R>, isNonNullable: boolean, path: string, isBuiltInType?: boolean): R | undefined;
    serialize<T, R = T>(value: T | undefined | null, typeName: string, isNonNullable: boolean, path: string, isBuiltInType?: boolean): R | undefined;
    deserialize<T, R = T>(value: T | undefined | null, typeName: string, isNonNullable: boolean, path: string, isBuiltInType?: boolean): R | undefined;
    protected resolveSerializer<T, R = T>(typeName: string): Processor<T, R>;
    protected resolveDeserializer<T, R = T>(typeName: string): Processor<T, R>;
}
export {};
