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 , * )
185186public 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