import type { Files } from './files';
import type { Pipe } from './index';
import type { Logger } from './logging';
import type { AbsolutePath } from './paths';
import type { Result } from './types';
/** A convenience type indicating what can be returned by a {@link Plug}. */
export type PlugResult = Files | undefined | void;
/**
 * The {@link Plug} interface describes _build plugin_.
 *
 * A {@link Plug} receives a {@link Files} instance in its input (for example
 * a list of _source `.ts` files_) and optionally produces a possibly different
 * list (for example the _compiled `.js` files_).
 */
export interface Plug<T extends PlugResult> {
    pipe(files: Files, context: Context): T | Promise<T>;
}
/** A type identifying a {@link Plug} as a `function` */
export type PlugFunction<T extends PlugResult> = Plug<T>['pipe'];
/**
 * The {@link Context} class defines the context in which a {@link Plug}
 * is invoked.
 */
export declare class Context {
    /** The absolute file name where the task was defined. */
    readonly buildFile: AbsolutePath;
    /** The _name_ of the task associated with this {@link Context}. */
    readonly taskName: string;
    /** The directory of the file where the task was defined (convenience). */
    readonly buildDir: AbsolutePath;
    /** The {@link Logger} associated with this instance. */
    readonly log: Logger;
    constructor(
    /** The absolute file name where the task was defined. */
    buildFile: AbsolutePath, 
    /** The _name_ of the task associated with this {@link Context}. */
    taskName: string);
    /**
     * Resolve a (set of) path(s) in this {@link Context}.
     *
     * If the path (or first component thereof) starts with `@...`, then the
     * resolved path will be relative to the directory containing the build file
     * where the current task was defined, otherwise it will be relative to the
     * current working directory.
     */
    resolve(path: string, ...paths: string[]): AbsolutePath;
}
/**
 * An internal class recording _hot_ (failure will fail the task) and _cold_
 * (failure will be ignored) {@link Promise}s for a task's {@link Context}.
 */
export declare class ContextPromises {
    readonly context: Context;
    private readonly _cold;
    private readonly _hot;
    private constructor();
    /** Track a {@link Promise} _hot_ (failure will fail the task) */
    hot(promise: Promise<Result>): void;
    /** Track a {@link Promise} _cold_ (failure will be ignored) */
    cold(promise: Promise<Result>): void;
    /**
     * Await all tracked {@link Promise}s, triggering a build failure if any of
     * the _hot_ ones is rejected.
     */
    static wait(context: Context): Promise<void>;
    /** Get a {@link ContextPromises} instance for the given {@link Context} */
    static get(context: Context): ContextPromises;
}
/** The default implementation of the {@link Pipe} interface. */
export interface PipeImpl extends Pipe {
}
/** The default implementation of the {@link Pipe} interface. */
export declare class PipeImpl implements Pipe {
    private readonly _context;
    private readonly _promise;
    readonly [Symbol.toStringTag] = "Pipe";
    constructor(_context: Context, _promise: Promise<Result>);
    then<R1 = Files, R2 = never>(onfulfilled?: ((value: Files) => R1 | PromiseLike<R1>) | null | undefined, onrejected?: ((reason: any) => R2 | PromiseLike<R2>) | null | undefined): Promise<R1 | R2>;
    catch<R = never>(onrejected?: ((reason: any) => R | PromiseLike<R>) | null | undefined): Promise<Files | R>;
    finally(onfinally?: (() => void) | null | undefined): Promise<Files>;
    plug(plug: Plug<Files>): Pipe;
    plug(plug: PlugFunction<Files>): Pipe;
    plug(plug: Plug<void | undefined>): Promise<undefined>;
    plug(plug: PlugFunction<void | undefined>): Promise<undefined>;
}
/** The names which can be installed as direct plugs. */
export type PlugName = string & Exclude<keyof Pipe, 'plug' | keyof Promise<any>>;
/** The parameters of the plug extension with the given name */
export type PipeParameters<Name extends PlugName> = PipeOverloads<Name>['args'];
/** Extract arguments and return types from function overloads. */
type PipeOverloads<Name extends PlugName> = Pipe[Name] extends {
    (...args: infer A0): infer R0;
    (...args: infer A1): infer R1;
    (...args: infer A2): infer R2;
    (...args: infer A3): infer R3;
    (...args: infer A4): infer R4;
} ? (R0 extends (Pipe | Promise<undefined>) ? {
    args: A0;
    ret: R0;
} : never) | (R1 extends (Pipe | Promise<undefined>) ? {
    args: A1;
    ret: R1;
} : never) | (R2 extends (Pipe | Promise<undefined>) ? {
    args: A2;
    ret: R2;
} : never) | (R3 extends (Pipe | Promise<undefined>) ? {
    args: A3;
    ret: R3;
} : never) | (R4 extends (Pipe | Promise<undefined>) ? {
    args: A4;
    ret: R4;
} : never) : Pipe[Name] extends {
    (...args: infer A0): infer R0;
    (...args: infer A1): infer R1;
    (...args: infer A2): infer R2;
    (...args: infer A3): infer R3;
} ? (R0 extends (Pipe | Promise<undefined>) ? {
    args: A0;
    ret: R0;
} : never) | (R1 extends (Pipe | Promise<undefined>) ? {
    args: A1;
    ret: R1;
} : never) | (R2 extends (Pipe | Promise<undefined>) ? {
    args: A2;
    ret: R2;
} : never) | (R3 extends (Pipe | Promise<undefined>) ? {
    args: A3;
    ret: R3;
} : never) : Pipe[Name] extends {
    (...args: infer A0): infer R0;
    (...args: infer A1): infer R1;
    (...args: infer A2): infer R2;
} ? (R0 extends (Pipe | Promise<undefined>) ? {
    args: A0;
    ret: R0;
} : never) | (R1 extends (Pipe | Promise<undefined>) ? {
    args: A1;
    ret: R1;
} : never) | (R2 extends (Pipe | Promise<undefined>) ? {
    args: A2;
    ret: R2;
} : never) : Pipe[Name] extends {
    (...args: infer A0): infer R0;
    (...args: infer A1): infer R1;
} ? (R0 extends (Pipe | Promise<undefined>) ? {
    args: A0;
    ret: R0;
} : never) | (R1 extends (Pipe | Promise<undefined>) ? {
    args: A1;
    ret: R1;
} : never) : Pipe[Name] extends {
    (...args: infer A0): infer R0;
} ? (R0 extends (Pipe | Promise<undefined>) ? {
    args: A0;
    ret: R0;
} : never) : never;
/** The parameters of the plug extension with the given name */
type PipeResult<Name extends PlugName> = PipeOverloads<Name>['ret'];
/**
 * A type defining the correct constructor for a {@link Plug}, inferring
 * argument types and instance type from the definitions in {@link Pipe}.
 */
type PlugConstructor<Name extends PlugName> = PipeResult<Name> extends Pipe ? new (...args: PipeParameters<Name>) => Plug<Files> : PipeResult<Name> extends Promise<undefined> ? new (...args: PipeParameters<Name>) => Plug<void | undefined> : PipeResult<Name> extends (Pipe | Promise<undefined>) ? new (...args: PipeParameters<Name>) => Plug<Files | void | undefined> : never;
/**
 * Install a {@link Plug} into our {@link Pipe} prototype.
 *
 * This allows our shorthand syntax for well-defined plugs such as:
 *
 * ```
 * find('./src', '*.ts').write('./target')
 * // Nicer and easier than...
 * find('./src', '*.ts').plug(new Write('./target'))
 * ```
 *
 * Use this alongside interface merging like:
 *
 * ```
 * declare module '@plugjs/plug/pipe' {
 *   export interface Pipe {
 *     write(): Pipe
 *   }
 * }
 *
 * install('write', class Write implements Plug {
 *   constructorg(...args: PipeParams<'write'>) {
 *     // here `args` is automatically inferred by whatever was declared above
 *   }
 *
 *   // ... the plug implementation lives here
 * })
 * ```
 */
export declare function install<Name extends PlugName, Ctor extends PlugConstructor<Name>>(name: Name, ctor: Ctor): void;
export {};
