     case keyPath(String)
 }
 
-extension UnboxPath {
-    var components: [String] {
-        switch self {
-        case .key(let key):
-            return [key]
-        case .keyPath(let keyPath):
-            return keyPath.components(separatedBy: ".")
-        }
-    }
-}
-
 extension UnboxPath: CustomStringConvertible {
     var description: String {
         switch self {

private extension Unboxer {
    func unbox<R>(path: UnboxPath, transform: UnboxTransform<R>) throws -> R {
        do {
            var currentMode = UnboxingMode.dictionary(self.dictionary)
            let components = path.components
            let lastKey = try components.last.orThrow(UnboxPathError.emptyKeyPath)
            
            for key in components {
                switch currentMode {
                case .dictionary(let dictionary):
                    currentMode = try UnboxingMode(value: dictionary[key].orThrow(UnboxPathError.missingKey(key)))
                case .array(let array):
                    guard let index = Int(key), index < array.count else {
                        throw UnboxPathError.missingKey(key)
                    }
                    
                    currentMode = UnboxingMode(value: array[index])
                case .value(let value):
                    throw UnboxPathError.invalidValue(value, key)
                }
            }
            
            return try transform(currentMode.value).orThrow(UnboxPathError.invalidValue(currentMode.value, lastKey))
        } catch {
            if let publicError = error as? UnboxError {
                throw publicError


private extension Sequence {
    func map<T>(allowInvalidElements: Bool, transform: (Iterator.Element) throws -> T) throws -> [T] {
        if !allowInvalidElements {
            return try self.map(transform)
        }
        
        return self.flatMap {
            return try? transform($0)
        }
    }
}
