@@ -231,13 +231,16 @@ public enum RenderBlockContent: Equatable {
231231 public var header : HeaderType
232232 /// The rows in this table.
233233 public var rows : [ TableRow ]
234+ /// Any extended information that describes cells in this table.
235+ public var extendedData : Set < TableCellExtendedData >
234236 /// Additional metadata for this table, if present.
235237 public var metadata : RenderContentMetadata ?
236238
237239 /// 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 ) {
239241 self . header = header
240242 self . rows = rows
243+ self . extendedData = extendedData
241244 self . metadata = metadata
242245 }
243246 }
@@ -378,6 +381,36 @@ public enum RenderBlockContent: Equatable {
378381 cells = try container. decode ( [ Cell ] . self)
379382 }
380383 }
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+ }
381414
382415 /// A term definition.
383416 ///
@@ -429,6 +462,102 @@ public enum RenderBlockContent: Equatable {
429462 }
430463}
431464
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+
432561// Codable conformance
433562extension RenderBlockContent : Codable {
434563 private enum CodingKeys : CodingKey {
@@ -475,11 +604,8 @@ extension RenderBlockContent: Codable {
475604 case . dictionaryExample:
476605 self = try . dictionaryExample( . init( summary: container. decodeIfPresent ( [ RenderBlockContent ] . self, forKey: . summary) , example: container. decode ( CodeExample . self, forKey: . example) ) )
477606 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) )
483609 case . termList:
484610 self = try . termList( . init( items: container. decode ( [ TermListItem ] . self, forKey: . items) ) )
485611 case . row:
@@ -551,9 +677,8 @@ extension RenderBlockContent: Codable {
551677 try container. encodeIfPresent ( e. summary, forKey: . summary)
552678 try container. encode ( e. example, forKey: . example)
553679 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)
557682 case . termList( items: let l) :
558683 try container. encode ( l. items, forKey: . items)
559684 case . row( let row) :
0 commit comments