@@ -33,81 +33,136 @@ public struct MetaRelationship<MetaType: JSONAPI.Meta, LinksType: JSONAPI.Links>
3333
3434/// A `ResourceObject` relationship that can be encoded to or decoded from
3535/// a JSON API "Resource Linkage."
36+ ///
3637/// See https://jsonapi.org/format/#document-resource-object-linkage
38+ ///
3739/// A convenient typealias might make your code much more legible: `One<ResourceObjectDescription>`
38- public struct ToOneRelationship < Identifiable: JSONAPI . JSONAPIIdentifiable , MetaType: JSONAPI . Meta , LinksType: JSONAPI . Links > : RelationshipType , Equatable {
40+ ///
41+ /// The `IdMetaType` (if not `NoIdMetadata`) will be parsed out of the Resource Identifier Object.
42+ /// (see https://jsonapi.org/format/#document-resource-identifier-objects)
43+ ///
44+ /// The `MetaType` (if not `NoMetadata`) will be parsed out of the Relationship Object.
45+ /// (see https://jsonapi.org/format/#document-resource-object-relationships)
46+ public struct ToOneRelationship < Identifiable: JSONAPI . JSONAPIIdentifiable , IdMetaType: JSONAPI . Meta , MetaType: JSONAPI . Meta , LinksType: JSONAPI . Links > : RelationshipType , Equatable {
3947
4048 public let id : Identifiable . ID
4149
50+ public let idMeta : IdMetaType
51+
4252 public let meta : MetaType
4353 public let links : LinksType
4454
45- public init ( id: Identifiable . ID , meta: MetaType , links: LinksType ) {
55+ public init ( id: Identifiable . ID , meta: MetaType , links: LinksType ) where IdMetaType == NoIdMetadata {
4656 self . id = id
57+ self . idMeta = . none
58+ self . meta = meta
59+ self . links = links
60+ }
61+
62+ public init ( id: ( Identifiable . ID , IdMetaType ) , meta: MetaType , links: LinksType ) {
63+ self . id = id. 0
64+ self . idMeta = id. 1
4765 self . meta = meta
4866 self . links = links
4967 }
5068}
5169
5270extension ToOneRelationship where MetaType == NoMetadata , LinksType == NoLinks {
53- public init ( id: Identifiable . ID ) {
71+ public init ( id: Identifiable . ID ) where IdMetaType == NoIdMetadata {
72+ self . init ( id: id, meta: . none, links: . none)
73+ }
74+
75+ public init ( id: ( Identifiable . ID , IdMetaType ) ) {
5476 self . init ( id: id, meta: . none, links: . none)
5577 }
5678}
5779
5880extension ToOneRelationship {
59- public init < T: ResourceObjectType > ( resourceObject: T , meta: MetaType , links: LinksType ) where T. Id == Identifiable . ID {
81+ public init < T: ResourceObjectType > ( resourceObject: T , meta: MetaType , links: LinksType ) where T. Id == Identifiable . ID , IdMetaType == NoIdMetadata {
6082 self . init ( id: resourceObject. id, meta: meta, links: links)
6183 }
6284}
6385
64- extension ToOneRelationship where MetaType == NoMetadata , LinksType == NoLinks {
86+ extension ToOneRelationship where MetaType == NoMetadata , LinksType == NoLinks , IdMetaType == NoIdMetadata {
6587 public init < T: ResourceObjectType > ( resourceObject: T ) where T. Id == Identifiable . ID {
6688 self . init ( id: resourceObject. id, meta: . none, links: . none)
6789 }
6890}
6991
70- extension ToOneRelationship where Identifiable: OptionalRelatable {
92+ extension ToOneRelationship where Identifiable: OptionalRelatable , IdMetaType == NoIdMetadata {
7193 public init < T: ResourceObjectType > ( resourceObject: T ? , meta: MetaType , links: LinksType ) where T. Id == Identifiable . Wrapped . ID {
7294 self . init ( id: resourceObject? . id, meta: meta, links: links)
7395 }
7496}
7597
76- extension ToOneRelationship where Identifiable: OptionalRelatable , MetaType == NoMetadata , LinksType == NoLinks {
98+ extension ToOneRelationship where Identifiable: OptionalRelatable , MetaType == NoMetadata , LinksType == NoLinks , IdMetaType == NoIdMetadata {
7799 public init < T: ResourceObjectType > ( resourceObject: T ? ) where T. Id == Identifiable . Wrapped . ID {
78100 self . init ( id: resourceObject? . id, meta: . none, links: . none)
79101 }
80102}
81103
82104/// An ResourceObject relationship that can be encoded to or decoded from
83105/// a JSON API "Resource Linkage."
106+ ///
84107/// See https://jsonapi.org/format/#document-resource-object-linkage
108+ ///
85109/// A convenient typealias might make your code much more legible: `Many<ResourceObjectDescription>`
86- public struct ToManyRelationship < Relatable: JSONAPI . Relatable , MetaType: JSONAPI . Meta , LinksType: JSONAPI . Links > : RelationshipType , Equatable {
110+ ///
111+ /// The `IdMetaType` (if not `NoIdMetadata`) will be parsed out of the Resource Identifier Object.
112+ /// (see https://jsonapi.org/format/#document-resource-identifier-objects)
113+ ///
114+ /// The `MetaType` (if not `NoMetadata`) will be parsed out of the Relationship Object.
115+ /// (see https://jsonapi.org/format/#document-resource-object-relationships)
116+ public struct ToManyRelationship < Relatable: JSONAPI . Relatable , IdMetaType: JSONAPI . Meta , MetaType: JSONAPI . Meta , LinksType: JSONAPI . Links > : RelationshipType , Equatable {
117+
118+ public struct ID : Equatable {
119+ public let id : Relatable . ID
120+ public let meta : IdMetaType
87121
88- public let ids : [ Relatable . ID ]
122+ public init ( id: Relatable . ID , meta: IdMetaType ) {
123+ self . id = id
124+ self . meta = meta
125+ }
126+
127+ internal init ( _ idPair: ( Relatable . ID , IdMetaType ) ) {
128+ self . init ( id: idPair. 0 , meta: idPair. 1 )
129+ }
130+ }
131+
132+ public let metaIds : [ ID ]
133+
134+ public var ids : [ Relatable . ID ] {
135+ metaIds. map ( \. id)
136+ }
89137
90138 public let meta : MetaType
91139 public let links : LinksType
92140
93- public init ( ids: [ Relatable . ID ] , meta: MetaType , links: LinksType ) {
94- self . ids = ids
141+
142+ public init ( ids: [ Relatable . ID ] , meta: MetaType , links: LinksType ) where IdMetaType == NoIdMetadata {
143+ self . metaIds = ids. map { . init( id: $0, meta: . none) }
144+ self . meta = meta
145+ self . links = links
146+ }
147+
148+ public init ( idsWithMetadata ids: [ ( Relatable . ID , IdMetaType ) ] , meta: MetaType , links: LinksType ) {
149+ self . metaIds = ids. map ( ID . init)
95150 self . meta = meta
96151 self . links = links
97152 }
98153
99- public init < T: JSONAPIIdentifiable > ( pointers: [ ToOneRelationship < T , NoMetadata , NoLinks > ] , meta: MetaType , links: LinksType ) where T. ID == Relatable . ID {
100- ids = pointers. map ( \ . id)
154+ public init < T: JSONAPIIdentifiable > ( pointers: [ ToOneRelationship < T , NoIdMetadata , NoMetadata , NoLinks > ] , meta: MetaType , links: LinksType ) where T. ID == Relatable . ID , IdMetaType == NoIdMetadata {
155+ metaIds = pointers. map { . init ( id : $0 . id, meta : . none ) }
101156 self . meta = meta
102157 self . links = links
103158 }
104159
105- public init < T: ResourceObjectType > ( resourceObjects: [ T ] , meta: MetaType , links: LinksType ) where T. Id == Relatable . ID {
160+ public init < T: ResourceObjectType > ( resourceObjects: [ T ] , meta: MetaType , links: LinksType ) where T. Id == Relatable . ID , IdMetaType == NoIdMetadata {
106161 self . init ( ids: resourceObjects. map ( \. id) , meta: meta, links: links)
107162 }
108163
109164 private init ( meta: MetaType , links: LinksType ) {
110- self . init ( ids : [ ] , meta: meta, links: links)
165+ self . init ( idsWithMetadata : [ ] , meta: meta, links: links)
111166 }
112167
113168 public static func none( withMeta meta: MetaType , links: LinksType ) -> ToManyRelationship {
@@ -117,19 +172,23 @@ public struct ToManyRelationship<Relatable: JSONAPI.Relatable, MetaType: JSONAPI
117172
118173extension ToManyRelationship where MetaType == NoMetadata , LinksType == NoLinks {
119174
120- public init ( ids: [ Relatable . ID ] ) {
175+ public init ( ids: [ Relatable . ID ] ) where IdMetaType == NoIdMetadata {
121176 self . init ( ids: ids, meta: . none, links: . none)
122177 }
123178
124- public init < T: JSONAPIIdentifiable > ( pointers: [ ToOneRelationship < T , NoMetadata , NoLinks > ] ) where T. ID == Relatable . ID {
179+ public init ( idsWithMetadata ids: [ ( Relatable . ID , IdMetaType ) ] ) {
180+ self . init ( idsWithMetadata: ids, meta: . none, links: . none)
181+ }
182+
183+ public init < T: JSONAPIIdentifiable > ( pointers: [ ToOneRelationship < T , NoIdMetadata , NoMetadata , NoLinks > ] ) where T. ID == Relatable . ID , IdMetaType == NoIdMetadata {
125184 self . init ( pointers: pointers, meta: . none, links: . none)
126185 }
127186
128187 public static var none : ToManyRelationship {
129188 return . none( withMeta: . none, links: . none)
130189 }
131190
132- public init < T: ResourceObjectType > ( resourceObjects: [ T ] ) where T. Id == Relatable . ID {
191+ public init < T: ResourceObjectType > ( resourceObjects: [ T ] ) where T. Id == Relatable . ID , IdMetaType == NoIdMetadata {
133192 self . init ( resourceObjects: resourceObjects, meta: . none, links: . none)
134193 }
135194}
@@ -164,6 +223,7 @@ private enum ResourceLinkageCodingKeys: String, CodingKey {
164223private enum ResourceIdentifierCodingKeys : String , CodingKey {
165224 case id = " id "
166225 case entityType = " type "
226+ case metadata = " meta "
167227}
168228
169229extension MetaRelationship : Codable {
@@ -228,6 +288,16 @@ extension ToOneRelationship: Codable where Identifiable.ID: OptionalId {
228288 )
229289 )
230290 }
291+ guard let noIdMeta = NoIdMetadata ( ) as? IdMetaType else {
292+ throw DecodingError . valueNotFound (
293+ Self . self,
294+ DecodingError . Context (
295+ codingPath: decoder. codingPath,
296+ debugDescription: " Expected non-null relationship data with metadata inside. "
297+ )
298+ )
299+ }
300+ idMeta = noIdMeta
231301 id = val
232302 return
233303 }
@@ -256,6 +326,15 @@ extension ToOneRelationship: Codable where Identifiable.ID: OptionalId {
256326 )
257327 }
258328
329+ let idMeta : IdMetaType
330+ let maybeNoIdMeta : IdMetaType ? = NoIdMetadata ( ) as? IdMetaType
331+ if let noIdMeta = maybeNoIdMeta {
332+ idMeta = noIdMeta
333+ } else {
334+ idMeta = try identifier. decode ( IdMetaType . self, forKey: . metadata)
335+ }
336+ self . idMeta = idMeta
337+
259338 id = Identifiable . ID ( rawValue: try identifier. decode ( Identifiable . ID. RawType. self, forKey: . id) )
260339 }
261340
@@ -282,6 +361,9 @@ extension ToOneRelationship: Codable where Identifiable.ID: OptionalId {
282361 var identifier = container. nestedContainer ( keyedBy: ResourceIdentifierCodingKeys . self, forKey: . data)
283362
284363 try identifier. encode ( id. rawValue, forKey: . id)
364+ if IdMetaType . self != NoMetadata . self {
365+ try identifier. encode ( idMeta, forKey: . metadata)
366+ }
285367 try identifier. encode ( Identifiable . jsonType, forKey: . entityType)
286368 }
287369}
@@ -311,10 +393,10 @@ extension ToManyRelationship: Codable {
311393 throw error
312394 }
313395 throw JSONAPICodingError . quantityMismatch ( expected: . many,
314- path: context. codingPath)
396+ path: context. codingPath)
315397 }
316398
317- var newIds = [ Relatable . ID] ( )
399+ var newIds = [ ID] ( )
318400 while !identifiers. isAtEnd {
319401 let identifier = try identifiers. nestedContainer ( keyedBy: ResourceIdentifierCodingKeys . self)
320402
@@ -324,9 +406,19 @@ extension ToManyRelationship: Codable {
324406 throw JSONAPICodingError . typeMismatch ( expected: Relatable . jsonType, found: type, path: decoder. codingPath)
325407 }
326408
327- newIds. append ( Relatable . ID ( rawValue: try identifier. decode ( Relatable . ID. RawType. self, forKey: . id) ) )
409+ let id = try identifier. decode ( Relatable . ID. RawType. self, forKey: . id)
410+
411+ let idMeta : IdMetaType
412+ let maybeNoIdMeta : IdMetaType ? = NoIdMetadata ( ) as? IdMetaType
413+ if let noIdMeta = maybeNoIdMeta {
414+ idMeta = noIdMeta
415+ } else {
416+ idMeta = try identifier. decode ( IdMetaType . self, forKey: . metadata)
417+ }
418+
419+ newIds. append ( . init( id: Relatable . ID ( rawValue: id) , meta: idMeta) )
328420 }
329- ids = newIds
421+ metaIds = newIds
330422 }
331423
332424 public func encode( to encoder: Encoder ) throws {
@@ -342,10 +434,13 @@ extension ToManyRelationship: Codable {
342434
343435 var identifiers = container. nestedUnkeyedContainer ( forKey: . data)
344436
345- for id in ids {
437+ for id in metaIds {
346438 var identifier = identifiers. nestedContainer ( keyedBy: ResourceIdentifierCodingKeys . self)
347439
348- try identifier. encode ( id. rawValue, forKey: . id)
440+ try identifier. encode ( id. id. rawValue, forKey: . id)
441+ if IdMetaType . self != NoMetadata . self {
442+ try identifier. encode ( id. meta, forKey: . metadata)
443+ }
349444 try identifier. encode ( Relatable . jsonType, forKey: . entityType)
350445 }
351446 }
0 commit comments