Files
hyungi_document_server/clients/ds-app/Sources/DSKit/DSError.swift
T

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)"
}
}
}