@@ -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,83 @@ 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
+ enum CodingKeys : String , CodingKey {
482
+ case header, rows, extendedData, metadata
483
+ }
484
+
485
+ // TableCellExtendedData encodes the row and column indices as a dynamic key with the format "{row}_{column}".
486
+ struct DynamicIndexCodingKey : CodingKey , Equatable {
487
+ let row , column : Int
488
+ init ( row: Int , column: Int ) {
489
+ self . row = row
490
+ self . column = column
491
+ }
492
+
493
+ var stringValue : String {
494
+ return " \( row) _ \( column) "
495
+ }
496
+ init ? ( stringValue: String ) {
497
+ let coordinates = stringValue. split ( separator: " _ " )
498
+ guard coordinates. count == 2 ,
499
+ let rowIndex = Int ( coordinates. first!) ,
500
+ let columnIndex = Int ( coordinates. last!) else {
501
+ return nil
502
+ }
503
+ row = rowIndex
504
+ column = columnIndex
505
+ }
506
+ // The key is only represented by a string value
507
+ var intValue : Int ? { nil }
508
+ init ? ( intValue: Int ) { nil }
509
+ }
510
+
511
+ enum ExtendedDataCodingKeys : String , CodingKey {
512
+ case colspan, rowspan
513
+ }
514
+
515
+ public init ( from decoder: Decoder ) throws {
516
+ let container = try decoder. container ( keyedBy: CodingKeys . self)
517
+
518
+ self . header = try container. decode ( RenderBlockContent . HeaderType. self, forKey: . header)
519
+ self . rows = try container. decode ( [ RenderBlockContent . TableRow ] . self, forKey: . rows)
520
+ self . metadata = try container. decodeIfPresent ( RenderContentMetadata . self, forKey: . metadata)
521
+
522
+ var extendedData = Set < RenderBlockContent . TableCellExtendedData > ( )
523
+ if container. contains ( . extendedData) {
524
+ let dataContainer = try container. nestedContainer ( keyedBy: DynamicIndexCodingKey . self, forKey: . extendedData)
525
+
526
+ for index in dataContainer. allKeys {
527
+ let cellContainer = try dataContainer. nestedContainer ( keyedBy: ExtendedDataCodingKeys . self, forKey: index)
528
+ extendedData. insert ( . init( rowIndex: index. row,
529
+ columnIndex: index. column,
530
+ colspan: try cellContainer. decode ( UInt . self, forKey: . colspan) ,
531
+ rowspan: try cellContainer. decode ( UInt . self, forKey: . rowspan) ) )
532
+ }
533
+ }
534
+ self . extendedData = extendedData
535
+ }
536
+
537
+ public func encode( to encoder: Encoder ) throws {
538
+ var container = encoder. container ( keyedBy: CodingKeys . self)
539
+ try container. encode ( header, forKey: . header)
540
+ try container. encode ( rows, forKey: . rows)
541
+ try container. encodeIfPresent ( metadata, forKey: . metadata)
542
+
543
+ if !extendedData. isEmpty {
544
+ var dataContainer = container. nestedContainer ( keyedBy: DynamicIndexCodingKey . self, forKey: . extendedData)
545
+ for data in extendedData {
546
+ var cellContainer = dataContainer. nestedContainer ( keyedBy: ExtendedDataCodingKeys . self,
547
+ forKey: . init( row: data. rowIndex, column: data. columnIndex) )
548
+ try cellContainer. encode ( data. colspan, forKey: . colspan)
549
+ try cellContainer. encode ( data. rowspan, forKey: . rowspan)
550
+ }
551
+ }
552
+ }
553
+ }
554
+
445
555
// Codable conformance
446
556
extension RenderBlockContent : Codable {
447
557
private enum CodingKeys : CodingKey {
@@ -488,11 +598,8 @@ extension RenderBlockContent: Codable {
488
598
case . dictionaryExample:
489
599
self = try . dictionaryExample( . init( summary: container. decodeIfPresent ( [ RenderBlockContent ] . self, forKey: . summary) , example: container. decode ( CodeExample . self, forKey: . example) ) )
490
600
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
- ) )
601
+ // Defer to Table's own Codable implemenatation to parse `extendedData` properly.
602
+ self = try . table( . init( from: decoder) )
496
603
case . termList:
497
604
self = try . termList( . init( items: container. decode ( [ TermListItem ] . self, forKey: . items) ) )
498
605
case . row:
@@ -569,9 +676,8 @@ extension RenderBlockContent: Codable {
569
676
try container. encodeIfPresent ( e. summary, forKey: . summary)
570
677
try container. encode ( e. example, forKey: . example)
571
678
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)
679
+ // Defer to Table's own Codable implemenatation to format `extendedData` properly.
680
+ try t. encode ( to: encoder)
575
681
case . termList( items: let l) :
576
682
try container. encode ( l. items, forKey: . items)
577
683
case . row( let row) :
0 commit comments