Skip to content

Commit 5cafb19

Browse files
authored
Implement TODOs and add tests (#15535)
1 parent a219b40 commit 5cafb19

File tree

2 files changed

+409
-57
lines changed

2 files changed

+409
-57
lines changed

FirebaseAI/Sources/Types/Public/Generable/ModelOutput.swift

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
///
1717
/// Model output may contain a single value, an array, or key-value pairs with unique keys.
1818
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
19-
public struct ModelOutput: Sendable, Generable {
19+
public struct ModelOutput: Sendable, Generable, CustomDebugStringConvertible {
2020
/// The kind representation of this model output.
2121
///
2222
/// This property provides access to the content in a strongly-typed enum representation,
@@ -70,6 +70,10 @@ public struct ModelOutput: Sendable, Generable {
7070
/// A representation of this instance.
7171
public var modelOutput: ModelOutput { self }
7272

73+
public var debugDescription: String {
74+
return kind.debugDescription
75+
}
76+
7377
/// Creates model output representing a structure with the properties you specify.
7478
///
7579
/// The order of properties is important. For ``Generable`` types, the order must match the order
@@ -146,20 +150,18 @@ public struct ModelOutput: Sendable, Generable {
146150
/// Reads a top level, concrete partially `Generable` type from a named property.
147151
public func value<Value>(_ type: Value.Type = Value.self) throws -> Value
148152
where Value: ConvertibleFromModelOutput {
149-
fatalError("`ModelOutput.value(_:)` is not implemented.")
153+
return try Value(self)
150154
}
151155

152156
/// Reads a concrete `Generable` type from named property.
153157
public func value<Value>(_ type: Value.Type = Value.self,
154158
forProperty property: String) throws -> Value
155159
where Value: ConvertibleFromModelOutput {
156160
guard case let .structure(properties, _) = kind else {
157-
// TODO: Throw an error instead
158-
fatalError("Attempting to access a property on a non-object ModelOutput.")
161+
throw DecodingError.notAStructure
159162
}
160163
guard let value = properties[property] else {
161-
// TODO: Throw an error instead
162-
fatalError("Property '\(property)' not found in model output.")
164+
throw DecodingError.missingProperty(name: property)
163165
}
164166

165167
return try Value(value)
@@ -170,8 +172,7 @@ public struct ModelOutput: Sendable, Generable {
170172
forProperty property: String) throws -> Value?
171173
where Value: ConvertibleFromModelOutput {
172174
guard case let .structure(properties, _) = kind else {
173-
// TODO: Throw an error instead
174-
fatalError("Attempting to access a property on a non-object ModelOutput.")
175+
throw DecodingError.notAStructure
175176
}
176177
guard let value = properties[property] else {
177178
return nil
@@ -183,11 +184,35 @@ public struct ModelOutput: Sendable, Generable {
183184

184185
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
185186
public extension ModelOutput {
187+
/// An error that occurs when decoding a value from `ModelOutput`.
188+
enum DecodingError: Error, CustomDebugStringConvertible {
189+
/// A required property was not found in the `ModelOutput`.
190+
case missingProperty(name: String)
191+
192+
/// A property was accessed on a `ModelOutput` that is not a structure.
193+
case notAStructure
194+
195+
/// The context for a decoding error.
196+
public struct Context: Sendable {
197+
/// A description of the error.
198+
public let debugDescription: String
199+
}
200+
201+
public var debugDescription: String {
202+
switch self {
203+
case let .missingProperty(name):
204+
return "Missing property: \(name)"
205+
case .notAStructure:
206+
return "Not a structure"
207+
}
208+
}
209+
}
210+
186211
/// A representation of the different types of content that can be stored in `ModelOutput`.
187212
///
188213
/// `Kind` represents the various types of JSON-compatible data that can be held within a
189214
/// ``ModelOutput`` instance, including primitive types, arrays, and structured objects.
190-
enum Kind: Sendable {
215+
enum Kind: Sendable, CustomDebugStringConvertible {
191216
/// Represents a null value.
192217
case null
193218

@@ -212,5 +237,27 @@ public extension ModelOutput {
212237
/// - properties: A dictionary mapping string keys to ``ModelOutput`` values.
213238
/// - orderedKeys: An array of keys that specifies the order of properties.
214239
case structure(properties: [String: ModelOutput], orderedKeys: [String])
240+
241+
public var debugDescription: String {
242+
switch self {
243+
case .null:
244+
return "null"
245+
case let .bool(value):
246+
return String(describing: value)
247+
case let .number(value):
248+
return String(describing: value)
249+
case let .string(value):
250+
return #""\#(value)""#
251+
case let .array(elements):
252+
let descriptions = elements.map { $0.debugDescription }
253+
return "[\(descriptions.joined(separator: ", "))]"
254+
case let .structure(properties, orderedKeys):
255+
let descriptions = orderedKeys.compactMap { key -> String? in
256+
guard let value = properties[key] else { return nil }
257+
return #""\#(key)": \#(value.debugDescription)"#
258+
}
259+
return "{\(descriptions.joined(separator: ", "))}"
260+
}
261+
}
215262
}
216263
}

0 commit comments

Comments
 (0)