RadrootsBackgroundTransferResponse.swift (4369B)
1 import Foundation 2 3 public struct RadrootsBackgroundTransferResponsePolicy: Sendable, Equatable, Hashable, Codable { 4 public let maximumBodyBytes: Int 5 public let acceptedMediaTypes: [String] 6 7 public init(maximumBodyBytes: Int = 0, acceptedMediaTypes: [String] = []) throws { 8 guard (0 ... 65536).contains(maximumBodyBytes), acceptedMediaTypes.count <= 8 else { 9 throw RadrootsBackgroundTransferError.invalidRequest 10 } 11 let normalized = try acceptedMediaTypes.map { 12 try RadrootsBackgroundTransferValidation.normalizedMediaType($0) 13 } 14 guard Set(normalized).count == normalized.count, (maximumBodyBytes == 0) == normalized.isEmpty 15 else { 16 throw RadrootsBackgroundTransferError.invalidRequest 17 } 18 self.maximumBodyBytes = maximumBodyBytes 19 self.acceptedMediaTypes = normalized 20 } 21 22 public static let discard = Self(maximumBodyBytes: 0, acceptedMediaTypes: [], validated: ()) 23 24 public static func boundedJSON(maximumBodyBytes: Int = 16384) throws -> Self { 25 try Self(maximumBodyBytes: maximumBodyBytes, acceptedMediaTypes: ["application/json"]) 26 } 27 28 private init(maximumBodyBytes: Int, acceptedMediaTypes: [String], validated _: Void) { 29 self.maximumBodyBytes = maximumBodyBytes 30 self.acceptedMediaTypes = acceptedMediaTypes 31 } 32 33 private enum CodingKeys: String, CodingKey { 34 case maximumBodyBytes 35 case acceptedMediaTypes 36 } 37 38 public init(from decoder: any Decoder) throws { 39 let values = try decoder.container(keyedBy: CodingKeys.self) 40 try self.init( 41 maximumBodyBytes: values.decode(Int.self, forKey: .maximumBodyBytes), 42 acceptedMediaTypes: values.decode([String].self, forKey: .acceptedMediaTypes) 43 ) 44 } 45 46 public func encode(to encoder: any Encoder) throws { 47 var values = encoder.container(keyedBy: CodingKeys.self) 48 try values.encode(maximumBodyBytes, forKey: .maximumBodyBytes) 49 try values.encode(acceptedMediaTypes, forKey: .acceptedMediaTypes) 50 } 51 } 52 53 public struct RadrootsBackgroundTransferResponse: Sendable, Equatable, Hashable, Codable, 54 CustomDebugStringConvertible { 55 public let statusCode: Int 56 public let mediaType: String? 57 public let contentEncoding: String? 58 public let body: Data? 59 60 public var debugDescription: String { 61 "RadrootsBackgroundTransferResponse(statusCode: \(statusCode), " 62 + "bodyBytes: \(body?.count ?? 0), content: <redacted>)" 63 } 64 65 public init( 66 statusCode: Int, mediaType: String?, contentEncoding: String? = nil, body: Data? 67 ) throws { 68 guard (100 ... 599).contains(statusCode), body?.count ?? 0 <= 65536 else { 69 throw RadrootsBackgroundTransferError.invalidRequest 70 } 71 self.statusCode = statusCode 72 self.mediaType = try mediaType.map { 73 try RadrootsBackgroundTransferValidation.normalizedMediaType($0) 74 } 75 let normalizedEncoding = contentEncoding?.trimmingCharacters(in: .whitespacesAndNewlines) 76 .lowercased() 77 guard normalizedEncoding == nil || normalizedEncoding == "identity" else { 78 throw RadrootsBackgroundTransferError.invalidRequest 79 } 80 self.contentEncoding = normalizedEncoding 81 self.body = body 82 } 83 84 private enum CodingKeys: String, CodingKey { 85 case statusCode 86 case mediaType 87 case contentEncoding 88 case body 89 } 90 91 public init(from decoder: any Decoder) throws { 92 let values = try decoder.container(keyedBy: CodingKeys.self) 93 try self.init( 94 statusCode: values.decode(Int.self, forKey: .statusCode), 95 mediaType: values.decodeIfPresent(String.self, forKey: .mediaType), 96 contentEncoding: values.decodeIfPresent(String.self, forKey: .contentEncoding), 97 body: values.decodeIfPresent(Data.self, forKey: .body) 98 ) 99 } 100 101 public func encode(to encoder: any Encoder) throws { 102 var values = encoder.container(keyedBy: CodingKeys.self) 103 try values.encode(statusCode, forKey: .statusCode) 104 try values.encodeIfPresent(mediaType, forKey: .mediaType) 105 try values.encodeIfPresent(contentEncoding, forKey: .contentEncoding) 106 try values.encodeIfPresent(body, forKey: .body) 107 } 108 }