<commit_before>//
//  SWXMLHash+TypeConversion.swift
//  SWXMLHash
//
//  Created by <NAME> on 29.02.2016.
//
//


// MARK: - XMLIndexerDeserializable

public protocol XMLIndexerDeserializable {
    static func deserialize(element: XMLIndexer) throws -> Self
}

public extension XMLIndexerDeserializable {
    static func deserialize(element: XMLIndexer) throws -> Self {
        throw XMLDeserializationError.MissingDeserializationMethod(method: "XMLIndexerDeserializable.deserialize(element: XMLIndexer)")
    }
}


// MARK: - XMLElementDeserializable

public protocol XMLElementDeserializable {
    static func deserialize(element: XMLElement) throws -> Self
}

public extension XMLElementDeserializable {
    static func deserialize(element: XMLElement) throws -> Self {
        throw XMLDeserializationError.MissingDeserializationMethod(method: "XMLElementDeserializable.deserialize(element: XMLElement)")
    }
}


public extension XMLIndexer {
    
    // MARK: - XMLElementDeserializable
    
    func value<T: XMLElementDeserializable>() throws -> T {
        switch self {
        case .Element(let element):
            return try T.deserialize(element)
        default:
            throw XMLDeserializationError.InvalidNode(node: self)
        }
    }
    
    func value<T: XMLElementDeserializable>() throws -> T? {
        switch self {
        case .Element(let element):
            return try T.deserialize(element)
        default:
            return nil
        }
    }
    
    func value<T: XMLElementDeserializable>() throws -> [T] {
        switch self {
        case .List(let elements):
            return try elements.map{ try T.deserialize($0) }
        case .Element(let element):
            return try [element].map{ try T.deserialize($0) }
        default:
            return []
        }
    }
    
    func value<T: XMLElementDeserializable>() throws -> [T]? {
        switch self {
        case .List(let elements):
            return try elements.map{ try T.deserialize($0) }
        case .Element(let element):
            return try [element].map{ try T.deserialize($0) }
        default:
            return nil
        }
    }
    
    func value<T: XMLElementDeserializable>() throws -> [T?] {
        switch self {
        case .List(let elements):
            return try elements.map{ try T.deserialize($0) }
        case .Element(let element):
            return try [element].map{ try T.deserialize($0) }
        default:
            return []
        }
    }
    
    
    // MARK: - XMLIndexerDeserializable
    
    func value<T: XMLIndexerDeserializable>() throws -> T {
        switch self {
        case .Element:
            return try T.deserialize(self)
        default:
            throw XMLDeserializationError.InvalidNode(node: self)
        }
    }
    
    func value<T: XMLIndexerDeserializable>() throws -> T? {
        switch self {
        case .Element:
            return try T.deserialize(self)
        default:
            return nil
        }
    }
    
    func value<T where T: XMLIndexerDeserializable>() throws -> [T] {
        switch self {
        case .List(let elements):
            return try elements.map { try T.deserialize( XMLIndexer($0) ) }
        case .Element(let element):
            return try [element].map { try T.deserialize( XMLIndexer($0) ) }
        default:
            throw XMLDeserializationError.InvalidNode(node: self)
        }
    }
    
    func value<T: XMLIndexerDeserializable>() throws -> [T]? {
        switch self {
        case .List(let elements):
            return try elements.map { try T.deserialize( XMLIndexer($0) ) }
        case .Element(let element):
            return try [element].map { try T.deserialize( XMLIndexer($0) ) }
        default:
            throw XMLDeserializationError.InvalidNode(node: self)
        }
    }
    
    func value<T: XMLIndexerDeserializable>() throws -> [T?] {
        switch self {
        case .List(let elements):
            return try elements.map{  try T.deserialize( XMLIndexer($0) ) }
        case .Element(let element):
            return try [element].map{ try T.deserialize( XMLIndexer($0) ) }
        default:
            throw XMLDeserializationError.InvalidNode(node: self)
        }
    }
}

private extension XMLElement {
    func nonEmptyTextOrThrow() throws -> String {
        if let text = self.text where text.characters.count > 0 {
            return text
        } else { throw XMLDeserializationError.EmptyNodeValue }
    }
}

public enum XMLDeserializationError: ErrorType, CustomStringConvertible {
    case InvalidNode(node: XMLIndexer)
    case InvalidElement(element: XMLElement)
    case TypeConversion(type: String, element: XMLElement)
    case EmptyNodeValue
    case MissingDeserializationMethod(method: String)
    
    public var description: String {
        switch self {
        case .InvalidNode(let node):
            return "Invalid node: \(node)"
        case .InvalidElement(let element):
            return "Invalid element: \(element)"
        case .TypeConversion(let type, let node):
            return "Can't convert \(node) to \(type)"
        case .EmptyNodeValue:
            return "Empty node value"
        case .MissingDeserializationMethod(let method):
            return "Missing deserialization method: \(method)"
        }
    }
}


// MARK: - Common types deserialization

extension String: XMLElementDeserializable {
    public static func deserialize(element: XMLElement) throws -> String {
        guard let text = element.text
        else { throw XMLDeserializationError.TypeConversion(type: "String", element: element) }
        return text
    }
}

extension Int: XMLElementDeserializable {
    public static func deserialize(element: XMLElement) throws -> Int {
        guard let value = Int(try element.nonEmptyTextOrThrow())
        else { throw XMLDeserializationError.TypeConversion(type: "Int", element: element) }
        return value
    }
}

extension Double: XMLElementDeserializable {
    public static func deserialize(element: XMLElement) throws -> Double {
        guard let value = Double(try element.nonEmptyTextOrThrow())
        else { throw XMLDeserializationError.TypeConversion(type: "Double", element: element) }
        return value
    }<commit_msg>Unify `XMLDeserializationError` cases<commit_after>//
//  SWXMLHash+TypeConversion.swift
//  SWXMLHash
//
//  Created by <NAME> on 29.02.2016.
//
//


// MARK: - XMLIndexerDeserializable

public protocol XMLIndexerDeserializable {
    static func deserialize(element: XMLIndexer) throws -> Self
}

public extension XMLIndexerDeserializable {
    static func deserialize(element: XMLIndexer) throws -> Self {
        throw XMLDeserializationError.ImplementationIsMissing(method: "XMLIndexerDeserializable.deserialize(element: XMLIndexer)")
    }
}


// MARK: - XMLElementDeserializable

public protocol XMLElementDeserializable {
    static func deserialize(element: XMLElement) throws -> Self
}

public extension XMLElementDeserializable {
    static func deserialize(element: XMLElement) throws -> Self {
        throw XMLDeserializationError.ImplementationIsMissing(method: "XMLElementDeserializable.deserialize(element: XMLElement)")
    }
}


public extension XMLIndexer {
    
    // MARK: - XMLElementDeserializable
    
    func value<T: XMLElementDeserializable>() throws -> T {
        switch self {
        case .Element(let element):
            return try T.deserialize(element)
        default:
            throw XMLDeserializationError.NodeIsInvalid(node: self)
        }
    }
    
    func value<T: XMLElementDeserializable>() throws -> T? {
        switch self {
        case .Element(let element):
            return try T.deserialize(element)
        default:
            return nil
        }
    }
    
    func value<T: XMLElementDeserializable>() throws -> [T] {
        switch self {
        case .List(let elements):
            return try elements.map{ try T.deserialize($0) }
        case .Element(let element):
            return try [element].map{ try T.deserialize($0) }
        default:
            return []
        }
    }
    
    func value<T: XMLElementDeserializable>() throws -> [T]? {
        switch self {
        case .List(let elements):
            return try elements.map{ try T.deserialize($0) }
        case .Element(let element):
            return try [element].map{ try T.deserialize($0) }
        default:
            return nil
        }
    }
    
    func value<T: XMLElementDeserializable>() throws -> [T?] {
        switch self {
        case .List(let elements):
            return try elements.map{ try T.deserialize($0) }
        case .Element(let element):
            return try [element].map{ try T.deserialize($0) }
        default:
            return []
        }
    }
    
    
    // MARK: - XMLIndexerDeserializable
    
    func value<T: XMLIndexerDeserializable>() throws -> T {
        switch self {
        case .Element:
            return try T.deserialize(self)
        default:
            throw XMLDeserializationError.NodeIsInvalid(node: self)
        }
    }
    
    func value<T: XMLIndexerDeserializable>() throws -> T? {
        switch self {
        case .Element:
            return try T.deserialize(self)
        default:
            return nil
        }
    }
    
    func value<T where T: XMLIndexerDeserializable>() throws -> [T] {
        switch self {
        case .List(let elements):
            return try elements.map { try T.deserialize( XMLIndexer($0) ) }
        case .Element(let element):
            return try [element].map { try T.deserialize( XMLIndexer($0) ) }
        default:
            throw XMLDeserializationError.NodeIsInvalid(node: self)
        }
    }
    
    func value<T: XMLIndexerDeserializable>() throws -> [T]? {
        switch self {
        case .List(let elements):
            return try elements.map { try T.deserialize( XMLIndexer($0) ) }
        case .Element(let element):
            return try [element].map { try T.deserialize( XMLIndexer($0) ) }
        default:
            throw XMLDeserializationError.NodeIsInvalid(node: self)
        }
    }
    
    func value<T: XMLIndexerDeserializable>() throws -> [T?] {
        switch self {
        case .List(let elements):
            return try elements.map{  try T.deserialize( XMLIndexer($0) ) }
        case .Element(let element):
            return try [element].map{ try T.deserialize( XMLIndexer($0) ) }
        default:
            throw XMLDeserializationError.NodeIsInvalid(node: self)
        }
    }
}

private extension XMLElement {
    func nonEmptyTextOrThrow() throws -> String {
        if let text = self.text where text.characters.count > 0 {
            return text
        } else { throw XMLDeserializationError.NodeHasNoValue }
    }
}

public enum XMLDeserializationError: ErrorType, CustomStringConvertible {
    case ImplementationIsMissing(method: String)
    case NodeIsInvalid(node: XMLIndexer)
    case NodeHasNoValue
    case TypeConversionFailed(type: String, element: XMLElement)
    
    public var description: String {
        switch self {
        case .ImplementationIsMissing(let method):
            return "This deserialization method is not implemented: \(method)"
        case .NodeIsInvalid(let node):
            return "This node is invalid: \(node)"
        case .NodeHasNoValue:
            return "This node is empty"
        case .TypeConversionFailed(let type, let node):
            return "Can't convert node \(node) to value of type \(type)"
        }
    }
}


// MARK: - Common types deserialization

extension String: XMLElementDeserializable {
    public static func deserialize(element: XMLElement) throws -> String {
        guard let text = element.text
        else { throw XMLDeserializationError.TypeConversionFailed(type: "String", element: element) }
        return text
    }
}

extension Int: XMLElementDeserializable {
    public static func deserialize(element: XMLElement) throws -> Int {
        guard let value = Int(try element.nonEmptyTextOrThrow())
        else { throw XMLDeserializationError.TypeConversionFailed(type: "Int", element: element) }
        return value
    }
}

extension Double: XMLElementDeserializable {
    public static func deserialize(element: XMLElement) throws -> Double {
        guard let value = Double(try element.nonEmptyTextOrThrow())
        else { throw XMLDeserializationError.TypeConversionFailed(type: "Double", element: element) }
        return value
    }
{'fraction_non_alphanumeric': 0.0950900685477443, 'fraction_numerical': 0.003347680535628886, 'mean_word_length': 2.330767188744359, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3382522', 'n_tokens_mistral': 3439, 'n_tokens_neox': 3475, 'n_words': 900}