Skip to content

Commit 885ea10

Browse files
Merge pull request #6 from NeedleInAJayStack/feat/gridImprovements
Grid Improvements
2 parents 95f61f2 + 66adcd5 commit 885ea10

7 files changed

Lines changed: 170 additions & 30 deletions

File tree

Package.resolved

Lines changed: 56 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/Haystack/Dict.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,34 @@ public struct Dict: Val {
2020
self.elements = elements
2121
}
2222

23+
public func trap(_ name: String) throws -> any Val {
24+
guard let fieldVal = self.elements[name], !(fieldVal is Null) else {
25+
throw DictError.tagNotFound(name)
26+
}
27+
return fieldVal
28+
}
29+
30+
public func trap<T: Val>(_ name: String, as: T.Type) throws -> T {
31+
guard let fieldVal = self.elements[name], !(fieldVal is Null) else {
32+
throw DictError.tagNotFound(name)
33+
}
34+
return try fieldVal.coerce(to: T.self)
35+
}
36+
37+
public func get(_ name: String) throws -> (any Val)? {
38+
guard let fieldVal = self.elements[name], !(fieldVal is Null) else {
39+
return nil
40+
}
41+
return fieldVal
42+
}
43+
44+
public func get<T: Val>(_ name: String, as: T.Type) throws -> T? {
45+
guard let fieldVal = self.elements[name], !(fieldVal is Null) else {
46+
return nil
47+
}
48+
return try fieldVal.coerce(to: T.self)
49+
}
50+
2351
/// Converts to Zinc formatted string.
2452
/// See [Zinc Literals](https://project-haystack.org/doc/docHaystack/Zinc#literals)
2553
public func toZinc() -> String {
@@ -167,3 +195,7 @@ extension Dict: ExpressibleByDictionaryLiteral {
167195
self.elements = elements
168196
}
169197
}
198+
199+
public enum DictError: Error {
200+
case tagNotFound(String)
201+
}

Sources/Haystack/Utils/GridBuilder.swift

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public class GridBuilder {
5959
}
6060

6161
@discardableResult
62-
/// Append a new column to the grid. New columns may not be added if the builder contains rows.
62+
/// Append a new column to the grid.
6363
/// - Parameters:
6464
/// - name: The name of the new column
6565
/// - meta: Column-level metadata for the new column
@@ -77,6 +77,24 @@ public class GridBuilder {
7777
colMeta[name] = meta
7878
}
7979

80+
rows = rows.map { row in
81+
var newRow = row
82+
newRow[name] = Null.val
83+
return newRow
84+
}
85+
86+
return self
87+
}
88+
89+
@discardableResult
90+
/// Append a list of new columns to the grid.
91+
/// - Parameters:
92+
/// - names: The names of the new columns
93+
/// - Returns: This instance for chaining
94+
public func addCols(names: [String]) throws -> Self {
95+
for name in names {
96+
try self.addCol(name: name)
97+
}
8098
return self
8199
}
82100

@@ -102,7 +120,22 @@ public class GridBuilder {
102120
}
103121

104122
@discardableResult
105-
/// Append a new row to the grid. No new columns may be defined on the builder after calling this function.
123+
/// Append a new row to the grid. Newly seen columns are added automatically with no metadata, although column ordering is not guaranteed.
124+
/// - Parameter vals: The values of the row, in the same order as the columns.
125+
/// - Returns: This instance for chaining
126+
public func addRow(_ row: [String: any Val]) throws -> Self {
127+
for (colName, _) in row {
128+
if !colNames.contains(colName) {
129+
try self.addCol(name: colName)
130+
}
131+
}
132+
rows.append(row)
133+
134+
return self
135+
}
136+
137+
@discardableResult
138+
/// Append a new row to the grid.
106139
/// - Parameter vals: The values of the row, in the same order as the columns.
107140
/// - Returns: This instance for chaining
108141
public func addRow(_ vals: [any Val]) throws -> Self {
@@ -117,6 +150,17 @@ public class GridBuilder {
117150
rows.append(row)
118151
return self
119152
}
153+
154+
@discardableResult
155+
/// Append new rows to the grid.
156+
/// - Parameter vals: The values of the rows, in the same order as the columns.
157+
/// - Returns: This instance for chaining
158+
public func addRows(_ rows: [[any Val]]) throws -> Self {
159+
for row in rows {
160+
try self.addRow(row)
161+
}
162+
return self
163+
}
120164
}
121165

122166
enum GridBuilderError: Error {

Sources/Haystack/Val.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ extension Val {
1515

1616
return self == otherAsMyType
1717
}
18+
19+
func coerce<T: Val>(to: T.Type) throws -> T {
20+
guard let coercedSelf = self as? T else {
21+
throw ValError.cannotBeCoerced(self.toZinc(), T.valType)
22+
}
23+
return coercedSelf
24+
}
1825
}
1926

2027

Sources/Haystack/ValError.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
public enum ValError: Error {
3+
case cannotBeCoerced(String, ValType)
34
case invalidDateDefinition
45
case invalidDateFormat(String)
56
case invalidDateTimeDefinition

Tests/HaystackTests/DictTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,24 @@ final class DictTests: XCTestCase {
130130
])
131131
)
132132
}
133+
134+
func testTrap() {
135+
let dict: Dict = ["a": "abc", "b": null]
136+
137+
try XCTAssertNoThrow(dict.trap("a"))
138+
try XCTAssertEqual(dict.trap("a", as: String.self), "abc")
139+
try XCTAssertThrowsError(dict.trap("a", as: Number.self))
140+
try XCTAssertThrowsError(dict.trap("b"))
141+
try XCTAssertThrowsError(dict.trap("c"))
142+
}
143+
144+
func testGet() {
145+
let dict: Dict = ["a": "abc", "b": null]
146+
147+
try XCTAssertNotNil(dict.get("a"))
148+
try XCTAssertEqual(dict.get("a", as: String.self), "abc")
149+
try XCTAssertThrowsError(dict.get("a", as: Number.self))
150+
try XCTAssertNil(dict.get("b"))
151+
try XCTAssertNil(dict.get("c"))
152+
}
133153
}

Tests/HaystackTests/GridTests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ final class GridTests: XCTestCase {
99
.addCol(name: "equip")
1010
.addCol(name: "siteRef")
1111
.addCol(name: "installed")
12-
.addRow(["RTU-1", marker, Ref("153c-699a", dis: "HQ"), Date(year: 2005, month: 6, day: 1)])
13-
.addRow(["RTU-2", marker, Ref("153c-699b", dis: "Library"), Date(year: 1997, month: 7, day: 12)])
12+
.addRow(["dis": "RTU-1", "equip": marker, "siteRef": Ref("153c-699a", dis: "HQ"), "installed": Date(year: 2005, month: 6, day: 1)])
13+
.addRow(["dis": "RTU-2", "equip": marker, "siteRef": Ref("153c-699b", dis: "Library"), "installed": Date(year: 1997, month: 7, day: 12)])
1414
.toGrid()
1515
let jsonString = #"{"_kind":"grid","meta":{"ver":"3.0","foo":"bar"},"cols":[{"name":"dis","meta":{"dis":"Equip Name"}},{"name":"equip"},{"name":"siteRef"},{"name":"installed"}],"rows":[{"dis":"RTU-1","equip":{"_kind":"marker"},"siteRef":{"_kind":"ref","val":"153c-699a","dis":"HQ"},"installed":{"_kind":"date","val":"2005-06-01"}},{"dis": "RTU-2","equip":{"_kind":"marker"},"siteRef":{"_kind":"ref","val":"153c-699b","dis":"Library"},"installed":{"_kind":"date","val":"1997-07-12"}}]}"#
1616

@@ -54,8 +54,8 @@ final class GridTests: XCTestCase {
5454
.addCol(name: "equip")
5555
.addCol(name: "siteRef")
5656
.addCol(name: "installed")
57-
.addRow(["RTU-1", marker, Ref("153c-699a", dis: "HQ"), Date(year: 2005, month: 6, day: 1)])
58-
.addRow(["RTU-2", marker, Ref("153c-699b", dis: "Library"), Date(year: 1997, month: 7, day: 12)])
57+
.addRow(["dis": "RTU-1", "equip": marker, "siteRef": Ref("153c-699a", dis: "HQ"), "installed": Date(year: 2005, month: 6, day: 1)])
58+
.addRow(["dis": "RTU-2", "equip": marker, "siteRef": Ref("153c-699b", dis: "Library"), "installed": Date(year: 1997, month: 7, day: 12)])
5959
.toGrid()
6060
.toZinc(),
6161
"""
@@ -86,17 +86,17 @@ final class GridTests: XCTestCase {
8686
.addCol(name: "equip")
8787
.addCol(name: "siteRef")
8888
.addCol(name: "managed")
89-
.addRow(["RTU-1", marker, Ref("153c-699a", dis: "HQ"), true])
90-
.addRow(["RTU-2", marker, Ref("153c-699b", dis: "Library"), false])
89+
.addRow(["dis": "RTU-1", "equip": marker, "siteRef": Ref("153c-699a", dis: "HQ"), "managed": true])
90+
.addRow(["dis": "RTU-2", "equip": marker, "siteRef": Ref("153c-699b", dis: "Library"), "managed": false])
9191

9292
let builder2 = try GridBuilder()
9393
.setMeta(["ver": "3.0", "foo": "bar"])
9494
.addCol(name: "dis", meta: ["dis": "Equip Name"])
9595
.addCol(name: "equip")
9696
.addCol(name: "siteRef")
9797
.addCol(name: "managed")
98-
.addRow(["RTU-1", marker, Ref("153c-699a", dis: "HQ"), false])
99-
.addRow(["RTU-2", marker, Ref("153c-699b", dis: "Library"), false])
98+
.addRow(["dis": "RTU-1", "equip": marker, "siteRef": Ref("153c-699a", dis: "HQ"), "managed": false])
99+
.addRow(["dis": "RTU-2", "equip": marker, "siteRef": Ref("153c-699b", dis: "Library"), "managed": false])
100100

101101

102102
// Test basic

0 commit comments

Comments
 (0)