utahiosmac / Marshal

Marshaling the typeless wild west of [String: Any]
MIT License
697 stars 62 forks source link

Make `MarshalError` more descriptive #132

Open olejnjak opened 6 years ago

olejnjak commented 6 years ago

Hi,

I love using Marshal for mapping as it's far more simple and straightforward than other options.

I think that the only pain is that if mapping fails for some reason, it is very complicated to determine where the problem occurred (especially if you have complicated JSON structure with repeating keys). I thought that this could be solved by making MarshalError a bit more descriptive. This would mean that MarshalError would become a struct.

Let's say it would look like this (maybe adding a property to add the payload which failed to map would be also useful).

public struct MarshalError {
    public enum Kind { ... current MarshalError cases }

    public let kind: Kind
    public let objectTypes: [Any.Type]
}

This structure would allow any ValueType to add which type didn't managed to map itself. I've been playing with this idea in playground so this would be the first idea of implementation, but I'm sure it would be possible to wrap it into some function which would make the use of it easier.

I'm open to implementing this change but I'd love to discuss it first - it's a big breaking change and I don't want to make it without any chance to get this merged 🙂.

Sega-Zero commented 5 years ago

Wouldn't it be better to add another indirect enum case that will wrap other cases? Something similar to this:

indirect enum MyError: Error, CustomStringConvertible {
    case type1(type: Any.Type)
    case type2(type: Any.Type)
    case wrapping(error: MyError, type: Any.Type)

    var description: String {
        switch self {
        case .type1(let type):
            return "Type1 (type: \(type))"
        case .type2(let type):
            return "Type1 (type: \(type))"
        case let .wrapping(error, type):
            return """
                   Wrapping error for type: \(type). Containing error:
                   \(error)
                   """
        }
    }
}

struct A {
    init() throws {
        throw MyError.type1(type: type(of: self))
    }
}

struct B {
    init() throws {
        do {
            _ = try A()
        } catch let error as MyError {
            throw MyError.wrapping(error: error, type: type(of: self))
        }
    }
}