如何将类型要求的安全性与枚举的安全性结合起来?

时间:2016-12-26 15:41:32

标签: swift enums type-safety

所以我正在使用swift中的sql数据库抽象层,我想尽可能安全地编写它。我的意思是,我宁愿让任何人都无法使用我的库在数据库中做任何非法行为。

我还无法完全解决的一件事是如何使从Sw​​ift类型到SQL数据类型的转换100%安全。所以我将解释我目前的实施及其缺陷:

因此在SQL中,许多数据类型都有参数。例如,如果要将列定义为VARCHAR,则需要为其指定VARCHAR的最大长度参数,例如VARCHAR(255)。您可以说VARCHAR是SQL的原始数据类型之一,VARCHAR(255)是更多指定的数据类型。为了在swift中以类型安全的方式表示,我创建了两个协议:

public protocol PrimitiveDataType {
    associatedtype Options = Void
}

public protocol SpecifiedDataType {
    associatedtype Primitive: PrimitiveDataType

    static var options: Primitive.Options { get }
    static func from(primitive: Primitive) -> Self
    var primitive: Primitive { get }
}

所以这是一个PrimitiveDataType的例子:

extension String: PrimitiveDataType {
    public enum DatabaseStringType {
        case char(length: Int), varchar(limit: Int), text
    }

    public typealias Options = DatabaseStringType
}

以下是SpecifiedDataType

的示例
struct Name {
    let value: String

    init?(_ value: String) {
        guard case 0...50 = value.characters.count else {
            return nil
        }

        self.value = value
    }
}

extension Name: SpecifiedDataType {
    static let options = String.DatabaseStringType.varchar(limit: 50)

    static func from(primitive: String) -> Name {
        return Name(primitive)!
    }

    var primitive: String {
        return value
    }
}

现在,我可以使用库中其他位置的信息来了解每当请求Name类型时预期的数据库列类型。这使我的库能够确保数据库列的类型始终正确。

我还没有编写所有代码,但它可能看起来像这样:

func createColumn<SDT: SpecifiedDataType>(ofType type: SDT) -> String {
    switch SDT.options {
    case let options as String.DatabaseStringType:
        switch options {
        case .text:
            return "TEXT"
        case .char(let length):
            return "CHAR(\(length))"
        case .varchar(let limit):
            return "VARCHAR(\(limit))"
        }
    case let length as UInt32.Options:
        // return something like UNSIGNED INTEGER(\(length)) or whatever

    // case etcetera
    }
}

这只有一个小问题。有效的原始数据类型集是有限的,但PrimitiveDataType协议的可能实现集是无限制的,因为我无法阻止任何人创建自己的协议实现。

因此,最好使用某种枚举而不是协议的多种实现。因为没有人可以扩展我创建的枚举,所以我可以将选项限制为我定义为有效类型的任何内容。但是,枚举会产生另一个在我当前实现中不存在的问题。

您可以在我当前的实现中看到,每当有人实现SpecifiedDataType时,其定义的选项如下:

static let options = String.DatabaseStringType.varchar(limit: 50)

我可以相信编译器会强制它们通过实现from(primitive: String)方法和primitive: String(计算)变量使其类型与字符串兼容。 AFAIK,如果我为enum切换协议,我将失去编译器强制的类型安全性。

总而言之,我希望拥有以下所有内容:

  • 开发人员声明他们希望与其类型对应的sql数据类型的方法。
  • 一种方法,然后强制开发人员实际使其类型与该sql数据类型兼容。
  • 为了保证使用我的库的开发人员不能以某种方式扩展sql-data类型列表,但只能使用我为他/她定义的那些。

到目前为止,我只知道如何做前两个,但不是最后一个,或最后一个,而不是前两个。我怎么得到这三个?

0 个答案:

没有答案