enumの値チェック

enumをrawValueから生成するときに、値を制限したいことはよくあると思います。 Himotokiと組み合わせることを前提に、rawValueが不正だった場合に詳細なエラーメッセージを含むDecodeError.TypeMismatch をthrowするinitializerを作ったので共有します。 なおRawValueがIntの場合限定です。有効範囲を0から11として無理やりinitializerを呼んでみて、ダメだったらエラーにするというなんとも無理やりな実装です。

こんな風に使います。

enum Status: Int {
    case Initial = 0, Active = 1, Inactive = 2
}

struct Bar: Decodable {
    let status: Status
    static func decode(e: Extractor) throws -> Bar {
        return Bar(
            status: try Status(int: try e <| "status", keyPath: "status")
        )
    }
}

以下、init(int:) の実装です。

import Foundation
import Himotoki

extension RawRepresentable where Self.RawValue == Int {

    /// Failable initializer for any RawRepresentable(such as enum) types
    /// If passed rawValue didn't match expectation,
    /// throws detailed error with `expected` and passed in `keyPath`.
    ///
    /// - parameter int: the rawValue
    /// - parameter keyPath:
    /// - throws:  DecodeError.TypeMismatch
    /// - seealso: for Error type, see Himotoki
    init(int: Self.RawValue, keyPath: KeyPath = "") throws {
        guard let _ = Self(rawValue: int) else {
            throw DecodeError.TypeMismatch(expected:
                expectedIntValues(Self.self), actual: "\(int)", keyPath: keyPath)
        }
        self.init(rawValue: int)!
    }

    init?(optionalInt: Self.RawValue?, keyPath: KeyPath = "") throws {
        guard let int = optionalInt else {
            return nil
        }
        try self.init(int: int)
    }
}

private func expectedIntValues<T: RawRepresentable where T.RawValue == Int>
    (type: T.Type) -> String
{
    return (0...11) /// Note: Assuming that the valid rawValues are between 0 to 11 .
        .flatMap{type.init(rawValue: $0)}
        .reduce("") {acc, e in "\(acc == "" ? "" : acc+",")\(e.rawValue)"}
}

フィードバックいただけると嬉しいです。