Skip to content

Commit f8681a2

Browse files
committed
Add structured errors etc.
1 parent df1134f commit f8681a2

File tree

8 files changed

+1546
-1
lines changed

8 files changed

+1546
-1
lines changed

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ let products: [Product] = [
3131
let dependencies: [Package.Dependency] = [
3232
.package(
3333
url: "https://github.com/grpc/grpc-swift.git",
34-
exact: "2.0.0-beta.1"
34+
branch: "main"
3535
),
3636
.package(
3737
url: "https://github.com/apple/swift-protobuf.git",
@@ -73,6 +73,7 @@ let targets: [Target] = [
7373
dependencies: [
7474
.target(name: "GRPCProtobuf"),
7575
.product(name: "GRPCCore", package: "grpc-swift"),
76+
.product(name: "GRPCInProcessTransport", package: "grpc-swift"),
7677
.product(name: "SwiftProtobuf", package: "swift-protobuf"),
7778
],
7879
swiftSettings: defaultSwiftSettings
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright 2024, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
internal import SwiftProtobuf
18+
19+
/// A type which can be packed and unpacked from a `Google_Protobuf_Any` message.
20+
internal protocol GoogleProtobufAnyPackable {
21+
static var typeURL: String { get }
22+
23+
/// Pack the value into a `Google_Protobuf_Any`, if possible.
24+
func pack() throws -> Google_Protobuf_Any
25+
26+
/// Unpack the value from a `Google_Protobuf_Any`, if possible.
27+
init?(unpacking any: Google_Protobuf_Any) throws
28+
}
29+
30+
/// A type which is backed by a Protobuf message.
31+
///
32+
/// This is a convenience protocol to allow for automatic packing/unpacking of messages
33+
/// where possible.
34+
internal protocol ProtobufBacked {
35+
associatedtype Message: SwiftProtobuf.Message
36+
var storage: Message { get set }
37+
init(storage: Message)
38+
}
39+
40+
extension GoogleProtobufAnyPackable where Self: ProtobufBacked {
41+
func pack() throws -> Google_Protobuf_Any {
42+
try .with {
43+
$0.typeURL = Self.typeURL
44+
$0.value = try self.storage.serializedBytes()
45+
}
46+
}
47+
48+
init?(unpacking any: Google_Protobuf_Any) throws {
49+
guard let storage = try any.unpack(Message.self) else { return nil }
50+
self.init(storage: storage)
51+
}
52+
}
53+
54+
extension Google_Protobuf_Any {
55+
func unpack<Unpacked: Message>(_ as: Unpacked.Type) throws -> Unpacked? {
56+
if self.isA(Unpacked.self) {
57+
return try Unpacked(serializedBytes: self.value)
58+
} else {
59+
return nil
60+
}
61+
}
62+
}
63+
64+
extension ErrorDetails {
65+
// Note: this type isn't packable into an 'Any' protobuf so doesn't conform
66+
// to 'GoogleProtobufAnyPackable' despite holding types which are packable.
67+
68+
func pack() throws -> Google_Protobuf_Any {
69+
switch self.wrapped {
70+
case .errorInfo(let info):
71+
return try info.pack()
72+
case .retryInfo(let info):
73+
return try info.pack()
74+
case .debugInfo(let info):
75+
return try info.pack()
76+
case .quotaFailure(let info):
77+
return try info.pack()
78+
case .preconditionFailure(let info):
79+
return try info.pack()
80+
case .badRequest(let info):
81+
return try info.pack()
82+
case .requestInfo(let info):
83+
return try info.pack()
84+
case .resourceInfo(let info):
85+
return try info.pack()
86+
case .help(let info):
87+
return try info.pack()
88+
case .localizedMessage(let info):
89+
return try info.pack()
90+
case .any(let any):
91+
return any
92+
}
93+
}
94+
95+
init(unpacking any: Google_Protobuf_Any) throws {
96+
if let unpacked = try Self.unpack(any: any) {
97+
self = unpacked
98+
} else {
99+
self = .any(any)
100+
}
101+
}
102+
103+
private static func unpack(any: Google_Protobuf_Any) throws -> Self? {
104+
switch any.typeURL {
105+
case ErrorInfo.typeURL:
106+
if let unpacked = try ErrorInfo(unpacking: any) {
107+
return .errorInfo(unpacked)
108+
}
109+
case RetryInfo.typeURL:
110+
if let unpacked = try RetryInfo(unpacking: any) {
111+
return .retryInfo(unpacked)
112+
}
113+
case DebugInfo.typeURL:
114+
if let unpacked = try DebugInfo(unpacking: any) {
115+
return .debugInfo(unpacked)
116+
}
117+
case QuotaFailure.typeURL:
118+
if let unpacked = try QuotaFailure(unpacking: any) {
119+
return .quotaFailure(unpacked)
120+
}
121+
case PreconditionFailure.typeURL:
122+
if let unpacked = try PreconditionFailure(unpacking: any) {
123+
return .preconditionFailure(unpacked)
124+
}
125+
case BadRequest.typeURL:
126+
if let unpacked = try BadRequest(unpacking: any) {
127+
return .badRequest(unpacked)
128+
}
129+
case RequestInfo.typeURL:
130+
if let unpacked = try RequestInfo(unpacking: any) {
131+
return .requestInfo(unpacked)
132+
}
133+
case ResourceInfo.typeURL:
134+
if let unpacked = try ResourceInfo(unpacking: any) {
135+
return .resourceInfo(unpacked)
136+
}
137+
case Help.typeURL:
138+
if let unpacked = try Help(unpacking: any) {
139+
return .help(unpacked)
140+
}
141+
case LocalizedMessage.typeURL:
142+
if let unpacked = try LocalizedMessage(unpacking: any) {
143+
return .localizedMessage(unpacked)
144+
}
145+
default:
146+
return .any(any)
147+
}
148+
149+
return nil
150+
}
151+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2024, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
extension ErrorDetails: CustomStringConvertible {
18+
public var description: String {
19+
switch self.wrapped {
20+
case .errorInfo(let info):
21+
return String(describing: info)
22+
case .retryInfo(let info):
23+
return String(describing: info)
24+
case .debugInfo(let info):
25+
return String(describing: info)
26+
case .quotaFailure(let info):
27+
return String(describing: info)
28+
case .preconditionFailure(let info):
29+
return String(describing: info)
30+
case .badRequest(let info):
31+
return String(describing: info)
32+
case .requestInfo(let info):
33+
return String(describing: info)
34+
case .resourceInfo(let info):
35+
return String(describing: info)
36+
case .help(let info):
37+
return String(describing: info)
38+
case .localizedMessage(let info):
39+
return String(describing: info)
40+
case .any(let any):
41+
return String(describing: any)
42+
}
43+
}
44+
}
45+
46+
// Some errors use protobuf messages as their storage so the default description isn't
47+
// representative
48+
49+
extension ErrorDetails.ErrorInfo: CustomStringConvertible {
50+
public var description: String {
51+
"\(Self.self)(reason: \"\(self.reason)\", domain: \"\(self.domain)\", metadata: \(self.metadata))"
52+
}
53+
}
54+
55+
extension ErrorDetails.DebugInfo: CustomStringConvertible {
56+
public var description: String {
57+
"\(Self.self)(stack: \(self.stack), detail: \"\(self.detail)\")"
58+
}
59+
}
60+
61+
extension ErrorDetails.QuotaFailure.Violation: CustomStringConvertible {
62+
public var description: String {
63+
"\(Self.self)(subject: \"\(self.subject)\", violationDescription: \"\(self.violationDescription)\")"
64+
}
65+
}
66+
67+
extension ErrorDetails.PreconditionFailure.Violation: CustomStringConvertible {
68+
public var description: String {
69+
"\(Self.self)(subject: \"\(self.subject)\", type: \"\(self.type)\", violationDescription: \"\(self.violationDescription)\")"
70+
}
71+
}
72+
73+
extension ErrorDetails.BadRequest.FieldViolation: CustomStringConvertible {
74+
public var description: String {
75+
"\(Self.self)(field: \"\(self.field)\", violationDescription: \"\(self.violationDescription)\")"
76+
}
77+
}
78+
79+
extension ErrorDetails.RequestInfo: CustomStringConvertible {
80+
public var description: String {
81+
"\(Self.self)(requestID: \"\(self.requestID)\", servingData: \"\(self.servingData)\")"
82+
}
83+
}
84+
85+
extension ErrorDetails.ResourceInfo: CustomStringConvertible {
86+
public var description: String {
87+
"\(Self.self)(name: \"\(self.name)\", owner: \"\(self.owner)\", type: \"\(self.type)\", errorDescription: \"\(self.errorDescription)\")"
88+
}
89+
}
90+
91+
extension ErrorDetails.Help.Link: CustomStringConvertible {
92+
public var description: String {
93+
"\(Self.self)(url: \"\(self.url)\", linkDescription: \"\(self.linkDescription)\")"
94+
}
95+
}
96+
97+
extension ErrorDetails.LocalizedMessage: CustomStringConvertible {
98+
public var description: String {
99+
"\(Self.self)(locale: \"\(self.locale)\", message: \"\(self.message)\")"
100+
}
101+
}

0 commit comments

Comments
 (0)