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