File: Table.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (134 lines) | stat: -rw-r--r-- 4,882 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/*
 This source file is part of the Swift.org open source project

 Copyright (c) 2021 Apple Inc. and the Swift project authors
 Licensed under Apache License v2.0 with Runtime Library Exception

 See https://swift.org/LICENSE.txt for license information
 See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/


/// A table.
///
/// A table consists of a *head*, a single row of cells; and a *body*, which can contain zero or more *rows*.
///
/// There are a few invariants on the table which must be kept due to the parser's implementation of the [spec](https://github.github.com/gfm/#tables-extension-).
///
/// - All rows must have the same number of cells. Therefore, sibling rows will be expanded with empty cells to fit larger incoming rows. Trimming columns from the table requires explicit action
/// - Column alignment applies to all cells within in the same column. See ``columnAlignments``.
public struct Table: BlockMarkup {
    /// The alignment of all cells under a table column.
    public enum ColumnAlignment {
        /// Left alignment.
        case left

        /// Center alignment.
        case center

        /// Right alignment.
        case right
    }

    public var _data: _MarkupData

    init(_ data: _MarkupData) {
        self._data = data
    }

    init(_ raw: RawMarkup) throws {
        guard case .table = raw.data else {
            throw RawMarkup.Error.concreteConversionError(from: raw, to: Table.self)
        }
        let absoluteRaw = AbsoluteRawMarkup(markup: raw, metadata: MarkupMetadata(id: .newRoot(), indexInParent: 0))
        self.init(_MarkupData(absoluteRaw))
    }
}

// MARK: - Public API

public extension Table {
    /// Create a table from a header, body, and optional column alignments.
    ///
    /// - parameter columnAlignments: An optional list of alignments for each column,
    ///   truncated or expanded with `nil` to fit the table's maximum column count.
    /// - parameter head: A ``Table/Head-swift.struct`` element serving as the table's head.
    /// - parameter body: A ``Table/Body-swift.struct`` element serving as the table's body.
    init(columnAlignments: [ColumnAlignment?]? = nil,
         header: Head = Head(),
         body: Body = Body()) {
        try! self.init(RawMarkup.table(columnAlignments: columnAlignments ?? [],
                                       parsedRange: nil,
                                       header: header.raw.markup,
                                       body: body.raw.markup))
    }

    /// The maximum number of columns in each row.
    var maxColumnCount: Int {
        return max(head.childCount, body.maxColumnCount)
    }

    /// The table's header, a single row of cells.
    var head: Head {
        get {
            return child(at: 0) as! Head
        }
        set {
            _data = _data.replacingSelf(.table(columnAlignments: columnAlignments,
                                             parsedRange: nil,
                                             header: newValue.raw.markup,
                                             body: raw.markup.child(at: 1)))
        }
    }

    /// The table's body, a collection of rows.
    var body: Body {
        get {
            return child(at: 1) as! Body
        }
        set {
            _data = _data.replacingSelf(.table(columnAlignments: columnAlignments,
                                             parsedRange: nil,
                                             header: raw.markup.child(at: 0),
                                             body: newValue.raw.markup))
        }
    }

    /// Alignments to apply to each cell in each column.
    var columnAlignments: [Table.ColumnAlignment?] {
        get {
            guard case let .table(columnAlignments: alignments) = self.raw.markup.data else {
                fatalError("\(self) markup wrapped unexpected \(_data.raw)")
            }
            return alignments
        }

        set {
            _data = _data.replacingSelf(.table(columnAlignments: newValue,
                                             parsedRange: nil,
                                             header: raw.markup.child(at: 0),
                                             body: raw.markup.child(at: 1)))
        }
    }

    /// `true` if both the ``Table/head-swift.property`` and ``Table/body-swift.property`` are empty.
    var isEmpty: Bool {
        return head.isEmpty && body.isEmpty
    }

    // MARK: Visitation

    func accept<V>(_ visitor: inout V) -> V.Result where V : MarkupVisitor {
        return visitor.visitTable(self)
    }
}

fileprivate extension Array where Element == Table.ColumnAlignment? {
    func ensuringCount(atLeast minCount: Int) -> Self {
        if count < minCount {
            return self + Array(repeating: nil, count: count - minCount)
        } else {
            return self
        }
    }
}