// This file was autogenerated by some hot garbage in the `uniffi` crate.
// Trust me, you don't want to mess with it!
import Foundation

// Depending on the consumer's build setup, the low-level FFI code
// might be in a separate module, or it might be compiled inline into
// this module. This is a bit of light hackery to work with both.
#if canImport(MozillaRustComponents)
    import MozillaRustComponents
#endif

private extension RustBuffer {
    // Allocate a new buffer, copying the contents of a `UInt8` array.
    init(bytes: [UInt8]) {
        let rbuf = bytes.withUnsafeBufferPointer { ptr in
            RustBuffer.from(ptr)
        }
        self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data)
    }

    static func from(_ ptr: UnsafeBufferPointer<UInt8>) -> RustBuffer {
        try! rustCall { ffi_syncmanager_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) }
    }

    // Frees the buffer in place.
    // The buffer must not be used after this is called.
    func deallocate() {
        try! rustCall { ffi_syncmanager_rustbuffer_free(self, $0) }
    }
}

private extension ForeignBytes {
    init(bufferPointer: UnsafeBufferPointer<UInt8>) {
        self.init(len: Int32(bufferPointer.count), data: bufferPointer.baseAddress)
    }
}

// For every type used in the interface, we provide helper methods for conveniently
// lifting and lowering that type from C-compatible data, and for reading and writing
// values of that type in a buffer.

// Helper classes/extensions that don't change.
// Someday, this will be in a library of its own.

private extension Data {
    init(rustBuffer: RustBuffer) {
        // TODO: This copies the buffer. Can we read directly from a
        // Rust buffer?
        self.init(bytes: rustBuffer.data!, count: Int(rustBuffer.len))
    }
}

// Define reader functionality.  Normally this would be defined in a class or
// struct, but we use standalone functions instead in order to make external
// types work.
//
// With external types, one swift source file needs to be able to call the read
// method on another source file's FfiConverter, but then what visibility
// should Reader have?
// - If Reader is fileprivate, then this means the read() must also
//   be fileprivate, which doesn't work with external types.
// - If Reader is internal/public, we'll get compile errors since both source
//   files will try define the same type.
//
// Instead, the read() method and these helper functions input a tuple of data

private func createReader(data: Data) -> (data: Data, offset: Data.Index) {
    (data: data, offset: 0)
}

// Reads an integer at the current offset, in big-endian order, and advances
// the offset on success. Throws if reading the integer would move the
// offset past the end of the buffer.
private func readInt<T: FixedWidthInteger>(_ reader: inout (data: Data, offset: Data.Index)) throws -> T {
    let range = reader.offset ..< reader.offset + MemoryLayout<T>.size
    guard reader.data.count >= range.upperBound else {
        throw UniffiInternalError.bufferOverflow
    }
    if T.self == UInt8.self {
        let value = reader.data[reader.offset]
        reader.offset += 1
        return value as! T
    }
    var value: T = 0
    let _ = withUnsafeMutableBytes(of: &value) { reader.data.copyBytes(to: $0, from: range) }
    reader.offset = range.upperBound
    return value.bigEndian
}

// Reads an arbitrary number of bytes, to be used to read
// raw bytes, this is useful when lifting strings
private func readBytes(_ reader: inout (data: Data, offset: Data.Index), count: Int) throws -> [UInt8] {
    let range = reader.offset ..< (reader.offset + count)
    guard reader.data.count >= range.upperBound else {
        throw UniffiInternalError.bufferOverflow
    }
    var value = [UInt8](repeating: 0, count: count)
    value.withUnsafeMutableBufferPointer { buffer in
        reader.data.copyBytes(to: buffer, from: range)
    }
    reader.offset = range.upperBound
    return value
}

// Reads a float at the current offset.
private func readFloat(_ reader: inout (data: Data, offset: Data.Index)) throws -> Float {
    return try Float(bitPattern: readInt(&reader))
}

// Reads a float at the current offset.
private func readDouble(_ reader: inout (data: Data, offset: Data.Index)) throws -> Double {
    return try Double(bitPattern: readInt(&reader))
}

// Indicates if the offset has reached the end of the buffer.
private func hasRemaining(_ reader: (data: Data, offset: Data.Index)) -> Bool {
    return reader.offset < reader.data.count
}

// Define writer functionality.  Normally this would be defined in a class or
// struct, but we use standalone functions instead in order to make external
// types work.  See the above discussion on Readers for details.

private func createWriter() -> [UInt8] {
    return []
}

private func writeBytes<S>(_ writer: inout [UInt8], _ byteArr: S) where S: Sequence, S.Element == UInt8 {
    writer.append(contentsOf: byteArr)
}

// Writes an integer in big-endian order.
//
// Warning: make sure what you are trying to write
// is in the correct type!
private func writeInt<T: FixedWidthInteger>(_ writer: inout [UInt8], _ value: T) {
    var value = value.bigEndian
    withUnsafeBytes(of: &value) { writer.append(contentsOf: $0) }
}

private func writeFloat(_ writer: inout [UInt8], _ value: Float) {
    writeInt(&writer, value.bitPattern)
}

private func writeDouble(_ writer: inout [UInt8], _ value: Double) {
    writeInt(&writer, value.bitPattern)
}

// Protocol for types that transfer other types across the FFI. This is
// analogous go the Rust trait of the same name.
private protocol FfiConverter {
    associatedtype FfiType
    associatedtype SwiftType

    static func lift(_ value: FfiType) throws -> SwiftType
    static func lower(_ value: SwiftType) -> FfiType
    static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType
    static func write(_ value: SwiftType, into buf: inout [UInt8])
}

// Types conforming to `Primitive` pass themselves directly over the FFI.
private protocol FfiConverterPrimitive: FfiConverter where FfiType == SwiftType {}

extension FfiConverterPrimitive {
    public static func lift(_ value: FfiType) throws -> SwiftType {
        return value
    }

    public static func lower(_ value: SwiftType) -> FfiType {
        return value
    }
}

// Types conforming to `FfiConverterRustBuffer` lift and lower into a `RustBuffer`.
// Used for complex types where it's hard to write a custom lift/lower.
private protocol FfiConverterRustBuffer: FfiConverter where FfiType == RustBuffer {}

extension FfiConverterRustBuffer {
    public static func lift(_ buf: RustBuffer) throws -> SwiftType {
        var reader = createReader(data: Data(rustBuffer: buf))
        let value = try read(from: &reader)
        if hasRemaining(reader) {
            throw UniffiInternalError.incompleteData
        }
        buf.deallocate()
        return value
    }

    public static func lower(_ value: SwiftType) -> RustBuffer {
        var writer = createWriter()
        write(value, into: &writer)
        return RustBuffer(bytes: writer)
    }
}

// An error type for FFI errors. These errors occur at the UniFFI level, not
// the library level.
private enum UniffiInternalError: LocalizedError {
    case bufferOverflow
    case incompleteData
    case unexpectedOptionalTag
    case unexpectedEnumCase
    case unexpectedNullPointer
    case unexpectedRustCallStatusCode
    case unexpectedRustCallError
    case unexpectedStaleHandle
    case rustPanic(_ message: String)

    public var errorDescription: String? {
        switch self {
        case .bufferOverflow: return "Reading the requested value would read past the end of the buffer"
        case .incompleteData: return "The buffer still has data after lifting its containing value"
        case .unexpectedOptionalTag: return "Unexpected optional tag; should be 0 or 1"
        case .unexpectedEnumCase: return "Raw enum value doesn't match any cases"
        case .unexpectedNullPointer: return "Raw pointer value was null"
        case .unexpectedRustCallStatusCode: return "Unexpected RustCallStatus code"
        case .unexpectedRustCallError: return "CALL_ERROR but no errorClass specified"
        case .unexpectedStaleHandle: return "The object in the handle map has been dropped already"
        case let .rustPanic(message): return message
        }
    }
}

private let CALL_SUCCESS: Int8 = 0
private let CALL_ERROR: Int8 = 1
private let CALL_PANIC: Int8 = 2

private extension RustCallStatus {
    init() {
        self.init(
            code: CALL_SUCCESS,
            errorBuf: RustBuffer(
                capacity: 0,
                len: 0,
                data: nil
            )
        )
    }
}

private func rustCall<T>(_ callback: (UnsafeMutablePointer<RustCallStatus>) -> T) throws -> T {
    try makeRustCall(callback, errorHandler: nil)
}

private func rustCallWithError<T>(
    _ errorHandler: @escaping (RustBuffer) throws -> Error,
    _ callback: (UnsafeMutablePointer<RustCallStatus>) -> T
) throws -> T {
    try makeRustCall(callback, errorHandler: errorHandler)
}

private func makeRustCall<T>(
    _ callback: (UnsafeMutablePointer<RustCallStatus>) -> T,
    errorHandler: ((RustBuffer) throws -> Error)?
) throws -> T {
    uniffiEnsureInitialized()
    var callStatus = RustCallStatus()
    let returnedVal = callback(&callStatus)
    try uniffiCheckCallStatus(callStatus: callStatus, errorHandler: errorHandler)
    return returnedVal
}

private func uniffiCheckCallStatus(
    callStatus: RustCallStatus,
    errorHandler: ((RustBuffer) throws -> Error)?
) throws {
    switch callStatus.code {
    case CALL_SUCCESS:
        return

    case CALL_ERROR:
        if let errorHandler = errorHandler {
            throw try errorHandler(callStatus.errorBuf)
        } else {
            callStatus.errorBuf.deallocate()
            throw UniffiInternalError.unexpectedRustCallError
        }

    case CALL_PANIC:
        // When the rust code sees a panic, it tries to construct a RustBuffer
        // with the message.  But if that code panics, then it just sends back
        // an empty buffer.
        if callStatus.errorBuf.len > 0 {
            throw try UniffiInternalError.rustPanic(FfiConverterString.lift(callStatus.errorBuf))
        } else {
            callStatus.errorBuf.deallocate()
            throw UniffiInternalError.rustPanic("Rust panic")
        }

    default:
        throw UniffiInternalError.unexpectedRustCallStatusCode
    }
}

// Public interface members begin here.

private struct FfiConverterBool: FfiConverter {
    typealias FfiType = Int8
    typealias SwiftType = Bool

    public static func lift(_ value: Int8) throws -> Bool {
        return value != 0
    }

    public static func lower(_ value: Bool) -> Int8 {
        return value ? 1 : 0
    }

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool {
        return try lift(readInt(&buf))
    }

    public static func write(_ value: Bool, into buf: inout [UInt8]) {
        writeInt(&buf, lower(value))
    }
}

private struct FfiConverterString: FfiConverter {
    typealias SwiftType = String
    typealias FfiType = RustBuffer

    public static func lift(_ value: RustBuffer) throws -> String {
        defer {
            value.deallocate()
        }
        if value.data == nil {
            return String()
        }
        let bytes = UnsafeBufferPointer<UInt8>(start: value.data!, count: Int(value.len))
        return String(bytes: bytes, encoding: String.Encoding.utf8)!
    }

    public static func lower(_ value: String) -> RustBuffer {
        return value.utf8CString.withUnsafeBufferPointer { ptr in
            // The swift string gives us int8_t, we want uint8_t.
            ptr.withMemoryRebound(to: UInt8.self) { ptr in
                // The swift string gives us a trailing null byte, we don't want it.
                let buf = UnsafeBufferPointer(rebasing: ptr.prefix(upTo: ptr.count - 1))
                return RustBuffer.from(buf)
            }
        }
    }

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String {
        let len: Int32 = try readInt(&buf)
        return try String(bytes: readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)!
    }

    public static func write(_ value: String, into buf: inout [UInt8]) {
        let len = Int32(value.utf8.count)
        writeInt(&buf, len)
        writeBytes(&buf, value.utf8)
    }
}

private struct FfiConverterTimestamp: FfiConverterRustBuffer {
    typealias SwiftType = Date

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Date {
        let seconds: Int64 = try readInt(&buf)
        let nanoseconds: UInt32 = try readInt(&buf)
        if seconds >= 0 {
            let delta = Double(seconds) + (Double(nanoseconds) / 1.0e9)
            return Date(timeIntervalSince1970: delta)
        } else {
            let delta = Double(seconds) - (Double(nanoseconds) / 1.0e9)
            return Date(timeIntervalSince1970: delta)
        }
    }

    public static func write(_ value: Date, into buf: inout [UInt8]) {
        var delta = value.timeIntervalSince1970
        var sign: Int64 = 1
        if delta < 0 {
            // The nanoseconds portion of the epoch offset must always be
            // positive, to simplify the calculation we will use the absolute
            // value of the offset.
            sign = -1
            delta = -delta
        }
        if delta.rounded(.down) > Double(Int64.max) {
            fatalError("Timestamp overflow, exceeds max bounds supported by Uniffi")
        }
        let seconds = Int64(delta)
        let nanoseconds = UInt32((delta - Double(seconds)) * 1.0e9)
        writeInt(&buf, sign * seconds)
        writeInt(&buf, nanoseconds)
    }
}

public protocol SyncManagerProtocol {
    func disconnect()
    func sync(params: SyncParams) throws -> SyncResult
    func getAvailableEngines() -> [String]
}

public class SyncManager: SyncManagerProtocol {
    fileprivate let pointer: UnsafeMutableRawPointer

    // TODO: We'd like this to be `private` but for Swifty reasons,
    // we can't implement `FfiConverter` without making this `required` and we can't
    // make it `required` without making it `public`.
    required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
        self.pointer = pointer
    }

    public convenience init() {
        self.init(unsafeFromRawPointer: try! rustCall {
            uniffi_syncmanager_fn_constructor_syncmanager_new($0)
        })
    }

    deinit {
        try! rustCall { uniffi_syncmanager_fn_free_syncmanager(pointer, $0) }
    }

    public func disconnect() {
        try!
            rustCall {
                uniffi_syncmanager_fn_method_syncmanager_disconnect(self.pointer, $0)
            }
    }

    public func sync(params: SyncParams) throws -> SyncResult {
        return try FfiConverterTypeSyncResult.lift(
            rustCallWithError(FfiConverterTypeSyncManagerError.lift) {
                uniffi_syncmanager_fn_method_syncmanager_sync(self.pointer,
                                                              FfiConverterTypeSyncParams.lower(params), $0)
            }
        )
    }

    public func getAvailableEngines() -> [String] {
        return try! FfiConverterSequenceString.lift(
            try!
                rustCall {
                    uniffi_syncmanager_fn_method_syncmanager_get_available_engines(self.pointer, $0)
                }
        )
    }
}

public struct FfiConverterTypeSyncManager: FfiConverter {
    typealias FfiType = UnsafeMutableRawPointer
    typealias SwiftType = SyncManager

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SyncManager {
        let v: UInt64 = try readInt(&buf)
        // The Rust code won't compile if a pointer won't fit in a UInt64.
        // We have to go via `UInt` because that's the thing that's the size of a pointer.
        let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v))
        if ptr == nil {
            throw UniffiInternalError.unexpectedNullPointer
        }
        return try lift(ptr!)
    }

    public static func write(_ value: SyncManager, into buf: inout [UInt8]) {
        // This fiddling is because `Int` is the thing that's the same size as a pointer.
        // The Rust code won't compile if a pointer won't fit in a `UInt64`.
        writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value)))))
    }

    public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> SyncManager {
        return SyncManager(unsafeFromRawPointer: pointer)
    }

    public static func lower(_ value: SyncManager) -> UnsafeMutableRawPointer {
        return value.pointer
    }
}

public func FfiConverterTypeSyncManager_lift(_ pointer: UnsafeMutableRawPointer) throws -> SyncManager {
    return try FfiConverterTypeSyncManager.lift(pointer)
}

public func FfiConverterTypeSyncManager_lower(_ value: SyncManager) -> UnsafeMutableRawPointer {
    return FfiConverterTypeSyncManager.lower(value)
}

public struct DeviceSettings {
    public var fxaDeviceId: String
    public var name: String
    public var kind: DeviceType

    // Default memberwise initializers are never public by default, so we
    // declare one manually.
    public init(fxaDeviceId: String, name: String, kind: DeviceType) {
        self.fxaDeviceId = fxaDeviceId
        self.name = name
        self.kind = kind
    }
}

extension DeviceSettings: Equatable, Hashable {
    public static func == (lhs: DeviceSettings, rhs: DeviceSettings) -> Bool {
        if lhs.fxaDeviceId != rhs.fxaDeviceId {
            return false
        }
        if lhs.name != rhs.name {
            return false
        }
        if lhs.kind != rhs.kind {
            return false
        }
        return true
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(fxaDeviceId)
        hasher.combine(name)
        hasher.combine(kind)
    }
}

public struct FfiConverterTypeDeviceSettings: FfiConverterRustBuffer {
    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DeviceSettings {
        return try DeviceSettings(
            fxaDeviceId: FfiConverterString.read(from: &buf),
            name: FfiConverterString.read(from: &buf),
            kind: FfiConverterTypeDeviceType.read(from: &buf)
        )
    }

    public static func write(_ value: DeviceSettings, into buf: inout [UInt8]) {
        FfiConverterString.write(value.fxaDeviceId, into: &buf)
        FfiConverterString.write(value.name, into: &buf)
        FfiConverterTypeDeviceType.write(value.kind, into: &buf)
    }
}

public func FfiConverterTypeDeviceSettings_lift(_ buf: RustBuffer) throws -> DeviceSettings {
    return try FfiConverterTypeDeviceSettings.lift(buf)
}

public func FfiConverterTypeDeviceSettings_lower(_ value: DeviceSettings) -> RustBuffer {
    return FfiConverterTypeDeviceSettings.lower(value)
}

public struct SyncAuthInfo {
    public var kid: String
    public var fxaAccessToken: String
    public var syncKey: String
    public var tokenserverUrl: String

    // Default memberwise initializers are never public by default, so we
    // declare one manually.
    public init(kid: String, fxaAccessToken: String, syncKey: String, tokenserverUrl: String) {
        self.kid = kid
        self.fxaAccessToken = fxaAccessToken
        self.syncKey = syncKey
        self.tokenserverUrl = tokenserverUrl
    }
}

extension SyncAuthInfo: Equatable, Hashable {
    public static func == (lhs: SyncAuthInfo, rhs: SyncAuthInfo) -> Bool {
        if lhs.kid != rhs.kid {
            return false
        }
        if lhs.fxaAccessToken != rhs.fxaAccessToken {
            return false
        }
        if lhs.syncKey != rhs.syncKey {
            return false
        }
        if lhs.tokenserverUrl != rhs.tokenserverUrl {
            return false
        }
        return true
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(kid)
        hasher.combine(fxaAccessToken)
        hasher.combine(syncKey)
        hasher.combine(tokenserverUrl)
    }
}

public struct FfiConverterTypeSyncAuthInfo: FfiConverterRustBuffer {
    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SyncAuthInfo {
        return try SyncAuthInfo(
            kid: FfiConverterString.read(from: &buf),
            fxaAccessToken: FfiConverterString.read(from: &buf),
            syncKey: FfiConverterString.read(from: &buf),
            tokenserverUrl: FfiConverterString.read(from: &buf)
        )
    }

    public static func write(_ value: SyncAuthInfo, into buf: inout [UInt8]) {
        FfiConverterString.write(value.kid, into: &buf)
        FfiConverterString.write(value.fxaAccessToken, into: &buf)
        FfiConverterString.write(value.syncKey, into: &buf)
        FfiConverterString.write(value.tokenserverUrl, into: &buf)
    }
}

public func FfiConverterTypeSyncAuthInfo_lift(_ buf: RustBuffer) throws -> SyncAuthInfo {
    return try FfiConverterTypeSyncAuthInfo.lift(buf)
}

public func FfiConverterTypeSyncAuthInfo_lower(_ value: SyncAuthInfo) -> RustBuffer {
    return FfiConverterTypeSyncAuthInfo.lower(value)
}

public struct SyncParams {
    public var reason: SyncReason
    public var engines: SyncEngineSelection
    public var enabledChanges: [String: Bool]
    public var localEncryptionKeys: [String: String]
    public var authInfo: SyncAuthInfo
    public var persistedState: String?
    public var deviceSettings: DeviceSettings

    // Default memberwise initializers are never public by default, so we
    // declare one manually.
    public init(reason: SyncReason, engines: SyncEngineSelection, enabledChanges: [String: Bool], localEncryptionKeys: [String: String], authInfo: SyncAuthInfo, persistedState: String?, deviceSettings: DeviceSettings) {
        self.reason = reason
        self.engines = engines
        self.enabledChanges = enabledChanges
        self.localEncryptionKeys = localEncryptionKeys
        self.authInfo = authInfo
        self.persistedState = persistedState
        self.deviceSettings = deviceSettings
    }
}

extension SyncParams: Equatable, Hashable {
    public static func == (lhs: SyncParams, rhs: SyncParams) -> Bool {
        if lhs.reason != rhs.reason {
            return false
        }
        if lhs.engines != rhs.engines {
            return false
        }
        if lhs.enabledChanges != rhs.enabledChanges {
            return false
        }
        if lhs.localEncryptionKeys != rhs.localEncryptionKeys {
            return false
        }
        if lhs.authInfo != rhs.authInfo {
            return false
        }
        if lhs.persistedState != rhs.persistedState {
            return false
        }
        if lhs.deviceSettings != rhs.deviceSettings {
            return false
        }
        return true
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(reason)
        hasher.combine(engines)
        hasher.combine(enabledChanges)
        hasher.combine(localEncryptionKeys)
        hasher.combine(authInfo)
        hasher.combine(persistedState)
        hasher.combine(deviceSettings)
    }
}

public struct FfiConverterTypeSyncParams: FfiConverterRustBuffer {
    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SyncParams {
        return try SyncParams(
            reason: FfiConverterTypeSyncReason.read(from: &buf),
            engines: FfiConverterTypeSyncEngineSelection.read(from: &buf),
            enabledChanges: FfiConverterDictionaryStringBool.read(from: &buf),
            localEncryptionKeys: FfiConverterDictionaryStringString.read(from: &buf),
            authInfo: FfiConverterTypeSyncAuthInfo.read(from: &buf),
            persistedState: FfiConverterOptionString.read(from: &buf),
            deviceSettings: FfiConverterTypeDeviceSettings.read(from: &buf)
        )
    }

    public static func write(_ value: SyncParams, into buf: inout [UInt8]) {
        FfiConverterTypeSyncReason.write(value.reason, into: &buf)
        FfiConverterTypeSyncEngineSelection.write(value.engines, into: &buf)
        FfiConverterDictionaryStringBool.write(value.enabledChanges, into: &buf)
        FfiConverterDictionaryStringString.write(value.localEncryptionKeys, into: &buf)
        FfiConverterTypeSyncAuthInfo.write(value.authInfo, into: &buf)
        FfiConverterOptionString.write(value.persistedState, into: &buf)
        FfiConverterTypeDeviceSettings.write(value.deviceSettings, into: &buf)
    }
}

public func FfiConverterTypeSyncParams_lift(_ buf: RustBuffer) throws -> SyncParams {
    return try FfiConverterTypeSyncParams.lift(buf)
}

public func FfiConverterTypeSyncParams_lower(_ value: SyncParams) -> RustBuffer {
    return FfiConverterTypeSyncParams.lower(value)
}

public struct SyncResult {
    public var status: ServiceStatus
    public var successful: [String]
    public var failures: [String: String]
    public var persistedState: String
    public var declined: [String]?
    public var nextSyncAllowedAt: Date?
    public var telemetryJson: String?

    // Default memberwise initializers are never public by default, so we
    // declare one manually.
    public init(status: ServiceStatus, successful: [String], failures: [String: String], persistedState: String, declined: [String]?, nextSyncAllowedAt: Date?, telemetryJson: String?) {
        self.status = status
        self.successful = successful
        self.failures = failures
        self.persistedState = persistedState
        self.declined = declined
        self.nextSyncAllowedAt = nextSyncAllowedAt
        self.telemetryJson = telemetryJson
    }
}

extension SyncResult: Equatable, Hashable {
    public static func == (lhs: SyncResult, rhs: SyncResult) -> Bool {
        if lhs.status != rhs.status {
            return false
        }
        if lhs.successful != rhs.successful {
            return false
        }
        if lhs.failures != rhs.failures {
            return false
        }
        if lhs.persistedState != rhs.persistedState {
            return false
        }
        if lhs.declined != rhs.declined {
            return false
        }
        if lhs.nextSyncAllowedAt != rhs.nextSyncAllowedAt {
            return false
        }
        if lhs.telemetryJson != rhs.telemetryJson {
            return false
        }
        return true
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(status)
        hasher.combine(successful)
        hasher.combine(failures)
        hasher.combine(persistedState)
        hasher.combine(declined)
        hasher.combine(nextSyncAllowedAt)
        hasher.combine(telemetryJson)
    }
}

public struct FfiConverterTypeSyncResult: FfiConverterRustBuffer {
    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SyncResult {
        return try SyncResult(
            status: FfiConverterTypeServiceStatus.read(from: &buf),
            successful: FfiConverterSequenceString.read(from: &buf),
            failures: FfiConverterDictionaryStringString.read(from: &buf),
            persistedState: FfiConverterString.read(from: &buf),
            declined: FfiConverterOptionSequenceString.read(from: &buf),
            nextSyncAllowedAt: FfiConverterOptionTimestamp.read(from: &buf),
            telemetryJson: FfiConverterOptionString.read(from: &buf)
        )
    }

    public static func write(_ value: SyncResult, into buf: inout [UInt8]) {
        FfiConverterTypeServiceStatus.write(value.status, into: &buf)
        FfiConverterSequenceString.write(value.successful, into: &buf)
        FfiConverterDictionaryStringString.write(value.failures, into: &buf)
        FfiConverterString.write(value.persistedState, into: &buf)
        FfiConverterOptionSequenceString.write(value.declined, into: &buf)
        FfiConverterOptionTimestamp.write(value.nextSyncAllowedAt, into: &buf)
        FfiConverterOptionString.write(value.telemetryJson, into: &buf)
    }
}

public func FfiConverterTypeSyncResult_lift(_ buf: RustBuffer) throws -> SyncResult {
    return try FfiConverterTypeSyncResult.lift(buf)
}

public func FfiConverterTypeSyncResult_lower(_ value: SyncResult) -> RustBuffer {
    return FfiConverterTypeSyncResult.lower(value)
}

// Note that we don't yet support `indirect` for enums.
// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
public enum ServiceStatus {
    case ok
    case networkError
    case serviceError
    case authError
    case backedOff
    case otherError
}

public struct FfiConverterTypeServiceStatus: FfiConverterRustBuffer {
    typealias SwiftType = ServiceStatus

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ServiceStatus {
        let variant: Int32 = try readInt(&buf)
        switch variant {
        case 1: return .ok

        case 2: return .networkError

        case 3: return .serviceError

        case 4: return .authError

        case 5: return .backedOff

        case 6: return .otherError

        default: throw UniffiInternalError.unexpectedEnumCase
        }
    }

    public static func write(_ value: ServiceStatus, into buf: inout [UInt8]) {
        switch value {
        case .ok:
            writeInt(&buf, Int32(1))

        case .networkError:
            writeInt(&buf, Int32(2))

        case .serviceError:
            writeInt(&buf, Int32(3))

        case .authError:
            writeInt(&buf, Int32(4))

        case .backedOff:
            writeInt(&buf, Int32(5))

        case .otherError:
            writeInt(&buf, Int32(6))
        }
    }
}

public func FfiConverterTypeServiceStatus_lift(_ buf: RustBuffer) throws -> ServiceStatus {
    return try FfiConverterTypeServiceStatus.lift(buf)
}

public func FfiConverterTypeServiceStatus_lower(_ value: ServiceStatus) -> RustBuffer {
    return FfiConverterTypeServiceStatus.lower(value)
}

extension ServiceStatus: Equatable, Hashable {}

// Note that we don't yet support `indirect` for enums.
// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
public enum SyncEngineSelection {
    case all
    case some(engines: [String])
}

public struct FfiConverterTypeSyncEngineSelection: FfiConverterRustBuffer {
    typealias SwiftType = SyncEngineSelection

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SyncEngineSelection {
        let variant: Int32 = try readInt(&buf)
        switch variant {
        case 1: return .all

        case 2: return try .some(
                engines: FfiConverterSequenceString.read(from: &buf)
            )

        default: throw UniffiInternalError.unexpectedEnumCase
        }
    }

    public static func write(_ value: SyncEngineSelection, into buf: inout [UInt8]) {
        switch value {
        case .all:
            writeInt(&buf, Int32(1))

        case let .some(engines):
            writeInt(&buf, Int32(2))
            FfiConverterSequenceString.write(engines, into: &buf)
        }
    }
}

public func FfiConverterTypeSyncEngineSelection_lift(_ buf: RustBuffer) throws -> SyncEngineSelection {
    return try FfiConverterTypeSyncEngineSelection.lift(buf)
}

public func FfiConverterTypeSyncEngineSelection_lower(_ value: SyncEngineSelection) -> RustBuffer {
    return FfiConverterTypeSyncEngineSelection.lower(value)
}

extension SyncEngineSelection: Equatable, Hashable {}

public enum SyncManagerError {
    // Simple error enums only carry a message
    case UnknownEngine(message: String)

    // Simple error enums only carry a message
    case UnsupportedFeature(message: String)

    // Simple error enums only carry a message
    case Sync15Error(message: String)

    // Simple error enums only carry a message
    case UrlParseError(message: String)

    // Simple error enums only carry a message
    case InterruptedError(message: String)

    // Simple error enums only carry a message
    case JsonError(message: String)

    // Simple error enums only carry a message
    case LoginsError(message: String)

    // Simple error enums only carry a message
    case PlacesError(message: String)

    // Simple error enums only carry a message
    case AnyhowError(message: String)

    fileprivate static func uniffiErrorHandler(_ error: RustBuffer) throws -> Error {
        return try FfiConverterTypeSyncManagerError.lift(error)
    }
}

public struct FfiConverterTypeSyncManagerError: FfiConverterRustBuffer {
    typealias SwiftType = SyncManagerError

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SyncManagerError {
        let variant: Int32 = try readInt(&buf)
        switch variant {
        case 1: return try .UnknownEngine(
                message: FfiConverterString.read(from: &buf)
            )

        case 2: return try .UnsupportedFeature(
                message: FfiConverterString.read(from: &buf)
            )

        case 3: return try .Sync15Error(
                message: FfiConverterString.read(from: &buf)
            )

        case 4: return try .UrlParseError(
                message: FfiConverterString.read(from: &buf)
            )

        case 5: return try .InterruptedError(
                message: FfiConverterString.read(from: &buf)
            )

        case 6: return try .JsonError(
                message: FfiConverterString.read(from: &buf)
            )

        case 7: return try .LoginsError(
                message: FfiConverterString.read(from: &buf)
            )

        case 8: return try .PlacesError(
                message: FfiConverterString.read(from: &buf)
            )

        case 9: return try .AnyhowError(
                message: FfiConverterString.read(from: &buf)
            )

        default: throw UniffiInternalError.unexpectedEnumCase
        }
    }

    public static func write(_ value: SyncManagerError, into buf: inout [UInt8]) {
        switch value {
        case let .UnknownEngine(message):
            writeInt(&buf, Int32(1))
        case let .UnsupportedFeature(message):
            writeInt(&buf, Int32(2))
        case let .Sync15Error(message):
            writeInt(&buf, Int32(3))
        case let .UrlParseError(message):
            writeInt(&buf, Int32(4))
        case let .InterruptedError(message):
            writeInt(&buf, Int32(5))
        case let .JsonError(message):
            writeInt(&buf, Int32(6))
        case let .LoginsError(message):
            writeInt(&buf, Int32(7))
        case let .PlacesError(message):
            writeInt(&buf, Int32(8))
        case let .AnyhowError(message):
            writeInt(&buf, Int32(9))
        }
    }
}

extension SyncManagerError: Equatable, Hashable {}

extension SyncManagerError: Error {}

// Note that we don't yet support `indirect` for enums.
// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion.
public enum SyncReason {
    case scheduled
    case user
    case preSleep
    case startup
    case enabledChange
    case backgrounded
}

public struct FfiConverterTypeSyncReason: FfiConverterRustBuffer {
    typealias SwiftType = SyncReason

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SyncReason {
        let variant: Int32 = try readInt(&buf)
        switch variant {
        case 1: return .scheduled

        case 2: return .user

        case 3: return .preSleep

        case 4: return .startup

        case 5: return .enabledChange

        case 6: return .backgrounded

        default: throw UniffiInternalError.unexpectedEnumCase
        }
    }

    public static func write(_ value: SyncReason, into buf: inout [UInt8]) {
        switch value {
        case .scheduled:
            writeInt(&buf, Int32(1))

        case .user:
            writeInt(&buf, Int32(2))

        case .preSleep:
            writeInt(&buf, Int32(3))

        case .startup:
            writeInt(&buf, Int32(4))

        case .enabledChange:
            writeInt(&buf, Int32(5))

        case .backgrounded:
            writeInt(&buf, Int32(6))
        }
    }
}

public func FfiConverterTypeSyncReason_lift(_ buf: RustBuffer) throws -> SyncReason {
    return try FfiConverterTypeSyncReason.lift(buf)
}

public func FfiConverterTypeSyncReason_lower(_ value: SyncReason) -> RustBuffer {
    return FfiConverterTypeSyncReason.lower(value)
}

extension SyncReason: Equatable, Hashable {}

private struct FfiConverterOptionString: FfiConverterRustBuffer {
    typealias SwiftType = String?

    public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
        guard let value = value else {
            writeInt(&buf, Int8(0))
            return
        }
        writeInt(&buf, Int8(1))
        FfiConverterString.write(value, into: &buf)
    }

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
        switch try readInt(&buf) as Int8 {
        case 0: return nil
        case 1: return try FfiConverterString.read(from: &buf)
        default: throw UniffiInternalError.unexpectedOptionalTag
        }
    }
}

private struct FfiConverterOptionTimestamp: FfiConverterRustBuffer {
    typealias SwiftType = Date?

    public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
        guard let value = value else {
            writeInt(&buf, Int8(0))
            return
        }
        writeInt(&buf, Int8(1))
        FfiConverterTimestamp.write(value, into: &buf)
    }

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
        switch try readInt(&buf) as Int8 {
        case 0: return nil
        case 1: return try FfiConverterTimestamp.read(from: &buf)
        default: throw UniffiInternalError.unexpectedOptionalTag
        }
    }
}

private struct FfiConverterOptionSequenceString: FfiConverterRustBuffer {
    typealias SwiftType = [String]?

    public static func write(_ value: SwiftType, into buf: inout [UInt8]) {
        guard let value = value else {
            writeInt(&buf, Int8(0))
            return
        }
        writeInt(&buf, Int8(1))
        FfiConverterSequenceString.write(value, into: &buf)
    }

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
        switch try readInt(&buf) as Int8 {
        case 0: return nil
        case 1: return try FfiConverterSequenceString.read(from: &buf)
        default: throw UniffiInternalError.unexpectedOptionalTag
        }
    }
}

private struct FfiConverterSequenceString: FfiConverterRustBuffer {
    typealias SwiftType = [String]

    public static func write(_ value: [String], into buf: inout [UInt8]) {
        let len = Int32(value.count)
        writeInt(&buf, len)
        for item in value {
            FfiConverterString.write(item, into: &buf)
        }
    }

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String] {
        let len: Int32 = try readInt(&buf)
        var seq = [String]()
        seq.reserveCapacity(Int(len))
        for _ in 0 ..< len {
            try seq.append(FfiConverterString.read(from: &buf))
        }
        return seq
    }
}

private struct FfiConverterDictionaryStringBool: FfiConverterRustBuffer {
    public static func write(_ value: [String: Bool], into buf: inout [UInt8]) {
        let len = Int32(value.count)
        writeInt(&buf, len)
        for (key, value) in value {
            FfiConverterString.write(key, into: &buf)
            FfiConverterBool.write(value, into: &buf)
        }
    }

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String: Bool] {
        let len: Int32 = try readInt(&buf)
        var dict = [String: Bool]()
        dict.reserveCapacity(Int(len))
        for _ in 0 ..< len {
            let key = try FfiConverterString.read(from: &buf)
            let value = try FfiConverterBool.read(from: &buf)
            dict[key] = value
        }
        return dict
    }
}

private struct FfiConverterDictionaryStringString: FfiConverterRustBuffer {
    public static func write(_ value: [String: String], into buf: inout [UInt8]) {
        let len = Int32(value.count)
        writeInt(&buf, len)
        for (key, value) in value {
            FfiConverterString.write(key, into: &buf)
            FfiConverterString.write(value, into: &buf)
        }
    }

    public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String: String] {
        let len: Int32 = try readInt(&buf)
        var dict = [String: String]()
        dict.reserveCapacity(Int(len))
        for _ in 0 ..< len {
            let key = try FfiConverterString.read(from: &buf)
            let value = try FfiConverterString.read(from: &buf)
            dict[key] = value
        }
        return dict
    }
}

private enum InitializationResult {
    case ok
    case contractVersionMismatch
    case apiChecksumMismatch
}

// Use a global variables to perform the versioning checks. Swift ensures that
// the code inside is only computed once.
private var initializationResult: InitializationResult {
    // Get the bindings contract version from our ComponentInterface
    let bindings_contract_version = 22
    // Get the scaffolding contract version by calling the into the dylib
    let scaffolding_contract_version = ffi_syncmanager_uniffi_contract_version()
    if bindings_contract_version != scaffolding_contract_version {
        return InitializationResult.contractVersionMismatch
    }
    if uniffi_syncmanager_checksum_method_syncmanager_disconnect() != 41067 {
        return InitializationResult.apiChecksumMismatch
    }
    if uniffi_syncmanager_checksum_method_syncmanager_sync() != 40502 {
        return InitializationResult.apiChecksumMismatch
    }
    if uniffi_syncmanager_checksum_method_syncmanager_get_available_engines() != 20164 {
        return InitializationResult.apiChecksumMismatch
    }
    if uniffi_syncmanager_checksum_constructor_syncmanager_new() != 14200 {
        return InitializationResult.apiChecksumMismatch
    }

    return InitializationResult.ok
}

private func uniffiEnsureInitialized() {
    switch initializationResult {
    case .ok:
        break
    case .contractVersionMismatch:
        fatalError("UniFFI contract version mismatch: try cleaning and rebuilding your project")
    case .apiChecksumMismatch:
        fatalError("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
    }
}
