mirror of
https://github.com/divkit/divkit.git
synced 2026-05-07 20:02:32 +00:00
0663870bf9
commit_hash:52ce6e4a9f35a44ddef28565cec76ddaabdc2d08
146 lines
4.3 KiB
Swift
146 lines
4.3 KiB
Swift
import Foundation
|
|
import VGSL
|
|
|
|
@frozen
|
|
public indirect enum DeserializationError: Error, CustomStringConvertible {
|
|
case generic
|
|
case nonUTF8String(string: String)
|
|
case invalidJSONData(data: Data)
|
|
case missingType(representation: [String: Any])
|
|
case unknownType(type: String)
|
|
case invalidFieldRepresentation(field: String, representation: Any?)
|
|
case typeMismatch(expected: String, representation: Any)
|
|
case invalidValue(result: Any?, value: Any?)
|
|
case requiredFieldIsMissing(field: String)
|
|
case nestedObjectError(field: String, error: DeserializationError)
|
|
case noData
|
|
case composite(error: DerivedError, causes: NonEmptyArray<DeserializationError>)
|
|
case unexpectedError(message: String)
|
|
|
|
public enum DerivedError: Error, CustomStringConvertible {
|
|
case invalidValue(result: Any?, from: Any?)
|
|
|
|
public var description: String {
|
|
switch self {
|
|
case let .invalidValue(result, value):
|
|
"Invalid value: '\(dbgStrLimited(result))', from: '\(dbgStrLimited(value))'"
|
|
}
|
|
}
|
|
}
|
|
|
|
public var description: String {
|
|
getDescription(path: [])
|
|
}
|
|
|
|
public var errorMessage: String {
|
|
switch self {
|
|
case .generic:
|
|
"Deserialization error"
|
|
case .nonUTF8String:
|
|
"Non-UTF8 string"
|
|
case .invalidJSONData:
|
|
"Invalid JSON"
|
|
case let .missingType(representation):
|
|
"Missing type: \(representation)"
|
|
case let .unknownType(type):
|
|
"Unknown type: \(type)"
|
|
case let .invalidFieldRepresentation(field, representation):
|
|
"Invalid '\(field)' value: \(dbgStrLimited(representation))"
|
|
case let .typeMismatch(expected, representation):
|
|
"Type mismatch: \(dbgStrLimited(representation)), but '\(expected)' expected"
|
|
case let .invalidValue(result, value):
|
|
"Invalid value: '\(dbgStrLimited(result))', from '\(dbgStrLimited(value))'"
|
|
case let .requiredFieldIsMissing(field):
|
|
"Required field is missing: \(field)"
|
|
case let .nestedObjectError(_, error):
|
|
error.errorMessage
|
|
case .noData:
|
|
"No data"
|
|
case let .composite(error, _):
|
|
"\(error)"
|
|
case let .unexpectedError(message):
|
|
"Unexpected error: \(message)"
|
|
}
|
|
}
|
|
|
|
public var userInfo: [String: String] {
|
|
getUserInfo([])
|
|
}
|
|
|
|
public var rootCauses: [DeserializationError] {
|
|
switch self {
|
|
case let .composite(_, causes):
|
|
causes.flatMap(\.rootCauses)
|
|
case let .nestedObjectError(field, error):
|
|
error.rootCauses.map { .nestedObjectError(field: field, error: $0) }
|
|
default:
|
|
[self]
|
|
}
|
|
}
|
|
|
|
private var subkind: String {
|
|
switch self {
|
|
case .generic: "generic"
|
|
case .invalidFieldRepresentation: "invalidFieldRepresentation"
|
|
case .invalidJSONData: "invalidJSONData"
|
|
case .invalidValue: "invalidValue"
|
|
case .missingType: "missingType"
|
|
case let .nestedObjectError(_, error): error.subkind
|
|
case .noData: "noData"
|
|
case .nonUTF8String: "nonUTF8String"
|
|
case .requiredFieldIsMissing: "requiredFieldIsMissing"
|
|
case .typeMismatch: "typeMismatch"
|
|
case .unexpectedError: "unexpectedError"
|
|
case .unknownType: "unknownType"
|
|
case .composite: "composite"
|
|
}
|
|
}
|
|
|
|
private func getDescription(path: [String]) -> String {
|
|
switch self {
|
|
case let .composite(error, causes):
|
|
"[\(path.joined(separator: "/"))]: \(error)," +
|
|
" caused by " +
|
|
"\(causes.map { $0.getDescription(path: path) }.joined(separator: "; "))"
|
|
case let .nestedObjectError(field, error):
|
|
error.getDescription(path: path + [field])
|
|
default:
|
|
"[\(path.joined(separator: "/"))]: \(errorMessage)"
|
|
}
|
|
}
|
|
|
|
private func getUserInfo(_ path: [String]) -> [String: String] {
|
|
var userInfo: [String: String] = [:]
|
|
|
|
switch self {
|
|
case let .nestedObjectError(field, error):
|
|
return error.getUserInfo(path + [field])
|
|
case .composite:
|
|
userInfo["root_causes"] =
|
|
"\(rootCauses.map { $0.getDescription(path: path) }.joined(separator: "; "))"
|
|
default:
|
|
break
|
|
}
|
|
|
|
return userInfo + [
|
|
"subkind": subkind,
|
|
"path": path.joined(separator: "/"),
|
|
"description": errorMessage,
|
|
]
|
|
}
|
|
}
|
|
|
|
fileprivate func dbgStrLimited(_ val: (some Any)?, limit: UInt = 100) -> String {
|
|
val.map { "\($0)".crop(limit) } ?? "nil"
|
|
}
|
|
|
|
extension String {
|
|
fileprivate func crop(_ limit: UInt) -> String {
|
|
if self.count <= limit {
|
|
self
|
|
} else {
|
|
"\(self.prefix(Int(limit)))..."
|
|
}
|
|
}
|
|
}
|