| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- // Sources/SwiftProtobuf/SwiftProtobufError.swift
- //
- // Copyright (c) 2024 Apple Inc. and the project authors
- // Licensed under Apache License v2.0 with Runtime Library Exception
- //
- // See LICENSE.txt for license information:
- // https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
- //
- // -----------------------------------------------------------------------------
- /// A SwiftProtobuf specific error.
- ///
- /// All errors have a high-level ``SwiftProtobufError/Code-swift.struct`` which identifies the domain
- /// of the error. For example, an issue when encoding a proto into binary data will result in a
- /// ``SwiftProtobufError/Code-swift.struct/binaryEncodingError`` error code.
- /// Errors also include a message describing what went wrong and how to remedy it (if applicable). The
- /// ``SwiftProtobufError/message`` is not static and may include dynamic information such as the
- /// type URL for a type that could not be decoded, for example.
- public struct SwiftProtobufError: Error, @unchecked Sendable {
- // Note: @unchecked because we use a backing class for storage.
- private var storage: Storage
- private mutating func ensureStorageIsUnique() {
- if !isKnownUniquelyReferenced(&self.storage) {
- self.storage = self.storage.copy()
- }
- }
- private final class Storage {
- var code: Code
- var message: String
- var location: SourceLocation
- init(
- code: Code,
- message: String,
- location: SourceLocation
- ) {
- self.code = code
- self.message = message
- self.location = location
- }
- func copy() -> Self {
- Self(
- code: self.code,
- message: self.message,
- location: self.location
- )
- }
- }
- /// A high-level error code to provide broad a classification.
- public var code: Code {
- get { self.storage.code }
- set {
- self.ensureStorageIsUnique()
- self.storage.code = newValue
- }
- }
- /// A message describing what went wrong and how it may be remedied.
- package var message: String {
- get { self.storage.message }
- set {
- self.ensureStorageIsUnique()
- self.storage.message = newValue
- }
- }
- private var location: SourceLocation {
- get { self.storage.location }
- set {
- self.ensureStorageIsUnique()
- self.storage.location = newValue
- }
- }
- public init(
- code: Code,
- message: String,
- location: SourceLocation
- ) {
- self.storage = Storage(code: code, message: message, location: location)
- }
- }
- extension SwiftProtobufError {
- /// A high level indication of the kind of error being thrown.
- public struct Code: Hashable, Sendable, CustomStringConvertible {
- private enum Wrapped: Hashable, Sendable, CustomStringConvertible {
- case binaryDecodingError
- case binaryStreamDecodingError
- case jsonDecodingError
- case jsonEncodingError
- var description: String {
- switch self {
- case .binaryDecodingError:
- return "Binary decoding error"
- case .binaryStreamDecodingError:
- return "Stream decoding error"
- case .jsonDecodingError:
- return "JSON decoding error"
- case .jsonEncodingError:
- return "JSON encoding error"
- }
- }
- }
- /// This Code's description.
- public var description: String {
- String(describing: self.code)
- }
- private var code: Wrapped
- private init(_ code: Wrapped) {
- self.code = code
- }
- /// Errors arising from binary decoding of data into protobufs.
- public static var binaryDecodingError: Self {
- Self(.binaryDecodingError)
- }
- /// Errors arising from decoding streams of binary messages. These errors have to do with the framing
- /// of the messages in the stream, or the stream as a whole.
- public static var binaryStreamDecodingError: Self {
- Self(.binaryStreamDecodingError)
- }
- /// Errors arising from JSON decoding of data into protobufs.
- public static var jsonDecodingError: Self {
- Self(.jsonDecodingError)
- }
- /// Errors arising from JSON encoding of messages.
- public static var jsonEncodingError: Self {
- Self(.jsonEncodingError)
- }
- }
- /// A location within source code.
- public struct SourceLocation: Sendable, Hashable {
- /// The function in which the error was thrown.
- public var function: String
- /// The file in which the error was thrown.
- public var file: String
- /// The line on which the error was thrown.
- public var line: Int
- public init(function: String, file: String, line: Int) {
- self.function = function
- self.file = file
- self.line = line
- }
- @usableFromInline
- internal static func here(
- function: String = #function,
- file: String = #fileID,
- line: Int = #line
- ) -> Self {
- SourceLocation(function: function, file: file, line: line)
- }
- }
- }
- extension SwiftProtobufError: CustomStringConvertible {
- public var description: String {
- "\(self.code) (at \(self.location)): \(self.message)"
- }
- }
- extension SwiftProtobufError: CustomDebugStringConvertible {
- public var debugDescription: String {
- "\(String(reflecting: self.code)) (at \(String(reflecting: self.location))): \(String(reflecting: self.message))"
- }
- }
- // - MARK: Common errors
- extension SwiftProtobufError {
- /// Errors arising from binary decoding of data into protobufs.
- public enum BinaryDecoding {
- /// Message is too large. Bytes and Strings have a max size of 2GB.
- public static func tooLarge(
- function: String = #function,
- file: String = #fileID,
- line: Int = #line
- ) -> SwiftProtobufError {
- SwiftProtobufError(
- code: .binaryDecodingError,
- message: "Message too large: Bytes and Strings have a max size of 2GB.",
- location: SourceLocation(function: function, file: file, line: line)
- )
- }
- }
- /// Errors arising from decoding streams of binary messages. These errors have to do with the framing
- /// of the messages in the stream, or the stream as a whole.
- public enum BinaryStreamDecoding {
- /// Message is too large. Bytes and Strings have a max size of 2GB.
- public static func tooLarge(
- function: String = #function,
- file: String = #fileID,
- line: Int = #line
- ) -> SwiftProtobufError {
- SwiftProtobufError(
- code: .binaryStreamDecodingError,
- message: "Message too large: Bytes and Strings have a max size of 2GB.",
- location: SourceLocation(function: function, file: file, line: line)
- )
- }
- /// While attempting to read the length of a message on the stream, the
- /// bytes were malformed for the protobuf format.
- public static func malformedLength(
- function: String = #function,
- file: String = #fileID,
- line: Int = #line
- ) -> SwiftProtobufError {
- SwiftProtobufError(
- code: .binaryStreamDecodingError,
- message: """
- While attempting to read the length of a binary-delimited message \
- on the stream, the bytes were malformed for the protobuf format.
- """,
- location: .init(function: function, file: file, line: line)
- )
- }
- /// This isn't really an error. `InputStream` documents that
- /// `hasBytesAvailable` _may_ return `True` if a read is needed to
- /// determine if there really are bytes available. So this "error" is thrown
- /// when a `parse` or `merge` fails because there were no bytes available.
- /// If this is raised, the callers should decide via what ever other means
- /// are correct if the stream has completely ended or if more bytes might
- /// eventually show up.
- public static func noBytesAvailable(
- function: String = #function,
- file: String = #fileID,
- line: Int = #line
- ) -> SwiftProtobufError {
- SwiftProtobufError(
- code: .binaryStreamDecodingError,
- message: """
- This is not really an error: please read the documentation for
- `SwiftProtobufError/BinaryStreamDecoding/noBytesAvailable` for more information.
- """,
- location: .init(function: function, file: file, line: line)
- )
- }
- }
- /// Errors arising from JSON decoding of data into protobufs.
- public enum JSONDecoding {
- /// While decoding a `google.protobuf.Any` encountered a malformed `@type` key for
- /// the `type_url` field.
- public static func invalidAnyTypeURL(
- type_url: String,
- function: String = #function,
- file: String = #fileID,
- line: Int = #line
- ) -> SwiftProtobufError {
- SwiftProtobufError(
- code: .jsonDecodingError,
- message: "google.protobuf.Any '@type' was invalid: \(type_url).",
- location: SourceLocation(function: function, file: file, line: line)
- )
- }
- /// While decoding a `google.protobuf.Any` no `@type` field but the message had other fields.
- public static func emptyAnyTypeURL(
- function: String = #function,
- file: String = #fileID,
- line: Int = #line
- ) -> SwiftProtobufError {
- SwiftProtobufError(
- code: .jsonDecodingError,
- message: "google.protobuf.Any '@type' was must be present if if the object is not empty.",
- location: SourceLocation(function: function, file: file, line: line)
- )
- }
- }
- /// Errors arising from JSON encoding of messages.
- public enum JSONEncoding {
- /// While encoding a `google.protobuf.Any` encountered a malformed `type_url` field.
- public static func invalidAnyTypeURL(
- type_url: String,
- function: String = #function,
- file: String = #fileID,
- line: Int = #line
- ) -> SwiftProtobufError {
- SwiftProtobufError(
- code: .jsonEncodingError,
- message: "google.protobuf.Any 'type_url' was invalid: \(type_url).",
- location: SourceLocation(function: function, file: file, line: line)
- )
- }
- /// While encoding a `google.protobuf.Any` encountered an empty `type_url` field.
- public static func emptyAnyTypeURL(
- function: String = #function,
- file: String = #fileID,
- line: Int = #line
- ) -> SwiftProtobufError {
- SwiftProtobufError(
- code: .jsonEncodingError,
- message: "google.protobuf.Any 'type_url' was empty, only allowed for empty objects.",
- location: SourceLocation(function: function, file: file, line: line)
- )
- }
- }
- }
|