 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,
};





/**
 * Attachment metadata that can be optionally attached to a [Record]. The [location] should
 * included in calls to [Client::get_attachment].
 */
export class Attachment {
    constructor(
        {
            filename, 
            mimetype, 
            location, 
            hash, 
            size
        } = {
            filename: undefined, 
            mimetype: undefined, 
            location: undefined, 
            hash: undefined, 
            size: undefined
        }
    ) {
        try {
            FfiConverterString.checkType(filename)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("filename");
            }
            throw e;
        }
        try {
            FfiConverterString.checkType(mimetype)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("mimetype");
            }
            throw e;
        }
        try {
            FfiConverterString.checkType(location)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("location");
            }
            throw e;
        }
        try {
            FfiConverterString.checkType(hash)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("hash");
            }
            throw e;
        }
        try {
            FfiConverterUInt64.checkType(size)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("size");
            }
            throw e;
        }
        /**
         * filename
         */
        this.filename = filename;
        /**
         * mimetype
         */
        this.mimetype = mimetype;
        /**
         * location
         */
        this.location = location;
        /**
         * hash
         */
        this.hash = hash;
        /**
         * size
         */
        this.size = size;
    }

    equals(other) {
        return (
            this.filename == other.filename
            && this.mimetype == other.mimetype
            && this.location == other.location
            && this.hash == other.hash
            && this.size == other.size
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeAttachment extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new Attachment({
            filename: FfiConverterString.read(dataStream),
            mimetype: FfiConverterString.read(dataStream),
            location: FfiConverterString.read(dataStream),
            hash: FfiConverterString.read(dataStream),
            size: FfiConverterUInt64.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterString.write(dataStream, value.filename);
        FfiConverterString.write(dataStream, value.mimetype);
        FfiConverterString.write(dataStream, value.location);
        FfiConverterString.write(dataStream, value.hash);
        FfiConverterUInt64.write(dataStream, value.size);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterString.computeSize(value.filename);
        totalSize += FfiConverterString.computeSize(value.mimetype);
        totalSize += FfiConverterString.computeSize(value.location);
        totalSize += FfiConverterString.computeSize(value.hash);
        totalSize += FfiConverterUInt64.computeSize(value.size);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof Attachment)) {
            throw new UniFFITypeError(`Expected 'Attachment', found '${typeof value}'`);
        }
        try {
            FfiConverterString.checkType(value.filename);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".filename");
            }
            throw e;
        }
        try {
            FfiConverterString.checkType(value.mimetype);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".mimetype");
            }
            throw e;
        }
        try {
            FfiConverterString.checkType(value.location);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".location");
            }
            throw e;
        }
        try {
            FfiConverterString.checkType(value.hash);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".hash");
            }
            throw e;
        }
        try {
            FfiConverterUInt64.checkType(value.size);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".size");
            }
            throw e;
        }
    }
}
// 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)
    }
}

/**
 * The Remote Settings server that the client should use.
 */
export class RemoteSettingsServer {}
/**
 * Prod
 */
RemoteSettingsServer.Prod = class extends RemoteSettingsServer{
   constructor() {
            super();
    }
}
/**
 * Stage
 */
RemoteSettingsServer.Stage = class extends RemoteSettingsServer{
   constructor() {
            super();
    }
}
/**
 * Dev
 */
RemoteSettingsServer.Dev = class extends RemoteSettingsServer{
   constructor() {
            super();
    }
}
/**
 * Custom
 */
RemoteSettingsServer.Custom = class extends RemoteSettingsServer{
   constructor({url = undefined } = {}) {
                super();
            try {
                FfiConverterString.checkType(url);
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart("url");
                }
                throw e;
            }
            this.url = url;
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettingsServer extends FfiConverterArrayBuffer {
    static read(dataStream) {
        // Use sequential indices (1-based) for the wire format to match the Rust scaffolding
        switch (dataStream.readInt32()) {
            case 1:
                return new RemoteSettingsServer.Prod();
            case 2:
                return new RemoteSettingsServer.Stage();
            case 3:
                return new RemoteSettingsServer.Dev();
            case 4:
                return new RemoteSettingsServer.Custom({
                    url: FfiConverterString.read(dataStream)
                });
            default:
                throw new UniFFITypeError("Unknown RemoteSettingsServer variant");
        }
    }

    static write(dataStream, value) {
        // Use sequential indices (1-based) for the wire format to match the Rust scaffolding
        if (value instanceof RemoteSettingsServer.Prod) {
            dataStream.writeInt32(1);
            return;
        }
        if (value instanceof RemoteSettingsServer.Stage) {
            dataStream.writeInt32(2);
            return;
        }
        if (value instanceof RemoteSettingsServer.Dev) {
            dataStream.writeInt32(3);
            return;
        }
        if (value instanceof RemoteSettingsServer.Custom) {
            dataStream.writeInt32(4);
            FfiConverterString.write(dataStream, value.url);
            return;
        }
        throw new UniFFITypeError("Unknown RemoteSettingsServer variant");
    }

    static computeSize(value) {
        // Size of the Int indicating the variant
        let totalSize = 4;
        if (value instanceof RemoteSettingsServer.Prod) {
            return totalSize;
        }
        if (value instanceof RemoteSettingsServer.Stage) {
            return totalSize;
        }
        if (value instanceof RemoteSettingsServer.Dev) {
            return totalSize;
        }
        if (value instanceof RemoteSettingsServer.Custom) {
            totalSize += FfiConverterString.computeSize(value.url);
            return totalSize;
        }
        throw new UniFFITypeError("Unknown RemoteSettingsServer variant");
    }

    static checkType(value) {
      if (!(value instanceof RemoteSettingsServer)) {
        throw new UniFFITypeError(`${value} is not a subclass instance of RemoteSettingsServer`);
      }
    }
}
// Export the FFIConverter object to make external types work.
export class FfiConverterOptionalTypeRemoteSettingsServer extends FfiConverterArrayBuffer {
    static checkType(value) {
        if (value !== undefined && value !== null) {
            FfiConverterTypeRemoteSettingsServer.checkType(value)
        }
    }

    static read(dataStream) {
        const code = dataStream.readUint8(0);
        switch (code) {
            case 0:
                return null
            case 1:
                return FfiConverterTypeRemoteSettingsServer.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);
        FfiConverterTypeRemoteSettingsServer.write(dataStream, value)
    }

    static computeSize(value) {
        if (value === null || value === undefined) {
            return 1;
        }
        return 1 + FfiConverterTypeRemoteSettingsServer.computeSize(value)
    }
}
/**
 * Custom configuration for the client.
 * Currently includes the following:
 * - `server`: The Remote Settings server to use. If not specified, defaults to the production server (`RemoteSettingsServer::Prod`).
 * - `server_url`: An optional custom Remote Settings server URL. Deprecated; please use `server` instead.
 * - `bucket_name`: The optional name of the bucket containing the collection on the server. If not specified, the standard bucket will be used.
 * - `collection_name`: The name of the collection for the settings server.
 */
export class RemoteSettingsConfig {
    constructor(
        {
            collectionName, 
            bucketName= null, 
            serverUrl= null, 
            server= null
        } = {
            collectionName: undefined, 
            bucketName: undefined, 
            serverUrl: undefined, 
            server: undefined
        }
    ) {
        try {
            FfiConverterString.checkType(collectionName)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("collectionName");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(bucketName)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("bucketName");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(serverUrl)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("serverUrl");
            }
            throw e;
        }
        try {
            FfiConverterOptionalTypeRemoteSettingsServer.checkType(server)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("server");
            }
            throw e;
        }
        /**
         * collectionName
         */
        this.collectionName = collectionName;
        /**
         * bucketName
         */
        this.bucketName = bucketName;
        /**
         * serverUrl
         */
        this.serverUrl = serverUrl;
        /**
         * server
         */
        this.server = server;
    }

    equals(other) {
        return (
            this.collectionName == other.collectionName
            && this.bucketName == other.bucketName
            && this.serverUrl == other.serverUrl
            && this.server == other.server
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettingsConfig extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new RemoteSettingsConfig({
            collectionName: FfiConverterString.read(dataStream),
            bucketName: FfiConverterOptionalString.read(dataStream),
            serverUrl: FfiConverterOptionalString.read(dataStream),
            server: FfiConverterOptionalTypeRemoteSettingsServer.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterString.write(dataStream, value.collectionName);
        FfiConverterOptionalString.write(dataStream, value.bucketName);
        FfiConverterOptionalString.write(dataStream, value.serverUrl);
        FfiConverterOptionalTypeRemoteSettingsServer.write(dataStream, value.server);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterString.computeSize(value.collectionName);
        totalSize += FfiConverterOptionalString.computeSize(value.bucketName);
        totalSize += FfiConverterOptionalString.computeSize(value.serverUrl);
        totalSize += FfiConverterOptionalTypeRemoteSettingsServer.computeSize(value.server);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof RemoteSettingsConfig)) {
            throw new UniFFITypeError(`Expected 'RemoteSettingsConfig', found '${typeof value}'`);
        }
        try {
            FfiConverterString.checkType(value.collectionName);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".collectionName");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.bucketName);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".bucketName");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.serverUrl);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".serverUrl");
            }
            throw e;
        }
        try {
            FfiConverterOptionalTypeRemoteSettingsServer.checkType(value.server);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".server");
            }
            throw e;
        }
    }
}
// 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 the FFIConverter object to make external types work.
export class FfiConverterOptionalMapStringString extends FfiConverterArrayBuffer {
    static checkType(value) {
        if (value !== undefined && value !== null) {
            FfiConverterMapStringString.checkType(value)
        }
    }

    static read(dataStream) {
        const code = dataStream.readUint8(0);
        switch (code) {
            case 0:
                return null
            case 1:
                return FfiConverterMapStringString.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);
        FfiConverterMapStringString.write(dataStream, value)
    }

    static computeSize(value) {
        if (value === null || value === undefined) {
            return 1;
        }
        return 1 + FfiConverterMapStringString.computeSize(value)
    }
}
/**
 * Remote settings context object
 * 
 * This is used to filter the records returned. We always fetch all `records` from the
 * remote-settings storage. Some records could have a `filter_expression`.  If this is passed in
 * and the record has a `filter_expression`, then only returns where the expression is true will
 * be returned.
 * 
 * See https://remote-settings.readthedocs.io/en/latest/target-filters.html for details.
 */
export class RemoteSettingsContext {
    constructor(
        {
            channel= null, 
            appVersion= null, 
            appId= null, 
            locale= null, 
            os= null, 
            osVersion= null, 
            formFactor= null, 
            country= null, 
            customTargettingAttributes= null
        } = {
            channel: undefined, 
            appVersion: undefined, 
            appId: undefined, 
            locale: undefined, 
            os: undefined, 
            osVersion: undefined, 
            formFactor: undefined, 
            country: undefined, 
            customTargettingAttributes: undefined
        }
    ) {
        try {
            FfiConverterOptionalString.checkType(channel)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("channel");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(appVersion)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("appVersion");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(appId)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("appId");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(locale)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("locale");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(os)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("os");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(osVersion)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("osVersion");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(formFactor)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("formFactor");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(country)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("country");
            }
            throw e;
        }
        try {
            FfiConverterOptionalMapStringString.checkType(customTargettingAttributes)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("customTargettingAttributes");
            }
            throw e;
        }
        /**
         * The delivery channel of the application (e.g "nightly")
         */
        this.channel = channel;
        /**
         * User visible version string (e.g. "1.0.3")
         */
        this.appVersion = appVersion;
        /**
         * String containing the XUL application app_id
         */
        this.appId = appId;
        /**
         * The locale of the application during initialization (e.g. "es-ES")
         */
        this.locale = locale;
        /**
         * The name of the operating system (e.g. "Android", "iOS", "Darwin", "WINNT")
         */
        this.os = os;
        /**
         * The user-visible version of the operating system (e.g. "1.2.3")
         */
        this.osVersion = osVersion;
        /**
         * Form-factor of the device ("phone", "tablet", or "desktop")
         */
        this.formFactor = formFactor;
        /**
         * Country of the user.
         * 
         * This is usually populated in one of two ways:
         * - The second component of the locale
         * - By using a geolocation service, which determines country via the user's IP.
         * Firefox apps usually have a module that integrates with these services,
         * for example `Region` on Desktop and `RegionMiddleware` on Android.
         */
        this.country = country;
        /**
         * Extra attributes to add to the env for JEXL filtering.
         * 
         * Use this for prototyping / testing new features.  In the long-term, new fields should be
         * added to the official list and supported by both the Rust and Gecko clients.
         */
        this.customTargettingAttributes = customTargettingAttributes;
    }

    equals(other) {
        return (
            this.channel == other.channel
            && this.appVersion == other.appVersion
            && this.appId == other.appId
            && this.locale == other.locale
            && this.os == other.os
            && this.osVersion == other.osVersion
            && this.formFactor == other.formFactor
            && this.country == other.country
            && this.customTargettingAttributes == other.customTargettingAttributes
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettingsContext extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new RemoteSettingsContext({
            channel: FfiConverterOptionalString.read(dataStream),
            appVersion: FfiConverterOptionalString.read(dataStream),
            appId: FfiConverterOptionalString.read(dataStream),
            locale: FfiConverterOptionalString.read(dataStream),
            os: FfiConverterOptionalString.read(dataStream),
            osVersion: FfiConverterOptionalString.read(dataStream),
            formFactor: FfiConverterOptionalString.read(dataStream),
            country: FfiConverterOptionalString.read(dataStream),
            customTargettingAttributes: FfiConverterOptionalMapStringString.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterOptionalString.write(dataStream, value.channel);
        FfiConverterOptionalString.write(dataStream, value.appVersion);
        FfiConverterOptionalString.write(dataStream, value.appId);
        FfiConverterOptionalString.write(dataStream, value.locale);
        FfiConverterOptionalString.write(dataStream, value.os);
        FfiConverterOptionalString.write(dataStream, value.osVersion);
        FfiConverterOptionalString.write(dataStream, value.formFactor);
        FfiConverterOptionalString.write(dataStream, value.country);
        FfiConverterOptionalMapStringString.write(dataStream, value.customTargettingAttributes);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterOptionalString.computeSize(value.channel);
        totalSize += FfiConverterOptionalString.computeSize(value.appVersion);
        totalSize += FfiConverterOptionalString.computeSize(value.appId);
        totalSize += FfiConverterOptionalString.computeSize(value.locale);
        totalSize += FfiConverterOptionalString.computeSize(value.os);
        totalSize += FfiConverterOptionalString.computeSize(value.osVersion);
        totalSize += FfiConverterOptionalString.computeSize(value.formFactor);
        totalSize += FfiConverterOptionalString.computeSize(value.country);
        totalSize += FfiConverterOptionalMapStringString.computeSize(value.customTargettingAttributes);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof RemoteSettingsContext)) {
            throw new UniFFITypeError(`Expected 'RemoteSettingsContext', found '${typeof value}'`);
        }
        try {
            FfiConverterOptionalString.checkType(value.channel);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".channel");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.appVersion);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".appVersion");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.appId);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".appId");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.locale);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".locale");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.os);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".os");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.osVersion);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".osVersion");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.formFactor);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".formFactor");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.country);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".country");
            }
            throw e;
        }
        try {
            FfiConverterOptionalMapStringString.checkType(value.customTargettingAttributes);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".customTargettingAttributes");
            }
            throw e;
        }
    }
}
// Export the FFIConverter object to make external types work.
export class FfiConverterOptionalTypeRemoteSettingsContext extends FfiConverterArrayBuffer {
    static checkType(value) {
        if (value !== undefined && value !== null) {
            FfiConverterTypeRemoteSettingsContext.checkType(value)
        }
    }

    static read(dataStream) {
        const code = dataStream.readUint8(0);
        switch (code) {
            case 0:
                return null
            case 1:
                return FfiConverterTypeRemoteSettingsContext.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);
        FfiConverterTypeRemoteSettingsContext.write(dataStream, value)
    }

    static computeSize(value) {
        if (value === null || value === undefined) {
            return 1;
        }
        return 1 + FfiConverterTypeRemoteSettingsContext.computeSize(value)
    }
}
/**
 * Remote settings configuration
 * 
 * This is the version used in the new API, hence the `2` at the end.  The plan is to move
 * consumers to the new API, remove the RemoteSettingsConfig struct, then remove the `2` from this
 * name.
 */
export class RemoteSettingsConfig2 {
    constructor(
        {
            server= null, 
            bucketName= null, 
            appContext= null
        } = {
            server: undefined, 
            bucketName: undefined, 
            appContext: undefined
        }
    ) {
        try {
            FfiConverterOptionalTypeRemoteSettingsServer.checkType(server)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("server");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(bucketName)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("bucketName");
            }
            throw e;
        }
        try {
            FfiConverterOptionalTypeRemoteSettingsContext.checkType(appContext)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("appContext");
            }
            throw e;
        }
        /**
         * The Remote Settings server to use. Defaults to [RemoteSettingsServer::Prod],
         */
        this.server = server;
        /**
         * Bucket name to use, defaults to "main".  Use "main-preview" for a preview bucket
         */
        this.bucketName = bucketName;
        /**
         * App context to use for JEXL filtering (when the `jexl` feature is present).
         */
        this.appContext = appContext;
    }

    equals(other) {
        return (
            this.server == other.server
            && this.bucketName == other.bucketName
            && this.appContext == other.appContext
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettingsConfig2 extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new RemoteSettingsConfig2({
            server: FfiConverterOptionalTypeRemoteSettingsServer.read(dataStream),
            bucketName: FfiConverterOptionalString.read(dataStream),
            appContext: FfiConverterOptionalTypeRemoteSettingsContext.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterOptionalTypeRemoteSettingsServer.write(dataStream, value.server);
        FfiConverterOptionalString.write(dataStream, value.bucketName);
        FfiConverterOptionalTypeRemoteSettingsContext.write(dataStream, value.appContext);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterOptionalTypeRemoteSettingsServer.computeSize(value.server);
        totalSize += FfiConverterOptionalString.computeSize(value.bucketName);
        totalSize += FfiConverterOptionalTypeRemoteSettingsContext.computeSize(value.appContext);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof RemoteSettingsConfig2)) {
            throw new UniFFITypeError(`Expected 'RemoteSettingsConfig2', found '${typeof value}'`);
        }
        try {
            FfiConverterOptionalTypeRemoteSettingsServer.checkType(value.server);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".server");
            }
            throw e;
        }
        try {
            FfiConverterOptionalString.checkType(value.bucketName);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".bucketName");
            }
            throw e;
        }
        try {
            FfiConverterOptionalTypeRemoteSettingsContext.checkType(value.appContext);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".appContext");
            }
            throw e;
        }
    }
}


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

    static read(dataStream) {
        const code = dataStream.readUint8(0);
        switch (code) {
            case 0:
                return null
            case 1:
                return FfiConverterTypeAttachment.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);
        FfiConverterTypeAttachment.write(dataStream, value)
    }

    static computeSize(value) {
        if (value === null || value === undefined) {
            return 1;
        }
        return 1 + FfiConverterTypeAttachment.computeSize(value)
    }
}
export class FfiConverterTypeRsJsonObject 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");
        }
    }
}
/**
 * A parsed Remote Settings record. Records can contain arbitrary fields, so clients
 * are required to further extract expected values from the [fields] member.
 */
export class RemoteSettingsRecord {
    constructor(
        {
            id, 
            lastModified, 
            deleted, 
            attachment, 
            fields
        } = {
            id: undefined, 
            lastModified: undefined, 
            deleted: undefined, 
            attachment: undefined, 
            fields: undefined
        }
    ) {
        try {
            FfiConverterString.checkType(id)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("id");
            }
            throw e;
        }
        try {
            FfiConverterUInt64.checkType(lastModified)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("lastModified");
            }
            throw e;
        }
        try {
            FfiConverterBoolean.checkType(deleted)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("deleted");
            }
            throw e;
        }
        try {
            FfiConverterOptionalTypeAttachment.checkType(attachment)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("attachment");
            }
            throw e;
        }
        try {
            FfiConverterTypeRsJsonObject.checkType(fields)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("fields");
            }
            throw e;
        }
        /**
         * id
         */
        this.id = id;
        /**
         * lastModified
         */
        this.lastModified = lastModified;
        /**
         * Tombstone flag (see https://remote-settings.readthedocs.io/en/latest/client-specifications.html#local-state)
         */
        this.deleted = deleted;
        /**
         * attachment
         */
        this.attachment = attachment;
        /**
         * fields
         */
        this.fields = fields;
    }

    equals(other) {
        return (
            this.id == other.id
            && this.lastModified == other.lastModified
            && this.deleted == other.deleted
            && this.attachment == other.attachment
            && this.fields == other.fields
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettingsRecord extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new RemoteSettingsRecord({
            id: FfiConverterString.read(dataStream),
            lastModified: FfiConverterUInt64.read(dataStream),
            deleted: FfiConverterBoolean.read(dataStream),
            attachment: FfiConverterOptionalTypeAttachment.read(dataStream),
            fields: FfiConverterTypeRsJsonObject.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterString.write(dataStream, value.id);
        FfiConverterUInt64.write(dataStream, value.lastModified);
        FfiConverterBoolean.write(dataStream, value.deleted);
        FfiConverterOptionalTypeAttachment.write(dataStream, value.attachment);
        FfiConverterTypeRsJsonObject.write(dataStream, value.fields);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterString.computeSize(value.id);
        totalSize += FfiConverterUInt64.computeSize(value.lastModified);
        totalSize += FfiConverterBoolean.computeSize(value.deleted);
        totalSize += FfiConverterOptionalTypeAttachment.computeSize(value.attachment);
        totalSize += FfiConverterTypeRsJsonObject.computeSize(value.fields);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof RemoteSettingsRecord)) {
            throw new UniFFITypeError(`Expected 'RemoteSettingsRecord', found '${typeof value}'`);
        }
        try {
            FfiConverterString.checkType(value.id);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".id");
            }
            throw e;
        }
        try {
            FfiConverterUInt64.checkType(value.lastModified);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".lastModified");
            }
            throw e;
        }
        try {
            FfiConverterBoolean.checkType(value.deleted);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".deleted");
            }
            throw e;
        }
        try {
            FfiConverterOptionalTypeAttachment.checkType(value.attachment);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".attachment");
            }
            throw e;
        }
        try {
            FfiConverterTypeRsJsonObject.checkType(value.fields);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".fields");
            }
            throw e;
        }
    }
}
// Export the FFIConverter object to make external types work.
export class FfiConverterSequenceTypeRemoteSettingsRecord extends FfiConverterArrayBuffer {
    static read(dataStream) {
        const len = dataStream.readInt32();
        const arr = [];
        for (let i = 0; i < len; i++) {
            arr.push(FfiConverterTypeRemoteSettingsRecord.read(dataStream));
        }
        return arr;
    }

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

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

    static checkType(value) {
        if (!Array.isArray(value)) {
            throw new UniFFITypeError(`${value} is not an array`);
        }
        value.forEach((innerValue, idx) => {
            try {
                FfiConverterTypeRemoteSettingsRecord.checkType(innerValue);
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart(`[${idx}]`);
                }
                throw e;
            }
        })
    }
}
/**
 * Data structure representing the top-level response from the Remote Settings.
 * [last_modified] will be extracted from the etag header of the response.
 */
export class RemoteSettingsResponse {
    constructor(
        {
            records, 
            lastModified
        } = {
            records: undefined, 
            lastModified: undefined
        }
    ) {
        try {
            FfiConverterSequenceTypeRemoteSettingsRecord.checkType(records)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("records");
            }
            throw e;
        }
        try {
            FfiConverterUInt64.checkType(lastModified)
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart("lastModified");
            }
            throw e;
        }
        /**
         * records
         */
        this.records = records;
        /**
         * lastModified
         */
        this.lastModified = lastModified;
    }

    equals(other) {
        return (
            this.records == other.records
            && this.lastModified == other.lastModified
        )
    }
}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettingsResponse extends FfiConverterArrayBuffer {
    static read(dataStream) {
        return new RemoteSettingsResponse({
            records: FfiConverterSequenceTypeRemoteSettingsRecord.read(dataStream),
            lastModified: FfiConverterUInt64.read(dataStream),
        });
    }
    static write(dataStream, value) {
        FfiConverterSequenceTypeRemoteSettingsRecord.write(dataStream, value.records);
        FfiConverterUInt64.write(dataStream, value.lastModified);
    }

    static computeSize(value) {
        let totalSize = 0;
        totalSize += FfiConverterSequenceTypeRemoteSettingsRecord.computeSize(value.records);
        totalSize += FfiConverterUInt64.computeSize(value.lastModified);
        return totalSize
    }

    static checkType(value) {
        super.checkType(value);
        if (!(value instanceof RemoteSettingsResponse)) {
            throw new UniFFITypeError(`Expected 'RemoteSettingsResponse', found '${typeof value}'`);
        }
        try {
            FfiConverterSequenceTypeRemoteSettingsRecord.checkType(value.records);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".records");
            }
            throw e;
        }
        try {
            FfiConverterUInt64.checkType(value.lastModified);
        } catch (e) {
            if (e instanceof UniFFITypeError) {
                e.addItemDescriptionPart(".lastModified");
            }
            throw e;
        }
    }
}

/**
 * Public error class, this is what we return to consumers
 */
export class RemoteSettingsError extends Error {}


/**
 * Network error while making a remote settings request
 */
export class Network extends RemoteSettingsError {

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

/**
 * The server has asked the client to backoff.
 */
export class Backoff extends RemoteSettingsError {

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

/**
 * Other
 */
export class Other extends RemoteSettingsError {

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

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettingsError extends FfiConverterArrayBuffer {
    static read(dataStream) {
        switch (dataStream.readInt32()) {
            case 1:
                return new Network(
                    FfiConverterString.read(dataStream)
                    );
            case 2:
                return new Backoff(
                    FfiConverterUInt64.read(dataStream)
                    );
            case 3:
                return new Other(
                    FfiConverterString.read(dataStream)
                    );
            default:
                throw new UniFFITypeError("Unknown RemoteSettingsError variant");
        }
    }
    static computeSize(value) {
        // Size of the Int indicating the variant
        let totalSize = 4;
        if (value instanceof Network) {
            totalSize += FfiConverterString.computeSize(value.reason);
            return totalSize;
        }
        if (value instanceof Backoff) {
            totalSize += FfiConverterUInt64.computeSize(value.seconds);
            return totalSize;
        }
        if (value instanceof Other) {
            totalSize += FfiConverterString.computeSize(value.reason);
            return totalSize;
        }
        throw new UniFFITypeError("Unknown RemoteSettingsError variant");
    }
    static write(dataStream, value) {
        if (value instanceof Network) {
            dataStream.writeInt32(1);
            FfiConverterString.write(dataStream, value.reason);
            return;
        }
        if (value instanceof Backoff) {
            dataStream.writeInt32(2);
            FfiConverterUInt64.write(dataStream, value.seconds);
            return;
        }
        if (value instanceof Other) {
            dataStream.writeInt32(3);
            FfiConverterString.write(dataStream, value.reason);
            return;
        }
        throw new UniFFITypeError("Unknown RemoteSettingsError variant");
    }

    static errorClass = RemoteSettingsError;
}

/**
 * RemoteSettingsInterface
 */
export class RemoteSettingsInterface {
    /**
     * Download an attachment with the provided id to the provided path.
     * @param {string} attachmentId
     * @param {string} path
     */
    async downloadAttachmentToPath(
        attachmentId, 
        path) {
      throw Error("downloadAttachmentToPath not implemented");
    }
    /**
     * Fetch all records for the configuration this client was initialized with.
     * @returns {Promise<RemoteSettingsResponse>}}
     */
    async getRecords() {
      throw Error("getRecords not implemented");
    }
    /**
     * Fetch all records added to the server since the provided timestamp,
     * using the configuration this client was initialized with.
     * @param {number} timestamp
     * @returns {Promise<RemoteSettingsResponse>}}
     */
    async getRecordsSince(
        timestamp) {
      throw Error("getRecordsSince not implemented");
    }

}

/**
 * RemoteSettings
 */
export class RemoteSettings extends RemoteSettingsInterface {
    // 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];
    }
    /**
     * Construct a new Remote Settings client with the given configuration.
     * @param {RemoteSettingsConfig} remoteSettingsConfig
     * @returns {RemoteSettings}
     */
    static init(
        remoteSettingsConfig) {
       
        FfiConverterTypeRemoteSettingsConfig.checkType(remoteSettingsConfig);
        const result = UniFFIScaffolding.callSync(
            63, // uniffi_remote_settings_fn_constructor_remotesettings_new
            FfiConverterTypeRemoteSettingsConfig.lower(remoteSettingsConfig),
        )
        return handleRustResult(
            result,
            FfiConverterTypeRemoteSettings.lift.bind(FfiConverterTypeRemoteSettings),
            FfiConverterTypeRemoteSettingsError.lift.bind(FfiConverterTypeRemoteSettingsError),
        )
    }

    /**
     * Download an attachment with the provided id to the provided path.
     * @param {string} attachmentId
     * @param {string} path
     */
    async downloadAttachmentToPath(
        attachmentId, 
        path) {
       
        FfiConverterString.checkType(attachmentId);
        FfiConverterString.checkType(path);
        const result = await UniFFIScaffolding.callAsyncWrapper(
            64, // uniffi_remote_settings_fn_method_remotesettings_download_attachment_to_path
            FfiConverterTypeRemoteSettings.lowerReceiver(this),
            FfiConverterString.lower(attachmentId),
            FfiConverterString.lower(path),
        )
        return handleRustResult(
            result,
            (result) => undefined,
            FfiConverterTypeRemoteSettingsError.lift.bind(FfiConverterTypeRemoteSettingsError),
        )
    }

    /**
     * Fetch all records for the configuration this client was initialized with.
     * @returns {Promise<RemoteSettingsResponse>}}
     */
    async getRecords() {
       
        const result = await UniFFIScaffolding.callAsyncWrapper(
            65, // uniffi_remote_settings_fn_method_remotesettings_get_records
            FfiConverterTypeRemoteSettings.lowerReceiver(this),
        )
        return handleRustResult(
            result,
            FfiConverterTypeRemoteSettingsResponse.lift.bind(FfiConverterTypeRemoteSettingsResponse),
            FfiConverterTypeRemoteSettingsError.lift.bind(FfiConverterTypeRemoteSettingsError),
        )
    }

    /**
     * Fetch all records added to the server since the provided timestamp,
     * using the configuration this client was initialized with.
     * @param {number} timestamp
     * @returns {Promise<RemoteSettingsResponse>}}
     */
    async getRecordsSince(
        timestamp) {
       
        FfiConverterUInt64.checkType(timestamp);
        const result = await UniFFIScaffolding.callAsyncWrapper(
            66, // uniffi_remote_settings_fn_method_remotesettings_get_records_since
            FfiConverterTypeRemoteSettings.lowerReceiver(this),
            FfiConverterUInt64.lower(timestamp),
        )
        return handleRustResult(
            result,
            FfiConverterTypeRemoteSettingsResponse.lift.bind(FfiConverterTypeRemoteSettingsResponse),
            FfiConverterTypeRemoteSettingsError.lift.bind(FfiConverterTypeRemoteSettingsError),
        )
    }

}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettings extends FfiConverter {
    static lift(value) {
        const opts = {};
        opts[constructUniffiObject] = value;
        return new RemoteSettings(opts);
    }

    static lower(value) {
        const ptr = value[uniffiObjectPtr];
        if (!(ptr instanceof UniFFIPointer)) {
            throw new UniFFITypeError("Object is not a 'RemoteSettings' instance");
        }
        return ptr;
    }

    static lowerReceiver(value) {
        // This works exactly the same as lower for non-trait interfaces
        return this.lower(value);
    }

    static read(dataStream) {
        return this.lift(dataStream.readPointer(11));
    }

    static write(dataStream, value) {
        dataStream.writePointer(11, this.lower(value));
    }

    static computeSize(value) {
        return 8;
    }
}



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

    static read(dataStream) {
        const code = dataStream.readUint8(0);
        switch (code) {
            case 0:
                return null
            case 1:
                return FfiConverterSequenceTypeRemoteSettingsRecord.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);
        FfiConverterSequenceTypeRemoteSettingsRecord.write(dataStream, value)
    }

    static computeSize(value) {
        if (value === null || value === undefined) {
            return 1;
        }
        return 1 + FfiConverterSequenceTypeRemoteSettingsRecord.computeSize(value)
    }
}
// Export the FFIConverter object to make external types work.
export class FfiConverterMapStringTypeRemoteSettingsRecord 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 = FfiConverterTypeRemoteSettingsRecord.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);
            FfiConverterTypeRemoteSettingsRecord.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 += FfiConverterTypeRemoteSettingsRecord.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 {
                FfiConverterTypeRemoteSettingsRecord.checkType(value);
            } catch (e) {
                if (e instanceof UniFFITypeError) {
                    e.addItemDescriptionPart(`[${key}]`);
                }
                throw e;
            }
        }
    }
}
// Export the FFIConverter object to make external types work.
export class FfiConverterOptionalMapStringTypeRemoteSettingsRecord extends FfiConverterArrayBuffer {
    static checkType(value) {
        if (value !== undefined && value !== null) {
            FfiConverterMapStringTypeRemoteSettingsRecord.checkType(value)
        }
    }

    static read(dataStream) {
        const code = dataStream.readUint8(0);
        switch (code) {
            case 0:
                return null
            case 1:
                return FfiConverterMapStringTypeRemoteSettingsRecord.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);
        FfiConverterMapStringTypeRemoteSettingsRecord.write(dataStream, value)
    }

    static computeSize(value) {
        if (value === null || value === undefined) {
            return 1;
        }
        return 1 + FfiConverterMapStringTypeRemoteSettingsRecord.computeSize(value)
    }
}

/**
 * Client for a single Remote Settings collection
 * 
 * Use [RemoteSettingsService::make_client] to create these.
 */
export class RemoteSettingsClientInterface {
    /**
     * Collection this client is for
     * @returns {Promise<string>}}
     */
    async collectionName() {
      throw Error("collectionName not implemented");
    }
    /**
     * Get attachment data for a remote settings record
     * 
     * Attachments are large binary blobs used for data that doesn't fit in a normal record.  They
     * are handled differently than other record data:
     * 
     * - Attachments are not downloaded in [RemoteSettingsService::sync]
     * - This method will make network requests if the attachment is not cached
     * - This method will throw if there is a network or other error when fetching the
     * attachment data.
     * @param {RemoteSettingsRecord} record
     * @returns {Promise<string>}}
     */
    async getAttachment(
        record) {
      throw Error("getAttachment not implemented");
    }
    /**
     * Get the current set of records.
     * 
     * This method normally fetches records from the last sync.  This means that it returns fast
     * and does not make any network requests.
     * 
     * If records have not yet been synced it will return None.  Use `sync_if_empty = true` to
     * change this behavior and perform a network request in this case.  That this is probably a
     * bad idea if you want to fetch the setting in application startup or when building the UI.
     * 
     * None will also be returned on disk IO errors or other unexpected errors.  The reason for
     * this is that there is not much an application can do in this situation other than fall back
     * to the same default handling as if records have not been synced.
     * 
     * Application-services schedules regular dumps of the server data for specific collections.
     * For these collections, `get_records` will never return None.  If you would like to add your
     * collection to this list, please reach out to the DISCO team.
     * @param {boolean} syncIfEmpty
     * @returns {Promise<?Array.<RemoteSettingsRecord>>}}
     */
    async getRecords(
        syncIfEmpty = false) {
      throw Error("getRecords not implemented");
    }
    /**
     * Get the current set of records as a map of record_id -> record.
     * 
     * See [Self::get_records] for an explanation of when this makes network requests, error
     * handling, and how the `sync_if_empty` param works.
     * @param {boolean} syncIfEmpty
     * @returns {Promise<?object>}}
     */
    async getRecordsMap(
        syncIfEmpty = false) {
      throw Error("getRecordsMap not implemented");
    }
    /**
     * Shutdown the client, releasing the SQLite connection used to cache records.
     */
    async shutdown() {
      throw Error("shutdown not implemented");
    }
    /**
     * sync
     */
    async sync() {
      throw Error("sync not implemented");
    }

}

/**
 * Client for a single Remote Settings collection
 * 
 * Use [RemoteSettingsService::make_client] to create these.
 */
export class RemoteSettingsClient extends RemoteSettingsClientInterface {
    // 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];
    }

    /**
     * Collection this client is for
     * @returns {Promise<string>}}
     */
    async collectionName() {
       
        const result = await UniFFIScaffolding.callAsyncWrapper(
            67, // uniffi_remote_settings_fn_method_remotesettingsclient_collection_name
            FfiConverterTypeRemoteSettingsClient.lowerReceiver(this),
        )
        return handleRustResult(
            result,
            FfiConverterString.lift.bind(FfiConverterString),
            null,
        )
    }

    /**
     * Get attachment data for a remote settings record
     * 
     * Attachments are large binary blobs used for data that doesn't fit in a normal record.  They
     * are handled differently than other record data:
     * 
     * - Attachments are not downloaded in [RemoteSettingsService::sync]
     * - This method will make network requests if the attachment is not cached
     * - This method will throw if there is a network or other error when fetching the
     * attachment data.
     * @param {RemoteSettingsRecord} record
     * @returns {Promise<string>}}
     */
    async getAttachment(
        record) {
       
        FfiConverterTypeRemoteSettingsRecord.checkType(record);
        const result = await UniFFIScaffolding.callAsyncWrapper(
            68, // uniffi_remote_settings_fn_method_remotesettingsclient_get_attachment
            FfiConverterTypeRemoteSettingsClient.lowerReceiver(this),
            FfiConverterTypeRemoteSettingsRecord.lower(record),
        )
        return handleRustResult(
            result,
            FfiConverterBytes.lift.bind(FfiConverterBytes),
            FfiConverterTypeRemoteSettingsError.lift.bind(FfiConverterTypeRemoteSettingsError),
        )
    }

    /**
     * Get the current set of records.
     * 
     * This method normally fetches records from the last sync.  This means that it returns fast
     * and does not make any network requests.
     * 
     * If records have not yet been synced it will return None.  Use `sync_if_empty = true` to
     * change this behavior and perform a network request in this case.  That this is probably a
     * bad idea if you want to fetch the setting in application startup or when building the UI.
     * 
     * None will also be returned on disk IO errors or other unexpected errors.  The reason for
     * this is that there is not much an application can do in this situation other than fall back
     * to the same default handling as if records have not been synced.
     * 
     * Application-services schedules regular dumps of the server data for specific collections.
     * For these collections, `get_records` will never return None.  If you would like to add your
     * collection to this list, please reach out to the DISCO team.
     * @param {boolean} syncIfEmpty
     * @returns {Promise<?Array.<RemoteSettingsRecord>>}}
     */
    async getRecords(
        syncIfEmpty = false) {
       
        FfiConverterBoolean.checkType(syncIfEmpty);
        const result = await UniFFIScaffolding.callAsyncWrapper(
            69, // uniffi_remote_settings_fn_method_remotesettingsclient_get_records
            FfiConverterTypeRemoteSettingsClient.lowerReceiver(this),
            FfiConverterBoolean.lower(syncIfEmpty),
        )
        return handleRustResult(
            result,
            FfiConverterOptionalSequenceTypeRemoteSettingsRecord.lift.bind(FfiConverterOptionalSequenceTypeRemoteSettingsRecord),
            null,
        )
    }

    /**
     * Get the current set of records as a map of record_id -> record.
     * 
     * See [Self::get_records] for an explanation of when this makes network requests, error
     * handling, and how the `sync_if_empty` param works.
     * @param {boolean} syncIfEmpty
     * @returns {Promise<?object>}}
     */
    async getRecordsMap(
        syncIfEmpty = false) {
       
        FfiConverterBoolean.checkType(syncIfEmpty);
        const result = await UniFFIScaffolding.callAsyncWrapper(
            70, // uniffi_remote_settings_fn_method_remotesettingsclient_get_records_map
            FfiConverterTypeRemoteSettingsClient.lowerReceiver(this),
            FfiConverterBoolean.lower(syncIfEmpty),
        )
        return handleRustResult(
            result,
            FfiConverterOptionalMapStringTypeRemoteSettingsRecord.lift.bind(FfiConverterOptionalMapStringTypeRemoteSettingsRecord),
            null,
        )
    }

    /**
     * Shutdown the client, releasing the SQLite connection used to cache records.
     */
    async shutdown() {
       
        const result = await UniFFIScaffolding.callAsyncWrapper(
            71, // uniffi_remote_settings_fn_method_remotesettingsclient_shutdown
            FfiConverterTypeRemoteSettingsClient.lowerReceiver(this),
        )
        return handleRustResult(
            result,
            (result) => undefined,
            null,
        )
    }

    /**
     * sync
     */
    async sync() {
       
        const result = await UniFFIScaffolding.callAsyncWrapper(
            72, // uniffi_remote_settings_fn_method_remotesettingsclient_sync
            FfiConverterTypeRemoteSettingsClient.lowerReceiver(this),
        )
        return handleRustResult(
            result,
            (result) => undefined,
            FfiConverterTypeRemoteSettingsError.lift.bind(FfiConverterTypeRemoteSettingsError),
        )
    }

}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettingsClient extends FfiConverter {
    static lift(value) {
        const opts = {};
        opts[constructUniffiObject] = value;
        return new RemoteSettingsClient(opts);
    }

    static lower(value) {
        const ptr = value[uniffiObjectPtr];
        if (!(ptr instanceof UniFFIPointer)) {
            throw new UniFFITypeError("Object is not a 'RemoteSettingsClient' instance");
        }
        return ptr;
    }

    static lowerReceiver(value) {
        // This works exactly the same as lower for non-trait interfaces
        return this.lower(value);
    }

    static read(dataStream) {
        return this.lift(dataStream.readPointer(12));
    }

    static write(dataStream, value) {
        dataStream.writePointer(12, this.lower(value));
    }

    static computeSize(value) {
        return 8;
    }
}

// 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;
            }
        })
    }
}

/**
 * Application-level Remote Settings manager.
 * 
 * This handles application-level operations, like syncing all the collections, and acts as a
 * factory for creating clients.
 */
export class RemoteSettingsServiceInterface {
    /**
     * clientUrl
     * @returns {string}
     */
    clientUrl() {
      throw Error("clientUrl not implemented");
    }
    /**
     * Create a new Remote Settings client
     * 
     * This method performs no IO or network requests and is safe to run in a main thread that can't be blocked.
     * @param {string} collectionName
     * @returns {Promise<RemoteSettingsClient>}}
     */
    async makeClient(
        collectionName) {
      throw Error("makeClient not implemented");
    }
    /**
     * Sync collections for all active clients
     * @returns {Promise<Array.<string>>}}
     */
    async sync() {
      throw Error("sync not implemented");
    }
    /**
     * Update the remote settings config
     * 
     * This will cause all current and future clients to use new config and will delete any stored
     * records causing the clients to return new results from the new config.
     * 
     * Only intended for QA/debugging.  Swapping the remote settings server in the middle of
     * execution can cause weird effects.
     * @param {RemoteSettingsConfig2} config
     */
    async updateConfig(
        config) {
      throw Error("updateConfig not implemented");
    }

}

/**
 * Application-level Remote Settings manager.
 * 
 * This handles application-level operations, like syncing all the collections, and acts as a
 * factory for creating clients.
 */
export class RemoteSettingsService extends RemoteSettingsServiceInterface {
    // 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];
    }
    /**
     * Construct a [RemoteSettingsService]
     * 
     * This is typically done early in the application-startup process.
     * 
     * This method performs no IO or network requests and is safe to run in a main thread that
     * can't be blocked.
     * 
     * `storage_dir` is a directory to store SQLite files in -- one per collection. If the
     * directory does not exist, it will be created when the storage is first used. Only the
     * directory and the SQLite files will be created, any parent directories must already exist.
     * @param {string} storageDir
     * @param {RemoteSettingsConfig2} config
     * @returns {RemoteSettingsService}
     */
    static init(
        storageDir, 
        config) {
       
        FfiConverterString.checkType(storageDir);
        FfiConverterTypeRemoteSettingsConfig2.checkType(config);
        const result = UniFFIScaffolding.callSync(
            73, // uniffi_remote_settings_fn_constructor_remotesettingsservice_new
            FfiConverterString.lower(storageDir),
            FfiConverterTypeRemoteSettingsConfig2.lower(config),
        )
        return handleRustResult(
            result,
            FfiConverterTypeRemoteSettingsService.lift.bind(FfiConverterTypeRemoteSettingsService),
            null,
        )
    }

    /**
     * clientUrl
     * @returns {string}
     */
    clientUrl() {
       
        const result = UniFFIScaffolding.callSync(
            74, // uniffi_remote_settings_fn_method_remotesettingsservice_client_url
            FfiConverterTypeRemoteSettingsService.lowerReceiver(this),
        )
        return handleRustResult(
            result,
            FfiConverterString.lift.bind(FfiConverterString),
            null,
        )
    }

    /**
     * Create a new Remote Settings client
     * 
     * This method performs no IO or network requests and is safe to run in a main thread that can't be blocked.
     * @param {string} collectionName
     * @returns {Promise<RemoteSettingsClient>}}
     */
    async makeClient(
        collectionName) {
       
        FfiConverterString.checkType(collectionName);
        const result = await UniFFIScaffolding.callAsyncWrapper(
            75, // uniffi_remote_settings_fn_method_remotesettingsservice_make_client
            FfiConverterTypeRemoteSettingsService.lowerReceiver(this),
            FfiConverterString.lower(collectionName),
        )
        return handleRustResult(
            result,
            FfiConverterTypeRemoteSettingsClient.lift.bind(FfiConverterTypeRemoteSettingsClient),
            null,
        )
    }

    /**
     * Sync collections for all active clients
     * @returns {Promise<Array.<string>>}}
     */
    async sync() {
       
        const result = await UniFFIScaffolding.callAsyncWrapper(
            76, // uniffi_remote_settings_fn_method_remotesettingsservice_sync
            FfiConverterTypeRemoteSettingsService.lowerReceiver(this),
        )
        return handleRustResult(
            result,
            FfiConverterSequenceString.lift.bind(FfiConverterSequenceString),
            FfiConverterTypeRemoteSettingsError.lift.bind(FfiConverterTypeRemoteSettingsError),
        )
    }

    /**
     * Update the remote settings config
     * 
     * This will cause all current and future clients to use new config and will delete any stored
     * records causing the clients to return new results from the new config.
     * 
     * Only intended for QA/debugging.  Swapping the remote settings server in the middle of
     * execution can cause weird effects.
     * @param {RemoteSettingsConfig2} config
     */
    async updateConfig(
        config) {
       
        FfiConverterTypeRemoteSettingsConfig2.checkType(config);
        const result = await UniFFIScaffolding.callAsyncWrapper(
            77, // uniffi_remote_settings_fn_method_remotesettingsservice_update_config
            FfiConverterTypeRemoteSettingsService.lowerReceiver(this),
            FfiConverterTypeRemoteSettingsConfig2.lower(config),
        )
        return handleRustResult(
            result,
            (result) => undefined,
            FfiConverterTypeRemoteSettingsError.lift.bind(FfiConverterTypeRemoteSettingsError),
        )
    }

}

// Export the FFIConverter object to make external types work.
export class FfiConverterTypeRemoteSettingsService extends FfiConverter {
    static lift(value) {
        const opts = {};
        opts[constructUniffiObject] = value;
        return new RemoteSettingsService(opts);
    }

    static lower(value) {
        const ptr = value[uniffiObjectPtr];
        if (!(ptr instanceof UniFFIPointer)) {
            throw new UniFFITypeError("Object is not a 'RemoteSettingsService' instance");
        }
        return ptr;
    }

    static lowerReceiver(value) {
        // This works exactly the same as lower for non-trait interfaces
        return this.lower(value);
    }

    static read(dataStream) {
        return this.lift(dataStream.readPointer(13));
    }

    static write(dataStream, value) {
        dataStream.writePointer(13, this.lower(value));
    }

    static computeSize(value) {
        return 8;
    }
}



PK