spiegel-keyman/ios/engine/KMEI/KeymanEngine/Classes/Resource Data/LanguageResource.swift

100 lines
3.3 KiB
Swift

//
// LanguageResource.swift
// KeymanEngine
//
// Created by Joshua Horton on 8/16/19.
// Copyright © 2019 SIL International. All rights reserved.
//
import Foundation
public enum LanguageResourceType: String, Codable {
case keyboard
case lexicalModel
}
// Subclasses must implement Equatable; Swift doesn't like it directly on the root protocol.
public protocol AnyLanguageResourceFullID {
var id: String { get }
var languageID: String { get }
var type: LanguageResourceType { get }
func matches(_ other: AnyLanguageResourceFullID, requireLanguageMatch: Bool) -> Bool
}
extension AnyLanguageResourceFullID {
// For matching when we're not sure if the underlying types actually match.
public func matches(_ other: AnyLanguageResourceFullID, requireLanguageMatch: Bool = true) -> Bool {
if requireLanguageMatch {
return id == other.id && type == other.type && languageID == other.languageID
} else {
return id == other.id && type == other.type
}
}
}
// Equatable only really makes sense when the types are identical.
extension AnyLanguageResourceFullID where Self: Equatable {
public static func ==(lhs: Self, rhs: Self) -> Bool {
return lhs.id == rhs.id && lhs.languageID == rhs.languageID && lhs.type == rhs.type
}
}
extension AnyLanguageResourceFullID {
var description: String {
return "{\(type): {id = \(id), languageID=\(languageID)}}"
}
}
public protocol LanguageResourceFullID: AnyLanguageResourceFullID {
associatedtype Resource: LanguageResource where Resource.FullID == Self
}
// Alas, 'associatedtype' stuff isn't exactly generic, and it's impossible to wildcard.
// So, this supports Swift's "type erasure" pattern, acting as a "wildcarded"
// LanguageResource that doesn't care about the specific associatedtype(s) that
// specific LanguageResources use.
public protocol AnyLanguageResource {
var id: String { get }
var name: String { get }
var languageID: String { get }
// Was not always tracked within KeymanEngine - is optional for legacy reasons.
var packageID: String? { get }
var packageKey: KeymanPackage.Key { get }
var fullID: AnyLanguageResourceFullID { get }
var version: String { get }
// Used for generating QR codes.
var sharableURL: String? { get }
// Used during resource installation
var fonts: [Font] { get }
var sourceFilename: String { get }
}
extension AnyLanguageResource {
public var packageKey: KeymanPackage.Key {
return KeymanPackage.Key(forResource: self)
}
}
// Necessary due to Swift details 'documented' at
// https://stackoverflow.com/questions/42561685/why-cant-a-get-only-property-requirement-in-a-protocol-be-satisfied-by-a-proper
public protocol LanguageResource: AnyLanguageResource {
associatedtype FullID: LanguageResourceFullID where FullID: Equatable, FullID.Resource == Self
associatedtype Package: KeymanPackage
var typedFullID: FullID { get }
}
extension LanguageResource {
// Thanks to https://stackoverflow.com/a/58774558 (on same thread mentioned above)
// for this approach.
public var fullID: AnyLanguageResourceFullID {
return typedFullID as AnyLanguageResourceFullID
}
}
internal protocol KMPInitializableLanguageResource: LanguageResource {
associatedtype Metadata: KMPResource where Metadata.LanguageResourceType == Self
init?(from metadata: Metadata, packageID: String, lgCode: String)
}