Skip to content

Commit

Permalink
Merge pull request #1 from NeedleInAJayStack/feature/client
Browse files Browse the repository at this point in the history
Haystack client
  • Loading branch information
NeedleInAJayStack authored Dec 15, 2022
2 parents 8cef725 + eecc797 commit 35b9e00
Show file tree
Hide file tree
Showing 23 changed files with 1,432 additions and 36 deletions.
16 changes: 16 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: test
on:
pull_request:
push: { branches: [ main ] }

jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: fwal/setup-swift@v1
- uses: actions/checkout@v2
- name: Run tests
run: swift test
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
.swiftpm/

This file was deleted.

16 changes: 16 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,32 @@ let package = Package(
name: "Haystack",
targets: ["Haystack"]
),
.library(
name: "HaystackClient",
targets: ["HaystackClient"]
),
],
dependencies: [],
targets: [
.target(
name: "Haystack",
dependencies: []
),
.target(
name: "HaystackClient",
dependencies: ["Haystack"]
),
.testTarget(
name: "HaystackTests",
dependencies: ["Haystack"]
),
.testTarget(
name: "HaystackClientTests",
dependencies: ["HaystackClient"]
),
.testTarget(
name: "HaystackClientIntegrationTests",
dependencies: ["HaystackClient"]
),
]
)
66 changes: 44 additions & 22 deletions Sources/Haystack/Grid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import Foundation
/// unit of data exchange over the
/// [HTTP API](https://project-haystack.org/doc/docHaystack/HttpApi).
///
/// To create a Grid, use a `GridBuilder`.
///
/// [Docs](https://project-haystack.org/doc/docHaystack/Kinds#grid)
public struct Grid: Val {
public static var valType: ValType { .Grid }
Expand All @@ -22,30 +24,39 @@ public struct Grid: Val {
/// Converts to Zinc formatted string.
/// See [Zinc Literals](https://project-haystack.org/doc/docHaystack/Zinc#literals)
public func toZinc() -> String {
var zinc = #"ver:"3.0""#
if meta.elements.count > 0 {
zinc += " \(meta.toZinc(withBraces: false))"
}
zinc += "\n"
// Ensure `ver` is listed first in meta
let ver = meta.elements["ver"] ?? "3.0"
var zinc = "ver:\(ver.toZinc())"

let zincCols = cols.map { col in
var colZinc = col.name
if let colMeta = col.meta, colMeta.elements.count > 0 {
colZinc += " \(colMeta.toZinc(withBraces: false))"
}
return colZinc
var metaWithoutVer = meta.elements
metaWithoutVer["ver"] = nil
if metaWithoutVer.count > 0 {
zinc += " \(Dict(metaWithoutVer).toZinc(withBraces: false))"
}
zinc += zincCols.joined(separator: ", ")
zinc += "\n"

let zincRows = rows.map { row in
let rowZincElements = cols.map { col in
let element = row.elements[col.name] ?? null
return element.toZinc()
if cols.isEmpty {
zinc += "empty\n"
} else {
let zincCols = cols.map { col in
var colZinc = col.name
if let colMeta = col.meta, colMeta.elements.count > 0 {
colZinc += " \(colMeta.toZinc(withBraces: false))"
}
return colZinc
}
return rowZincElements.joined(separator: ", ")
zinc += zincCols.joined(separator: ", ")
zinc += "\n"

let zincRows = rows.map { row in
let rowZincElements = cols.map { col in
let element = row.elements[col.name] ?? null
return element.toZinc()
}
return rowZincElements.joined(separator: ", ")
}
zinc += zincRows.joined(separator: "\n")
}
zinc += zincRows.joined(separator: "\n")

return zinc
}
Expand Down Expand Up @@ -77,8 +88,14 @@ extension Grid {
}

self.meta = try container.decode(Dict.self, forKey: .meta)
self.cols = try container.decode([Col].self, forKey: .cols)
self.rows = try container.decode([Dict].self, forKey: .rows)
let cols = try container.decode([Col].self, forKey: .cols)
if cols.map(\.name) == ["empty"] {
self.cols = []
self.rows = []
} else {
self.cols = cols
self.rows = try container.decode([Dict].self, forKey: .rows)
}
} else {
throw DecodingError.typeMismatch(
Self.self,
Expand All @@ -96,8 +113,13 @@ extension Grid {
var container = encoder.container(keyedBy: Self.CodingKeys)
try container.encode(Self.kindValue, forKey: ._kind)
try container.encode(meta, forKey: .meta)
try container.encode(cols, forKey: .cols)
try container.encode(rows, forKey: .rows)
if cols.isEmpty {
try container.encode([Col(name: "empty")], forKey: .cols)
try container.encode([Dict](), forKey: .rows)
} else {
try container.encode(cols, forKey: .cols)
try container.encode(rows, forKey: .rows)
}
}
}

Expand Down
5 changes: 3 additions & 2 deletions Sources/Haystack/IO/ZincReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,10 @@ public class ZincReader {
private func parseLiteral() throws -> any Val {
var val = self.curVal
if cur == .ref, peek == .str {
guard let refVal = curVal as? String, let dis = peekVal as? String else {
guard let refVal = curVal as? Ref, let dis = peekVal as? String else {
throw ZincReaderError.invalidRef
}
val = try Ref(refVal, dis: dis)
val = try Ref(refVal.val, dis: dis)
try consume(.ref)
}
try consume()
Expand All @@ -139,6 +139,7 @@ public class ZincReader {

private func parseList() throws -> List {
var elements = [any Val]()
try consume(.lbracket)
while cur != .rbracket, cur != .eof {
try elements.append(parseVal())
guard cur == .comma else {
Expand Down
4 changes: 4 additions & 0 deletions Sources/Haystack/Number.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public struct Number: Val {
}
return zinc
}

public var isInt: Bool {
return val == val.rounded()
}
}

// Number + Codable
Expand Down
11 changes: 10 additions & 1 deletion Sources/Haystack/Utils/GridBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class GridBuilder {
var rows: [[String: any Val]]

public init() {
meta = [:]
meta = ["ver":"3.0"] // We don't back-support old grid versions
colNames = []
colMeta = [:]
rows = []
Expand All @@ -17,6 +17,15 @@ public class GridBuilder {
/// Construct a grid from the assets of this instance
/// - Returns: The resulting grid
public func toGrid() -> Grid {
// empty grid handler
if colNames == ["empty"] {
return Grid(
meta: Dict(meta),
cols: [],
rows: []
)
}

let cols = colNames.map { colName in
if let meta = colMeta[colName] {
return Col(name: colName, meta: Dict(meta))
Expand Down
16 changes: 16 additions & 0 deletions Sources/HaystackClient/Authentication/AuthHash.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import CryptoKit

@available(macOS 10.15, *)
enum AuthHash: String {
case SHA512 = "SHA-512"
case SHA256 = "SHA-256"

var hash: any HashFunction.Type {
switch self {
case .SHA256:
return CryptoKit.SHA256.self
case .SHA512:
return CryptoKit.SHA512.self
}
}
}
25 changes: 25 additions & 0 deletions Sources/HaystackClient/Authentication/AuthMessage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
struct AuthMessage: CustomStringConvertible {
let scheme: String
let attributes: [String: String]

var description: String {
// Unwrap is safe because attributes is immutable
"\(scheme) \(attributes.keys.sorted().map { "\($0)=\(attributes[$0]!)" }.joined(separator: ", "))"
}

static func from(_ string: String) throws -> Self {
// Example input: "SCRAM hash=SHA-256, handshakeToken=aabbcc"
let scheme: String
let attributes: [String: String]
// If space exists then parse attributes as well.
if let spaceIndex = string.firstIndex(of: " ") {
scheme = String(string[..<spaceIndex]).trimmingCharacters(in: .whitespaces)
let attributesString = String(string[spaceIndex...]).trimmingCharacters(in: .whitespaces)
attributes = extractNameValuePairs(from: attributesString)
} else {
scheme = string
attributes = [:]
}
return Self(scheme: scheme, attributes: attributes)
}
}
4 changes: 4 additions & 0 deletions Sources/HaystackClient/Authentication/Authenticator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@available(macOS 10.15, *)
protocol Authenticator {
func getAuthToken() async throws -> String
}
Loading

0 comments on commit 35b9e00

Please sign in to comment.