@@ -235,13 +235,16 @@ public enum RenderBlockContent: Equatable {
235235 public var header : HeaderType
236236 /// The rows in this table.
237237 public var rows : [ TableRow ]
238+ /// Any extended information that describes cells in this table.
239+ public var extendedData : Set < TableCellExtendedData >
238240 /// Additional metadata for this table, if present.
239241 public var metadata : RenderContentMetadata ?
240242
241243 /// 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 ) {
243245 self . header = header
244246 self . rows = rows
247+ self . extendedData = extendedData
245248 self . metadata = metadata
246249 }
247250 }
@@ -382,6 +385,36 @@ public enum RenderBlockContent: Equatable {
382385 cells = try container. decode ( [ Cell ] . self)
383386 }
384387 }
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+ }
385418
386419 /// A term definition.
387420 ///
@@ -442,6 +475,102 @@ public enum RenderBlockContent: Equatable {
442475 }
443476}
444477
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+
445574// Codable conformance
446575extension RenderBlockContent : Codable {
447576 private enum CodingKeys : CodingKey {
@@ -488,11 +617,8 @@ extension RenderBlockContent: Codable {
488617 case . dictionaryExample:
489618 self = try . dictionaryExample( . init( summary: container. decodeIfPresent ( [ RenderBlockContent ] . self, forKey: . summary) , example: container. decode ( CodeExample . self, forKey: . example) ) )
490619 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) )
496622 case . termList:
497623 self = try . termList( . init( items: container. decode ( [ TermListItem ] . self, forKey: . items) ) )
498624 case . row:
@@ -569,9 +695,8 @@ extension RenderBlockContent: Codable {
569695 try container. encodeIfPresent ( e. summary, forKey: . summary)
570696 try container. encode ( e. example, forKey: . example)
571697 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)
575700 case . termList( items: let l) :
576701 try container. encode ( l. items, forKey: . items)
577702 case . row( let row) :
0 commit comments