/**
 * Tagged union in TypeScript.
 *
 * @packageDocumentation
 */

export declare type Enumerate<E extends EnumRecord> = {
    [Tag in keyof E]: E[Tag] extends infer Content extends [] | [any] ? Variant<E, Tag & string, Content> : never;
}[keyof E];

export declare type EnumOf<V extends VariantAny> = V extends Variant<infer E, any, any> ? E : never;

export declare type EnumRecord = Record<string, [] | [any]>;

/**
 * Tagged union in TypeScript.
 *
 * @packageDocumentation
 */
declare const enumTags: unique symbol;

/**
 * Rust's `Option<T>` analog
 *
 * @example
 *
 * ```ts
 * const maybeString: RustOption<string> = variant('None')
 * ```
 */
export declare type RustOption<T> = Enumerate<{
    None: [];
    Some: [T];
}>;

/**
 * Rust's `Result<Ok, Err>` analog
 *
 * @example
 *
 * ```ts
 * const file: RustResult<string, Error> = variant('Ok', 'file contents')
 * ```
 */
export declare type RustResult<Ok, Err> = Enumerate<{
    Ok: [Ok];
    Err: [Err];
}>;

export declare interface Variant<in out E extends EnumRecord, Tag extends string, in out Content extends [] | [any] = []> {
    [enumTags]: E;
    readonly tag: Tag;
    readonly content: Content extends [infer C] ? C : undefined;
    readonly unit: Content extends [] ? true : false;
    readonly as: <T extends keyof E>(tag: T) => [T, Content] extends [Tag, [infer C]] ? C : never;
}

export declare const variant: VariantFactoryFn;

export declare type VariantAny = Variant<any, any, any>;

export declare interface VariantFactoryFn {
    <V extends VariantAny>(...args: VariantToFactoryArgs<V>): V;
    <E extends EnumRecord>(...args: VariantToFactoryArgs<Enumerate<E>>): Enumerate<E>;
}

export declare type VariantToFactoryArgs<V extends VariantAny> = V extends Variant<any, infer Tag, infer Content> ? Content extends [infer C] ? [tag: Tag, content: C] : [tag: Tag] : never;

export { }
