f512d94c74
git-subtree-dir: clients/ds-app git-subtree-mainline:a24e3e6f22git-subtree-split:5206cf3b0c
63 lines
2.6 KiB
Swift
63 lines
2.6 KiB
Swift
import Foundation
|
|
|
|
/// Typed error decoded from the contract's two error body shapes ({detail: String} OR
|
|
/// {detail: {error_code, message}}) and keyed by HTTP status.
|
|
///
|
|
/// Boundary note: an in-body synthesis failure (AskResponse.synthesis_status == "backend_unavailable")
|
|
/// is an HTTP success and is NOT a DSError — only a true HTTP 503 maps to .serviceUnavailable.
|
|
public enum DSError: Error, Sendable {
|
|
case unauthorized(message: String?)
|
|
case notFound(message: String?)
|
|
case validation(message: String?, errorCode: String?)
|
|
case serviceUnavailable(message: String?)
|
|
case server(status: Int, message: String?, errorCode: String?)
|
|
case transport(underlying: String)
|
|
case decoding(String)
|
|
|
|
public var isAuthExpired: Bool {
|
|
if case .unauthorized = self { return true }
|
|
return false
|
|
}
|
|
|
|
public static func from(status: Int, data: Data) -> DSError {
|
|
let body = DSErrorBody.parse(data)
|
|
switch status {
|
|
case 401: return .unauthorized(message: body?.message)
|
|
case 404: return .notFound(message: body?.message)
|
|
case 422: return .validation(message: body?.message, errorCode: body?.errorCode)
|
|
case 503: return .serviceUnavailable(message: body?.message)
|
|
default: return .server(status: status, message: body?.message, errorCode: body?.errorCode)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct DSErrorBody {
|
|
let message: String?
|
|
let errorCode: String?
|
|
|
|
static func parse(_ data: Data) -> DSErrorBody? {
|
|
guard let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return nil }
|
|
if let s = obj["detail"] as? String {
|
|
return DSErrorBody(message: s, errorCode: nil)
|
|
}
|
|
if let d = obj["detail"] as? [String: Any] {
|
|
return DSErrorBody(message: d["message"] as? String, errorCode: d["error_code"] as? String)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
extension DSError: LocalizedError {
|
|
public var errorDescription: String? {
|
|
switch self {
|
|
case .unauthorized(let m): return m ?? "인증이 만료되었습니다. 다시 로그인하세요."
|
|
case .notFound(let m): return m ?? "찾을 수 없습니다."
|
|
case .validation(let m, _): return m ?? "요청이 올바르지 않습니다."
|
|
case .serviceUnavailable(let m): return m ?? "서비스를 일시적으로 사용할 수 없습니다."
|
|
case .server(let s, let m, _): return m ?? "서버 오류 (\(s))"
|
|
case .transport(let u): return "네트워크 오류: \(u)"
|
|
case .decoding(let d): return "응답 해석 실패: \(d)"
|
|
}
|
|
}
|
|
}
|