// This file was autogenerated by the `uniffi-bindgen-gecko-js` crate.
// Trust me, you don't want to mess with it!

import {
  ArrayBufferDataStream,
  FfiConverter,
  FfiConverterArrayBuffer,
  FfiConverterInt8,
  FfiConverterUInt8,
  FfiConverterInt16,
  FfiConverterUInt16,
  FfiConverterInt32,
  FfiConverterUInt32,
  FfiConverterInt64,
  FfiConverterUInt64,
  FfiConverterFloat32,
  FfiConverterFloat64,
  FfiConverterBoolean,
  FfiConverterBytes,
  FfiConverterString,
  UniFFICallbackHandler,
  UniFFICallbackMethodHandler,
  UniFFIError,
  UniFFIInternalError,
  UniFFITypeError,
  constructUniffiObject,
  handleRustResult,
  uniffiObjectPtr,
} from "moz-src:///toolkit/components/uniffi-js/js/UniFFI.sys.mjs";

// Objects intended to be used in the unit tests
export var UnitTestObjs = {
    uniffiObjectPtr,
};
/**
 * Allow non-HTTPS requests to the emulator loopback URL
 */
export function allowAndroidEmulatorLoopback() {
   
const result = UniFFIScaffolding.callSync(
    226, // uniffi_viaduct_fn_func_allow_android_emulator_loopback
)
return handleRustResult(
    result,
    (result) => undefined,
    null,
)
}

/**
 * Clear all OHTTP channel configurations
 */
export function clearOhttpChannels() {
   
const result = UniFFIScaffolding.callSync(
    227, // uniffi_viaduct_fn_func_clear_ohttp_channels
)
return handleRustResult(
    result,
    (result) => undefined,
    null,
)
}

/**
 * Configure default OHTTP channels for common Mozilla services
 * This sets up:
 * - "relay1": For general telemetry and services through Mozilla's shared gateway
 * - "merino": For Firefox Suggest recommendations through Merino's dedicated relay/gateway
 */
export function configureDefaultOhttpChannels() {
   
const result = UniFFIScaffolding.callSync(
    228, // uniffi_viaduct_fn_func_configure_default_ohttp_channels
)
return handleRustResult(
    result,
    (result) => undefined,
    FfiConverterTypeViaductError.lift.bind(FfiConverterTypeViaductError),
)
}

/**
 * Configure an OHTTP channel with the given configuration
 * If an existing OHTTP config exists with the same name, it will be overwritten
 * @param {string} channel
 * @param {OhttpConfig} config
 */
export function configureOhttpChannel(
    channel, 
    config) {
   
FfiConverterString.checkType(channel);
FfiConverterTypeOhttpConfig.checkType(config);
const result = UniFFIScaffolding.callSync(
    229, // uniffi_viaduct_fn_func_configure_ohttp_channel
    FfiConverterString.lower(channel),
    FfiConverterTypeOhttpConfig.lower(config),
)
return handleRustResult(
    result,
    (result) => undefined,
    FfiConverterTypeViaductError.lift.bind(FfiConverterTypeViaductError),
)
}

/**
 * initBackend
 * @param {Backend} backend
 */
export function initBackend(
    backend) {
   
FfiConverterTypeBackend.checkType(backend);
const result = UniFFIScaffolding.callSync(
    230, // uniffi_viaduct_fn_func_init_backend
    FfiConverterTypeBackend.lower(backend),
)
return handleRustResult(
    result,
    (result) => undefined,
    null,
)
}

/**
 * List all configured OHTTP channels
 * @returns {Array.<string>}
 */
export function listOhttpChannels() {
   
const result = UniFFIScaffolding.callSync(
    231, // uniffi_viaduct_fn_func_list_ohttp_channels
)
return handleRustResult(
    result,
    FfiConverterSequenceString.lift.bind(FfiConverterSequenceString),
    null,
)
}

/**
 * Send a request through an OHTTP channel.
 * 
 * This encrypts the request and routes it through the configured OHTTP
 * relay/gateway for the specified channel.
 * 
 * # Arguments
 * * `request` - The request to send
 * * `channel` - The name of the OHTTP channel to use (e.g., "merino")
 * 
 * # Example (Kotlin)
 * ```kotlin
 * val response = sendOhttpRequest(
 * Request(
 * method = Method.GET,
 * url = "https://example.com/api",
 * headers = mapOf("Accept" to "application/json"),
 * body = null
 * ),
 * "merino"
 * )
 * ```
 * @param {Request} request
 * @param {string} channel
 * @returns {Promise<Response>}}
 */
export async function sendOhttpRequest(
    request, 
    channel) {
   
FfiConverterTypeRequest.checkType(request);
FfiConverterString.checkType(channel);
const result = await UniFFIScaffolding.callAsync(
    232, // uniffi_viaduct_fn_func_send_ohttp_request
    FfiConverterTypeRequest.lower(request),
    FfiConverterString.lower(channel),
)
return handleRustResult(
    result,
    FfiConverterTypeResponse.lift.bind(FfiConverterTypeResponse),
    FfiConverterTypeViaductError.lift.bind(FfiConverterTypeViaductError),
)
}

/**
 * Set the global default user-agent
 * 
 * This is what's used when no user-agent is set in the `ClientSettings` and no `user-agent`
 * header is set in the Request.
 * @param {string} userAgent
 */
export function setGlobalDefaultUserAgent(
    userAgent) {
   
FfiConverterString.checkType(userAgent);
const result = UniFFIScaffolding.callSync(
    233, // uniffi_viaduct_fn_func_set_global_default_user_agent
    FfiConverterString.lower(userAgent),
)
return handleRustResult(
    result,
    (result) => undefined,
    null,
)
}






// Export the FFIConverter object to make external types work.
export class FfiConverterOptionalString extends FfiConverterArrayBuffer {
    static checkType(value) {
        if (value !== undefined && value !== null) {
            FfiConverterString.checkType(value)
        }
    }

    static read(dataStream) {
        const code = dataStream.readUint8(0);
        switch (code) {
            case 0:
                return null
            case 1:
                return FfiConverterString.read(dataStream)
            default:
                throw new UniFFIError(`Unexpected code: ${code}`);
        }
    }

    static write(dataStream, value) {
        if (value === null || value === undefined) {
            dataStream.writeUint8(0);
            return;
        }
        dataStream.writeUint8(1);
        FfiConverterString.write(dataStream, value)
    }

    static computeSize(value) {
        if (value === null || value === undefined) {
            return 1;
        }
        return 1 + FfiConverterString.computeSize(value)
    }
}
/**
 * ClientSettings
 */
export class ClientSettings {
    constructor(
        {
            timeout= 60000, 
            redirectLimit= 10, 
            ohttpChannel, 
            userAgent= null
        } = {
            timeout: undefined, 
            redirectLimit: undefined, 
            ohttpChannel: undefined, 
            userAgent: undefined
        }
    ) {
        try {
            FfiConverterUInt32.checkType(timeout)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("timeout");
            }
            throw e;
        }
        try {
            FfiConverterUInt32.checkType(redirectLimit)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("redirectLimit");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(ohttpChannel)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("ohttpChannel");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(userAgent)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("userAgent");
            }
            throw e;
        }
        /**
         * Timeout for the entire request in ms (0 indicates no timeout).
         * @type {number}
         */
        this.timeout = timeout;
        /**
         * Maximum amount of redirects to follow (0 means redirects are not allowed)
         * @type {number}
         */
        this.redirectLimit = redirectLimit;
        /**
         * OHTTP channel to use for all requests (if any)
         * @type {?string}
         */
        this.ohttpChannel = ohttpChannel;
        /**
         * Client default user-agent.
         * 
         * This overrides the global default user-agent and is used when no `User-agent` header is set
         * directly in the Request.
         * @type {?string}
         */
        this.userAgent = userAgent;
    }

    equals(other) {
        return (
            this.timeout == other.timeout
            && this.redirectLimit == other.redirectLimit
            && this.ohttpChannel == other.ohttpChannel
            && this.userAgent == other.userAgent
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeClientSettings extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new ClientSettings({
            timeout: FfiConverterUInt32.read(dataStream),
            redirectLimit: FfiConverterUInt32.read(dataStream),
            ohttpChannel: FfiConverterOptionalString.read(dataStream),
            userAgent: FfiConverterOptionalString.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterUInt32.write(dataStream, value.timeout);
        FfiConverterUInt32.write(dataStream, value.redirectLimit);
        FfiConverterOptionalString.write(dataStream, value.ohttpChannel);
        FfiConverterOptionalString.write(dataStream, value.userAgent);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterUInt32.computeSize(value.timeout);
        totalSize += FfiConverterUInt32.computeSize(value.redirectLimit);
        totalSize += FfiConverterOptionalString.computeSize(value.ohttpChannel);
        totalSize += FfiConverterOptionalString.computeSize(value.userAgent);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof ClientSettings)) {
            throw new UniFFITypeError(`Expected 'ClientSettings', found '${typeof value}'`);
        }
        try {
            FfiConverterUInt32.checkType(value.timeout);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".timeout");
            }
            throw e;
        }
        try {
            FfiConverterUInt32.checkType(value.redirectLimit);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".redirectLimit");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.ohttpChannel);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".ohttpChannel");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.userAgent);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".userAgent");
            }
            throw e;
        }
    }
}
/**
 * Configuration for an OHTTP channel
 */
export class OhttpConfig {
    constructor(
        {
            relayUrl, 
            gatewayHost
        } = {
            relayUrl: undefined, 
            gatewayHost: undefined
        }
    ) {
        try {
            FfiConverterString.checkType(relayUrl)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("relayUrl");
            }
            throw e;
        }
        try {
            FfiConverterString.checkType(gatewayHost)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("gatewayHost");
            }
            throw e;
        }
        /**
         * The relay URL that will proxy requests
         * @type {string}
         */
        this.relayUrl = relayUrl;
        /**
         * The gateway host that provides encryption keys and decrypts requests
         * @type {string}
         */
        this.gatewayHost = gatewayHost;
    }

    equals(other) {
        return (
            this.relayUrl == other.relayUrl
            && this.gatewayHost == other.gatewayHost
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeOhttpConfig extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new OhttpConfig({
            relayUrl: FfiConverterString.read(dataStream),
            gatewayHost: FfiConverterString.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterString.write(dataStream, value.relayUrl);
        FfiConverterString.write(dataStream, value.gatewayHost);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterString.computeSize(value.relayUrl);
        totalSize += FfiConverterString.computeSize(value.gatewayHost);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof OhttpConfig)) {
            throw new UniFFITypeError(`Expected 'OhttpConfig', found '${typeof value}'`);
        }
        try {
            FfiConverterString.checkType(value.relayUrl);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".relayUrl");
            }
            throw e;
        }
        try {
            FfiConverterString.checkType(value.gatewayHost);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".gatewayHost");
            }
            throw e;
        }
    }
}

/**
 * HTTP Methods.
 * 
 * The supported methods are the limited to what's supported by android-components.
 */
export const Method = Object.freeze({
    /**
     * GET
     */
    GET: 0,
    /**
     * HEAD
     */
    HEAD: 1,
    /**
     * POST
     */
    POST: 2,
    /**
     * PUT
     */
    PUT: 3,
    /**
     * DELETE
     */
    DELETE: 4,
    /**
     * CONNECT
     */
    CONNECT: 5,
    /**
     * OPTIONS
     */
    OPTIONS: 6,
    /**
     * TRACE
     */
    TRACE: 7,
    /**
     * PATCH
     */
    PATCH: 8,
});

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeMethod extends FfiConverterArrayBuffer {
    static #validValues = Object.values(Method)

    static read(dataStream) {
        // Use sequential indices (1-based) for the wire format to match the Rust scaffolding
        switch (dataStream.readInt32()) {
            case 1:
                return Method.GET
            case 2:
                return Method.HEAD
            case 3:
                return Method.POST
            case 4:
                return Method.PUT
            case 5:
                return Method.DELETE
            case 6:
                return Method.CONNECT
            case 7:
                return Method.OPTIONS
            case 8:
                return Method.TRACE
            case 9:
                return Method.PATCH
            default:
                throw new UniFFITypeError("Unknown Method variant");
        }
    }

    static write(dataStream, value) {
        // Use sequential indices (1-based) for the wire format to match the Rust scaffolding
        if (value === Method.GET) {
            dataStream.writeInt32(1);
            return;
        }
        if (value === Method.HEAD) {
            dataStream.writeInt32(2);
            return;
        }
        if (value === Method.POST) {
            dataStream.writeInt32(3);
            return;
        }
        if (value === Method.PUT) {
            dataStream.writeInt32(4);
            return;
        }
        if (value === Method.DELETE) {
            dataStream.writeInt32(5);
            return;
        }
        if (value === Method.CONNECT) {
            dataStream.writeInt32(6);
            return;
        }
        if (value === Method.OPTIONS) {
            dataStream.writeInt32(7);
            return;
        }
        if (value === Method.TRACE) {
            dataStream.writeInt32(8);
            return;
        }
        if (value === Method.PATCH) {
            dataStream.writeInt32(9);
            return;
        }
        throw new UniFFITypeError("Unknown Method variant");
    }

    static computeSize(value) {
        return 4;
    }

    static checkType(value) {
      // Check that the value is a valid enum variant
      if (!this.#validValues.includes(value)) {
          throw new UniFFITypeError(`${value} is not a valid value for Method`);
      }
    }
}
export class FfiConverterTypeViaductUrl extends FfiConverter {
    static lift(value) {
        return FfiConverterString.lift(value);
    }

    static lower(value) {
        return FfiConverterString.lower(value);
    }

    static write(dataStream, value) {
        FfiConverterString.write(dataStream, value);
    }

    static read(dataStream) {
        const builtinVal = FfiConverterString.read(dataStream);
        return builtinVal;
    }

    static computeSize(value) {
        return FfiConverterString.computeSize(value);
    }

    static checkType(value) {
        if (value === null || value === undefined) {
            throw new TypeError("value is null or undefined");
        }
    }
}
// Export the FFIConverter object to make external types work.
export class FfiConverterMapStringString extends FfiConverterArrayBuffer {
    static read(dataStream) {
        const len = dataStream.readInt32();
        const map = new Map();
        for (let i = 0; i < len; i++) {
            const key = FfiConverterString.read(dataStream);
            const value = FfiConverterString.read(dataStream);
            map.set(key, value);
        }

        return map;
    }

     static write(dataStream, map) {
        dataStream.writeInt32(map.size);
        for (const [key, value] of map) {
            FfiConverterString.write(dataStream, key);
            FfiConverterString.write(dataStream, value);
        }
    }

    static computeSize(map) {
        // The size of the length
        let size = 4;
        for (const [key, value] of map) {
            size += FfiConverterString.computeSize(key);
            size += FfiConverterString.computeSize(value);
        }
        return size;
    }

    static checkType(map) {
        for (const [key, value] of map) {
            try {
                FfiConverterString.checkType(key);
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("(key)");
                }
                throw e;
            }

            try {
                FfiConverterString.checkType(value);
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart(`[${key}]`);
                }
                throw e;
            }
        }
    }
}
export class FfiConverterTypeHeaders extends FfiConverter {
    static lift(value) {
        return FfiConverterMapStringString.lift(value);
    }

    static lower(value) {
        return FfiConverterMapStringString.lower(value);
    }

    static write(dataStream, value) {
        FfiConverterMapStringString.write(dataStream, value);
    }

    static read(dataStream) {
        const builtinVal = FfiConverterMapStringString.read(dataStream);
        return builtinVal;
    }

    static computeSize(value) {
        return FfiConverterMapStringString.computeSize(value);
    }

    static checkType(value) {
        if (value === null || value === undefined) {
            throw new TypeError("value is null or undefined");
        }
    }
}


// Export the FFIConverter object to make external types work.
export class FfiConverterOptionalBytes extends FfiConverterArrayBuffer {
    static checkType(value) {
        if (value !== undefined && value !== null) {
            FfiConverterBytes.checkType(value)
        }
    }

    static read(dataStream) {
        const code = dataStream.readUint8(0);
        switch (code) {
            case 0:
                return null
            case 1:
                return FfiConverterBytes.read(dataStream)
            default:
                throw new UniFFIError(`Unexpected code: ${code}`);
        }
    }

    static write(dataStream, value) {
        if (value === null || value === undefined) {
            dataStream.writeUint8(0);
            return;
        }
        dataStream.writeUint8(1);
        FfiConverterBytes.write(dataStream, value)
    }

    static computeSize(value) {
        if (value === null || value === undefined) {
            return 1;
        }
        return 1 + FfiConverterBytes.computeSize(value)
    }
}
/**
 * Request
 */
export class Request {
    constructor(
        {
            method, 
            url, 
            headers, 
            body
        } = {
            method: undefined, 
            url: undefined, 
            headers: undefined, 
            body: undefined
        }
    ) {
        try {
            FfiConverterTypeMethod.checkType(method)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("method");
            }
            throw e;
        }
        try {
            FfiConverterTypeViaductUrl.checkType(url)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("url");
            }
            throw e;
        }
        try {
            FfiConverterTypeHeaders.checkType(headers)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("headers");
            }
            throw e;
        }
        try {
            FfiConverterOptionalBytes.checkType(body)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("body");
            }
            throw e;
        }
        /**
         * @type {Method[keyof Method]}
         */
        this.method = method;
        /**
         * @type {ViaductUrl}
         */
        this.url = url;
        /**
         * @type {Headers}
         */
        this.headers = headers;
        /**
         * @type {?string}
         */
        this.body = body;
    }

    equals(other) {
        return (
            this.method == other.method
            && this.url == other.url
            && this.headers == other.headers
            && this.body == other.body
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRequest extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new Request({
            method: FfiConverterTypeMethod.read(dataStream),
            url: FfiConverterTypeViaductUrl.read(dataStream),
            headers: FfiConverterTypeHeaders.read(dataStream),
            body: FfiConverterOptionalBytes.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterTypeMethod.write(dataStream, value.method);
        FfiConverterTypeViaductUrl.write(dataStream, value.url);
        FfiConverterTypeHeaders.write(dataStream, value.headers);
        FfiConverterOptionalBytes.write(dataStream, value.body);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterTypeMethod.computeSize(value.method);
        totalSize += FfiConverterTypeViaductUrl.computeSize(value.url);
        totalSize += FfiConverterTypeHeaders.computeSize(value.headers);
        totalSize += FfiConverterOptionalBytes.computeSize(value.body);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof Request)) {
            throw new UniFFITypeError(`Expected 'Request', found '${typeof value}'`);
        }
        try {
            FfiConverterTypeMethod.checkType(value.method);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".method");
            }
            throw e;
        }
        try {
            FfiConverterTypeViaductUrl.checkType(value.url);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".url");
            }
            throw e;
        }
        try {
            FfiConverterTypeHeaders.checkType(value.headers);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".headers");
            }
            throw e;
        }
        try {
            FfiConverterOptionalBytes.checkType(value.body);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".body");
            }
            throw e;
        }
    }
}


/**
 * A response from the server.
 */
export class Response {
    constructor(
        {
            requestMethod, 
            url, 
            status, 
            headers, 
            body
        } = {
            requestMethod: undefined, 
            url: undefined, 
            status: undefined, 
            headers: undefined, 
            body: undefined
        }
    ) {
        try {
            FfiConverterTypeMethod.checkType(requestMethod)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("requestMethod");
            }
            throw e;
        }
        try {
            FfiConverterTypeViaductUrl.checkType(url)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("url");
            }
            throw e;
        }
        try {
            FfiConverterUInt16.checkType(status)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("status");
            }
            throw e;
        }
        try {
            FfiConverterTypeHeaders.checkType(headers)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("headers");
            }
            throw e;
        }
        try {
            FfiConverterBytes.checkType(body)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("body");
            }
            throw e;
        }
        /**
         * The method used to request this response.
         * @type {Method[keyof Method]}
         */
        this.requestMethod = requestMethod;
        /**
         * The URL of this response.
         * @type {ViaductUrl}
         */
        this.url = url;
        /**
         * The HTTP Status code of this response.
         * @type {number}
         */
        this.status = status;
        /**
         * The headers returned with this response.
         * @type {Headers}
         */
        this.headers = headers;
        /**
         * The body of the response.
         * @type {string}
         */
        this.body = body;
    }

    equals(other) {
        return (
            this.requestMethod == other.requestMethod
            && this.url == other.url
            && this.status == other.status
            && this.headers == other.headers
            && this.body == other.body
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeResponse extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new Response({
            requestMethod: FfiConverterTypeMethod.read(dataStream),
            url: FfiConverterTypeViaductUrl.read(dataStream),
            status: FfiConverterUInt16.read(dataStream),
            headers: FfiConverterTypeHeaders.read(dataStream),
            body: FfiConverterBytes.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterTypeMethod.write(dataStream, value.requestMethod);
        FfiConverterTypeViaductUrl.write(dataStream, value.url);
        FfiConverterUInt16.write(dataStream, value.status);
        FfiConverterTypeHeaders.write(dataStream, value.headers);
        FfiConverterBytes.write(dataStream, value.body);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterTypeMethod.computeSize(value.requestMethod);
        totalSize += FfiConverterTypeViaductUrl.computeSize(value.url);
        totalSize += FfiConverterUInt16.computeSize(value.status);
        totalSize += FfiConverterTypeHeaders.computeSize(value.headers);
        totalSize += FfiConverterBytes.computeSize(value.body);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof Response)) {
            throw new UniFFITypeError(`Expected 'Response', found '${typeof value}'`);
        }
        try {
            FfiConverterTypeMethod.checkType(value.requestMethod);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".requestMethod");
            }
            throw e;
        }
        try {
            FfiConverterTypeViaductUrl.checkType(value.url);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".url");
            }
            throw e;
        }
        try {
            FfiConverterUInt16.checkType(value.status);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".status");
            }
            throw e;
        }
        try {
            FfiConverterTypeHeaders.checkType(value.headers);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".headers");
            }
            throw e;
        }
        try {
            FfiConverterBytes.checkType(value.body);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".body");
            }
            throw e;
        }
    }
}

/**
 * ViaductError
 */
export class ViaductError extends Error {}


/**
 * RequestHeaderError
 */
export class RequestHeaderError extends ViaductError {

    constructor(
        v0,
        ...params
    ) {
        const message = `v0: ${ v0 }`;
        super(message, ...params);
        this.v0 = v0;
    }
    toString() {
        return `RequestHeaderError: ${super.toString()}`
    }
}

/**
 * BackendError
 */
export class BackendError extends ViaductError {

    constructor(
        v0,
        ...params
    ) {
        const message = `v0: ${ v0 }`;
        super(message, ...params);
        this.v0 = v0;
    }
    toString() {
        return `BackendError: ${super.toString()}`
    }
}

/**
 * NetworkError
 */
export class NetworkError extends ViaductError {

    constructor(
        v0,
        ...params
    ) {
        const message = `v0: ${ v0 }`;
        super(message, ...params);
        this.v0 = v0;
    }
    toString() {
        return `NetworkError: ${super.toString()}`
    }
}

/**
 * BackendAlreadyInitialized
 */
export class BackendAlreadyInitialized extends ViaductError {

    constructor(
        ...params
    ) {
        super(...params);
    }
    toString() {
        return `BackendAlreadyInitialized: ${super.toString()}`
    }
}

/**
 * BackendNotInitialized
 */
export class BackendNotInitialized extends ViaductError {

    constructor(
        ...params
    ) {
        super(...params);
    }
    toString() {
        return `BackendNotInitialized: ${super.toString()}`
    }
}

/**
 * SetBackendError
 */
export class SetBackendError extends ViaductError {

    constructor(
        ...params
    ) {
        super(...params);
    }
    toString() {
        return `SetBackendError: ${super.toString()}`
    }
}

/**
 * Note: we return this if the server returns a bad URL with
 * its response. This *probably* should never happen, but who knows.
 */
export class UrlError extends ViaductError {

    constructor(
        v0,
        ...params
    ) {
        const message = `v0: ${ v0 }`;
        super(message, ...params);
        this.v0 = v0;
    }
    toString() {
        return `UrlError: ${super.toString()}`
    }
}

/**
 * NonTlsUrl
 */
export class NonTlsUrl extends ViaductError {

    constructor(
        ...params
    ) {
        super(...params);
    }
    toString() {
        return `NonTlsUrl: ${super.toString()}`
    }
}

/**
 * OhttpChannelNotConfigured
 */
export class OhttpChannelNotConfigured extends ViaductError {

    constructor(
        v0,
        ...params
    ) {
        const message = `v0: ${ v0 }`;
        super(message, ...params);
        this.v0 = v0;
    }
    toString() {
        return `OhttpChannelNotConfigured: ${super.toString()}`
    }
}

/**
 * OhttpConfigFetchFailed
 */
export class OhttpConfigFetchFailed extends ViaductError {

    constructor(
        v0,
        ...params
    ) {
        const message = `v0: ${ v0 }`;
        super(message, ...params);
        this.v0 = v0;
    }
    toString() {
        return `OhttpConfigFetchFailed: ${super.toString()}`
    }
}

/**
 * OhttpRequestError
 */
export class OhttpRequestError extends ViaductError {

    constructor(
        v0,
        ...params
    ) {
        const message = `v0: ${ v0 }`;
        super(message, ...params);
        this.v0 = v0;
    }
    toString() {
        return `OhttpRequestError: ${super.toString()}`
    }
}

/**
 * OhttpResponseError
 */
export class OhttpResponseError extends ViaductError {

    constructor(
        v0,
        ...params
    ) {
        const message = `v0: ${ v0 }`;
        super(message, ...params);
        this.v0 = v0;
    }
    toString() {
        return `OhttpResponseError: ${super.toString()}`
    }
}

/**
 * OhttpNotSupported
 */
export class OhttpNotSupported extends ViaductError {

    constructor(
        ...params
    ) {
        super(...params);
    }
    toString() {
        return `OhttpNotSupported: ${super.toString()}`
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeViaductError extends FfiConverterArrayBuffer {
    static read(dataStream) {
        switch (dataStream.readInt32()) {
            case 1:
                return new RequestHeaderError(
                    FfiConverterString.read(dataStream)
                    );
            case 2:
                return new BackendError(
                    FfiConverterString.read(dataStream)
                    );
            case 3:
                return new NetworkError(
                    FfiConverterString.read(dataStream)
                    );
            case 4:
                return new BackendAlreadyInitialized(
                    );
            case 5:
                return new BackendNotInitialized(
                    );
            case 6:
                return new SetBackendError(
                    );
            case 7:
                return new UrlError(
                    FfiConverterString.read(dataStream)
                    );
            case 8:
                return new NonTlsUrl(
                    );
            case 9:
                return new OhttpChannelNotConfigured(
                    FfiConverterString.read(dataStream)
                    );
            case 10:
                return new OhttpConfigFetchFailed(
                    FfiConverterString.read(dataStream)
                    );
            case 11:
                return new OhttpRequestError(
                    FfiConverterString.read(dataStream)
                    );
            case 12:
                return new OhttpResponseError(
                    FfiConverterString.read(dataStream)
                    );
            case 13:
                return new OhttpNotSupported(
                    );
            default:
                throw new UniFFITypeError("Unknown ViaductError variant");
        }
    }
    static computeSize(value) {
        // Size of the Int indicating the variant
        let totalSize = 4;
        if (value instanceof RequestHeaderError) {
            totalSize += FfiConverterString.computeSize(value.v0);
            return totalSize;
        }
        if (value instanceof BackendError) {
            totalSize += FfiConverterString.computeSize(value.v0);
            return totalSize;
        }
        if (value instanceof NetworkError) {
            totalSize += FfiConverterString.computeSize(value.v0);
            return totalSize;
        }
        if (value instanceof BackendAlreadyInitialized) {
            return totalSize;
        }
        if (value instanceof BackendNotInitialized) {
            return totalSize;
        }
        if (value instanceof SetBackendError) {
            return totalSize;
        }
        if (value instanceof UrlError) {
            totalSize += FfiConverterString.computeSize(value.v0);
            return totalSize;
        }
        if (value instanceof NonTlsUrl) {
            return totalSize;
        }
        if (value instanceof OhttpChannelNotConfigured) {
            totalSize += FfiConverterString.computeSize(value.v0);
            return totalSize;
        }
        if (value instanceof OhttpConfigFetchFailed) {
            totalSize += FfiConverterString.computeSize(value.v0);
            return totalSize;
        }
        if (value instanceof OhttpRequestError) {
            totalSize += FfiConverterString.computeSize(value.v0);
            return totalSize;
        }
        if (value instanceof OhttpResponseError) {
            totalSize += FfiConverterString.computeSize(value.v0);
            return totalSize;
        }
        if (value instanceof OhttpNotSupported) {
            return totalSize;
        }
        throw new UniFFITypeError("Unknown ViaductError variant");
    }
    static write(dataStream, value) {
        if (value instanceof RequestHeaderError) {
            dataStream.writeInt32(1);
            FfiConverterString.write(dataStream, value.v0);
            return;
        }
        if (value instanceof BackendError) {
            dataStream.writeInt32(2);
            FfiConverterString.write(dataStream, value.v0);
            return;
        }
        if (value instanceof NetworkError) {
            dataStream.writeInt32(3);
            FfiConverterString.write(dataStream, value.v0);
            return;
        }
        if (value instanceof BackendAlreadyInitialized) {
            dataStream.writeInt32(4);
            return;
        }
        if (value instanceof BackendNotInitialized) {
            dataStream.writeInt32(5);
            return;
        }
        if (value instanceof SetBackendError) {
            dataStream.writeInt32(6);
            return;
        }
        if (value instanceof UrlError) {
            dataStream.writeInt32(7);
            FfiConverterString.write(dataStream, value.v0);
            return;
        }
        if (value instanceof NonTlsUrl) {
            dataStream.writeInt32(8);
            return;
        }
        if (value instanceof OhttpChannelNotConfigured) {
            dataStream.writeInt32(9);
            FfiConverterString.write(dataStream, value.v0);
            return;
        }
        if (value instanceof OhttpConfigFetchFailed) {
            dataStream.writeInt32(10);
            FfiConverterString.write(dataStream, value.v0);
            return;
        }
        if (value instanceof OhttpRequestError) {
            dataStream.writeInt32(11);
            FfiConverterString.write(dataStream, value.v0);
            return;
        }
        if (value instanceof OhttpResponseError) {
            dataStream.writeInt32(12);
            FfiConverterString.write(dataStream, value.v0);
            return;
        }
        if (value instanceof OhttpNotSupported) {
            dataStream.writeInt32(13);
            return;
        }
        throw new UniFFITypeError("Unknown ViaductError variant");
    }

    static errorClass = ViaductError;
}

/**
 * Backend
 */
export class Backend {
    /**
     * sendRequest
     * @param {Request} request
     * @param {ClientSettings} settings
     * @returns {Promise<Response>}}
     */
    async sendRequest(
        request, 
        settings) {
      throw Error("sendRequest not implemented");
    }

}

/**
 * Backend
 */
export class BackendImpl extends Backend {
    // Use `init` to instantiate this class.
    // DO NOT USE THIS CONSTRUCTOR DIRECTLY
    constructor(opts) {
        super();
        if (!Object.prototype.hasOwnProperty.call(opts, constructUniffiObject)) {
            throw new UniFFIError("Attempting to construct an int using the JavaScript constructor directly" +
            "Please use a UDL defined constructor, or the init function for the primary constructor")
        }
        if (!(opts[constructUniffiObject] instanceof UniFFIPointer)) {
            throw new UniFFIError("Attempting to create a UniFFI object with a pointer that is not an instance of UniFFIPointer")
        }
        this[uniffiObjectPtr] = opts[constructUniffiObject];
    }

    /**
     * sendRequest
     * @param {Request} request
     * @param {ClientSettings} settings
     * @returns {Promise<Response>}}
     */
    async sendRequest(
        request, 
        settings) {
       
        FfiConverterTypeRequest.checkType(request);
        FfiConverterTypeClientSettings.checkType(settings);
        const result = await UniFFIScaffolding.callAsync(
            234, // uniffi_viaduct_fn_method_backend_send_request
            FfiConverterTypeBackend.lowerReceiver(this),
            FfiConverterTypeRequest.lower(request),
            FfiConverterTypeClientSettings.lower(settings),
        )
        return handleRustResult(
            result,
            FfiConverterTypeResponse.lift.bind(FfiConverterTypeResponse),
            FfiConverterTypeViaductError.lift.bind(FfiConverterTypeViaductError),
        )
    }

}

// FfiConverter for a trait interface.  This is a hybrid of the FFIConverter regular interfaces and
// for callback interfaces.
//
// Export the FFIConverter object to make external types work.
export class FfiConverterTypeBackend extends FfiConverter {
    static lift(handle) {
        if (handle instanceof UniFFIPointer) {
          // Rust handle.  Construct an object from it
          const opts = {};
          opts[constructUniffiObject] = handle;
          return new BackendImpl(opts);
        } else {
          // JS handle.  Get the JS object from the callback handler
          return uniffiCallbackHandlerViaductBackend.takeCallbackObj(handle)
        }
    }

    static lower(value) {
        const ptr = value[uniffiObjectPtr];
        if (ptr instanceof UniFFIPointer) {
          // Rust-implemented interface, return the ptr.  The C++ code will clone it.
          return ptr;
        } else {
          // JS-implemented interface, store the object in the handle map and return the handle
          if (!(value instanceof Backend)) {
              throw new UniFFITypeError("expected 'Backend' subclass");
          }
          return uniffiCallbackHandlerViaductBackend.storeCallbackObj(value)
        }
    }

    // lowerReceiver is used when calling methods on an interface we got from Rust, 
    // it treats value like a regular interface.
    static lowerReceiver(value) {
        const ptr = value[uniffiObjectPtr];
        if (!(ptr instanceof UniFFIPointer)) {
            throw new UniFFITypeError("Object is not a 'BackendImpl' instance");
        }
        return ptr;
    }

    static read(dataStream) {
        return this.lift(dataStream.readHandleOrPointer(26))
    }

    static write(dataStream, value) {
        if (value[uniffiObjectPtr] instanceof UniFFIPointer) {
          // Rust-implemented interface, return the ptr.
          dataStream.writePointer(26, this.lower(value));
        } else {
          dataStream.writeInt64(this.lower(value))
        }
    }

    static computeSize(value) {
        return 8;
    }
}

const uniffiCallbackHandlerViaductBackend = new UniFFICallbackHandler(
    "Backend",
    8,
    [
        new UniFFICallbackMethodHandler(
            "sendRequest",
            [
                FfiConverterTypeRequest,
                FfiConverterTypeClientSettings,
            ],
            FfiConverterTypeResponse.lower.bind(FfiConverterTypeResponse),
            (e) => {
              if (e instanceof ViaductError) {
                return FfiConverterTypeViaductError.lower(e);
              }
              throw e;
            }
        ),
    ]
);

// Allow the shutdown-related functionality to be tested in the unit tests
UnitTestObjs.uniffiCallbackHandlerViaductBackend = uniffiCallbackHandlerViaductBackend;
// Export the FFIConverter object to make external types work.
export class FfiConverterSequenceString extends FfiConverterArrayBuffer {
    static read(dataStream) {
        const len = dataStream.readInt32();
        const arr = [];
        for (let i = 0; i < len; i++) {
            arr.push(FfiConverterString.read(dataStream));
        }
        return arr;
    }

    static write(dataStream, value) {
        dataStream.writeInt32(value.length);
        value.forEach((innerValue) => {
            FfiConverterString.write(dataStream, innerValue);
        })
    }

    static computeSize(value) {
        // The size of the length
        let size = 4;
        for (const innerValue of value) {
            size += FfiConverterString.computeSize(innerValue);
        }
        return size;
    }

    static checkType(value) {
        if (!Array.isArray(value)) {
            throw new UniFFITypeError(`${value} is not an array`);
        }
        value.forEach((innerValue, idx) => {
            try {
                FfiConverterString.checkType(innerValue);
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart(`[${idx}]`);
                }
                throw e;
            }
        })
    }
}


