api-swift / Sources / IrdAPI / Core / ResponseDecoder.swift
270 words
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import Foundation

/// The decode table shared by every request:
///
/// | status  | body                          | result                                        |
/// |---------|-------------------------------|-----------------------------------------------|
/// | 2xx     | empty                         | `{}`                                          |
/// | 2xx     | JSON                          | the value                                     |
/// | 2xx     | not JSON                      | `.transport("Failed to parse JSON response")` |
/// | non-2xx | empty                         | `.api(status, nil, "Empty error response")`   |
/// | non-2xx | `{error:{code,message}}`      | `.api(status, code, message, body)`           |
/// | non-2xx | `{error:{reason,details}}`    | `.api(status, reason, details ?? reason)`     |
/// | non-2xx | `{error:"code"}`              | `.api(status, code, code, body)`              |
/// | non-2xx | not JSON                      | `.api(status, nil, text, nil)`                |
enum ResponseDecoder {
    static func decode(status: Int, body: Data) throws -> (data: Data, json: JSONValue) {
        let ok = (200..<300).contains(status)
        let text = String(decoding: body, as: UTF8.self).trimmingCharacters(in: .whitespacesAndNewlines)
        if text.isEmpty {
            if ok { return (Data("{}".utf8), .object([:])) }
            throw IrdError.api(status: status, code: nil, message: "Empty error response", body: nil)
        }
        let parsed = try? JSONValue.parse(body)
        if ok {
            guard let parsed else { throw IrdError.transport("Failed to parse JSON response") }
            return (body, parsed)
        }
        guard let parsed, case .object = parsed else {
            throw IrdError.api(status: status, code: nil, message: text, body: nil)
        }
        let (code, message) = errorEnvelope(parsed, fallback: text)
        throw IrdError.api(status: status, code: code, message: message, body: parsed)
    }

    static func errorEnvelope(_ body: JSONValue, fallback: String) -> (code: String?, message: String) {
        switch body["error"] {
        case .string(let code)?:
            return (code, code)
        case .object(let e)?:
            let code = e["code"]?.stringValue ?? e["reason"]?.stringValue
            let message = e["message"]?.stringValue ?? e["details"]?.stringValue ?? code ?? fallback
            return (code, message)
        default:
            return (nil, fallback)
        }
    }
}

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/api-swift.git