@@ -231,13 +231,16 @@ public enum RenderBlockContent: Equatable {
231
231
public var header : HeaderType
232
232
/// The rows in this table.
233
233
public var rows : [ TableRow ]
234
+ /// Any extended information that describes cells in this table.
235
+ public var extendedData : Set < TableCellExtendedData >
234
236
/// Additional metadata for this table, if present.
235
237
public var metadata : RenderContentMetadata ?
236
238
237
239
/// Creates a new table with the given data.
238
- public init ( header: HeaderType , rows: [ TableRow ] , metadata: RenderContentMetadata ? = nil ) {
240
+ public init ( header: HeaderType , rows: [ TableRow ] , extendedData : Set < TableCellExtendedData > , metadata: RenderContentMetadata ? = nil ) {
239
241
self . header = header
240
242
self . rows = rows
243
+ self . extendedData = extendedData
241
244
self . metadata = metadata
242
245
}
243
246
}
@@ -378,6 +381,36 @@ public enum RenderBlockContent: Equatable {
378
381
cells = try container. decode ( [ Cell ] . self)
379
382
}
380
383
}
384
+
385
+ /// Extended data that may be applied to a table cell.
386
+ public struct TableCellExtendedData : Equatable , Hashable {
387
+ /// The row coordinate for the cell described by this data.
388
+ public let rowIndex : Int
389
+ /// The column coordinate for the cell described by this data.
390
+ public let columnIndex : Int
391
+
392
+ /// The number of columns this cell spans over.
393
+ ///
394
+ /// A value of 1 is the default. A value of zero means that this cell is being "spanned
395
+ /// over" by a previous cell in this row. A value of greater than 1 means that this cell
396
+ /// "spans over" later cells in this row.
397
+ public let colspan : UInt
398
+
399
+ /// The number of rows this cell spans over.
400
+ ///
401
+ /// A value of 1 is the default. A value of zero means that this cell is being "spanned
402
+ /// over" by another cell in a previous row. A value of greater than one means that this
403
+ /// cell "spans over" other cells in later rows.
404
+ public let rowspan : UInt
405
+
406
+ public init ( rowIndex: Int , columnIndex: Int ,
407
+ colspan: UInt , rowspan: UInt ) {
408
+ self . rowIndex = rowIndex
409
+ self . columnIndex = columnIndex
410
+ self . colspan = colspan
411
+ self . rowspan = rowspan
412
+ }
413
+ }
381
414
382
415
/// A term definition.
383
416
///
@@ -429,6 +462,102 @@ public enum RenderBlockContent: Equatable {
429
462
}
430
463
}
431
464
465
+ // Writing a manual Codable implementation for tables because the encoding of `extendedData` does
466
+ // not follow from the struct layout.
467
+ extension RenderBlockContent . Table : Codable {
468
+ // `extendedData` is encoded as a keyed container where the "keys" are the cell index, and
469
+ // the "values" are the remaining fields in the struct. The key is formatted as a string with
470
+ // the format "{row}_{column}", which is represented here as the `.index(row:column:)` enum
471
+ // case. This CodingKey implementation performs that parsing and formatting so that the
472
+ // Encodable/Decodable implementation can use the plain numbered indices.
473
+ enum CodingKeys : CodingKey , Equatable {
474
+ case header, rows, extendedData, metadata
475
+ case index( row: Int , column: Int )
476
+ case colspan, rowspan
477
+
478
+ var stringValue : String {
479
+ switch self {
480
+ case . header: return " header "
481
+ case . rows: return " rows "
482
+ case . extendedData: return " extendedData "
483
+ case . metadata: return " metadata "
484
+ case . colspan: return " colspan "
485
+ case . rowspan: return " rowspan "
486
+ case let . index( row, column) : return " \( row) _ \( column) "
487
+ }
488
+ }
489
+
490
+ init ? ( stringValue: String ) {
491
+ switch stringValue {
492
+ case " header " : self = . header
493
+ case " rows " : self = . rows
494
+ case " extendedData " : self = . extendedData
495
+ case " metadata " : self = . metadata
496
+ case " colspan " : self = . colspan
497
+ case " rowspan " : self = . rowspan
498
+ default :
499
+ let coordinates = stringValue. split ( separator: " _ " )
500
+ guard coordinates. count == 2 ,
501
+ let rowIndex = Int ( coordinates. first!) ,
502
+ let columnIndex = Int ( coordinates. last!) else {
503
+ return nil
504
+ }
505
+ self = . index( row: rowIndex, column: columnIndex)
506
+ }
507
+ }
508
+
509
+ var intValue : Int ? { nil }
510
+
511
+ init ? ( intValue: Int ) {
512
+ return nil
513
+ }
514
+ }
515
+
516
+ public init ( from decoder: Decoder ) throws {
517
+ let container = try decoder. container ( keyedBy: CodingKeys . self)
518
+
519
+ var extendedData = Set < RenderBlockContent . TableCellExtendedData > ( )
520
+ if container. allKeys. contains ( . extendedData) {
521
+ let dataContainer = try container. nestedContainer ( keyedBy: CodingKeys . self, forKey: . extendedData)
522
+
523
+ for index in dataContainer. allKeys {
524
+ guard case let . index( row, column) = index else { continue }
525
+
526
+ let cellContainer = try dataContainer. nestedContainer ( keyedBy: CodingKeys . self, forKey: index)
527
+ extendedData. insert ( . init( rowIndex: row,
528
+ columnIndex: column,
529
+ colspan: try cellContainer. decode ( UInt . self, forKey: . colspan) ,
530
+ rowspan: try cellContainer. decode ( UInt . self, forKey: . rowspan) ) )
531
+ }
532
+ }
533
+
534
+ self = . init( header: try container. decode ( RenderBlockContent . HeaderType. self, forKey: . header) ,
535
+ rows: try container. decode ( [ RenderBlockContent . TableRow ] . self, forKey: . rows) ,
536
+ extendedData: extendedData,
537
+ metadata: try container. decodeIfPresent ( RenderContentMetadata . self, forKey: . metadata) )
538
+ }
539
+
540
+ public func encode( to encoder: Encoder ) throws {
541
+ var container = encoder. container ( keyedBy: CodingKeys . self)
542
+
543
+ try container. encode ( header, forKey: . header)
544
+ try container. encode ( rows, forKey: . rows)
545
+
546
+ if !extendedData. isEmpty {
547
+ var dataContainer = container. nestedContainer ( keyedBy: CodingKeys . self, forKey: . extendedData)
548
+ for data in extendedData {
549
+ var cellContainer = dataContainer. nestedContainer ( keyedBy: CodingKeys . self,
550
+ forKey: . index( row: data. rowIndex,
551
+ column: data. columnIndex) )
552
+ try cellContainer. encode ( data. colspan, forKey: . colspan)
553
+ try cellContainer. encode ( data. rowspan, forKey: . rowspan)
554
+ }
555
+ }
556
+
557
+ try container. encodeIfPresent ( metadata, forKey: . metadata)
558
+ }
559
+ }
560
+
432
561
// Codable conformance
433
562
extension RenderBlockContent : Codable {
434
563
private enum CodingKeys : CodingKey {
@@ -475,11 +604,8 @@ extension RenderBlockContent: Codable {
475
604
case . dictionaryExample:
476
605
self = try . dictionaryExample( . init( summary: container. decodeIfPresent ( [ RenderBlockContent ] . self, forKey: . summary) , example: container. decode ( CodeExample . self, forKey: . example) ) )
477
606
case . table:
478
- self = try . table( . init(
479
- header: container. decode ( HeaderType . self, forKey: . header) ,
480
- rows: container. decode ( [ TableRow ] . self, forKey: . rows) ,
481
- metadata: container. decodeIfPresent ( RenderContentMetadata . self, forKey: . metadata)
482
- ) )
607
+ // Defer to Table's own Codable implemenatation to parse `extendedData` properly.
608
+ self = try . table( . init( from: decoder) )
483
609
case . termList:
484
610
self = try . termList( . init( items: container. decode ( [ TermListItem ] . self, forKey: . items) ) )
485
611
case . row:
@@ -551,9 +677,8 @@ extension RenderBlockContent: Codable {
551
677
try container. encodeIfPresent ( e. summary, forKey: . summary)
552
678
try container. encode ( e. example, forKey: . example)
553
679
case . table( let t) :
554
- try container. encode ( t. header, forKey: . header)
555
- try container. encode ( t. rows, forKey: . rows)
556
- try container. encodeIfPresent ( t. metadata, forKey: . metadata)
680
+ // Defer to Table's own Codable implemenatation to format `extendedData` properly.
681
+ try t. encode ( to: encoder)
557
682
case . termList( items: let l) :
558
683
try container. encode ( l. items, forKey: . items)
559
684
case . row( let row) :
0 commit comments