Skip to content

Commit 8e85ed0

Browse files
authored
Add support for Pkl 0.29 features (#56)
* Implement decoding for `Bytes` type * Implement codegen for `Bytes` * Add support for http rewrites * Add methods `EvaluateOutputBytes`, `EvaluateOutputFilesBytes` * Run tests against Pkl 0.29
1 parent 54c491e commit 8e85ed0

File tree

17 files changed

+316
-43
lines changed

17 files changed

+316
-43
lines changed

.circleci/config.pkl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ local class PklDistribution {
4747
}
4848

4949
local pklCurrent: PklDistribution = new {
50-
version = "0.27.2"
50+
version = "0.29.0"
5151
}
5252

5353
local pklDistributions: Listing<PklDistribution> = new {
@@ -134,9 +134,11 @@ jobs {
134134
distribution.downloadRunStep
135135
swiftTest
136136
new RunStep { command = "make circleci-config" }
137-
new RunStep { command = "make test-snippets" }
138-
new RunStep { command = "make test-pkl" }
139-
new RunStep { command = "make generate-fixtures" }
137+
when (distribution == pklCurrent) {
138+
new RunStep { command = "make test-snippets" }
139+
new RunStep { command = "make test-pkl" }
140+
new RunStep { command = "make generate-fixtures" }
141+
}
140142
new StoreTestResults {
141143
path = ".out/test-results/"
142144
}

.circleci/config.yml

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,21 @@ jobs:
2020
name: swift test
2121
- run:
2222
command: make circleci-config
23-
- run:
24-
command: make test-snippets
25-
- run:
26-
command: make test-pkl
27-
- run:
28-
command: make generate-fixtures
2923
- store_test_results:
3024
path: .out/test-results/
3125
resource_class: xlarge
3226
docker:
3327
- image: swift:5.10-rhel-ubi9
34-
test-pkl-0-27-2:
28+
test-pkl-0-29-0:
3529
steps:
3630
- checkout
3731
- run:
3832
command: |-
39-
PKL=$(mktemp /tmp/pkl-0.27.2-XXXXXX)
40-
curl -L "https://github.com/apple/pkl/releases/download/0.27.2/pkl-linux-amd64" > $PKL
33+
PKL=$(mktemp /tmp/pkl-0.29.0-XXXXXX)
34+
curl -L "https://github.com/apple/pkl/releases/download/0.29.0/pkl-linux-amd64" > $PKL
4135
chmod +x $PKL
4236
echo "export PKL_EXEC=$PKL" >> $BASH_ENV
43-
name: Downloading pkl-0.27.2
37+
name: Downloading pkl-0.29.0
4438
- run:
4539
command: |-
4640
mkdir -p .out/test-results/
@@ -126,11 +120,11 @@ jobs:
126120
- checkout
127121
- run:
128122
command: |-
129-
PKL=$(mktemp /tmp/pkl-0.27.2-XXXXXX)
130-
curl -L "https://github.com/apple/pkl/releases/download/0.27.2/pkl-linux-amd64" > $PKL
123+
PKL=$(mktemp /tmp/pkl-0.29.0-XXXXXX)
124+
curl -L "https://github.com/apple/pkl/releases/download/0.29.0/pkl-linux-amd64" > $PKL
131125
chmod +x $PKL
132126
echo "export PKL_EXEC=$PKL" >> $BASH_ENV
133-
name: Downloading pkl-0.27.2
127+
name: Downloading pkl-0.29.0
134128
- run:
135129
command: $PKL_EXEC project package --skip-publish-check --output-path out/pkl-package/ codegen/src/
136130
- persist_to_workspace:
@@ -211,7 +205,7 @@ workflows:
211205
- test-pkl-0-25-3:
212206
requires:
213207
- hold
214-
- test-pkl-0-27-2:
208+
- test-pkl-0-29-0:
215209
requires:
216210
- hold
217211
- test-license-headers:
@@ -227,7 +221,7 @@ workflows:
227221
main:
228222
jobs:
229223
- test-pkl-0-25-3
230-
- test-pkl-0-27-2
224+
- test-pkl-0-29-0
231225
- test-license-headers
232226
- test-format
233227
when:
@@ -242,7 +236,7 @@ workflows:
242236
ignore: /.*/
243237
tags:
244238
only: /^v?\d+\.\d+\.\d+$/
245-
- test-pkl-0-27-2:
239+
- test-pkl-0-29-0:
246240
filters:
247241
branches:
248242
ignore: /.*/
@@ -263,7 +257,7 @@ workflows:
263257
- pkl-package:
264258
requires:
265259
- test-pkl-0-25-3
266-
- test-pkl-0-27-2
260+
- test-pkl-0-29-0
267261
- test-license-headers
268262
- test-format
269263
filters:
@@ -274,7 +268,7 @@ workflows:
274268
- pkl-gen-swift-macos:
275269
requires:
276270
- test-pkl-0-25-3
277-
- test-pkl-0-27-2
271+
- test-pkl-0-29-0
278272
- test-license-headers
279273
- test-format
280274
filters:
@@ -285,7 +279,7 @@ workflows:
285279
- pkl-gen-swift-linux-amd64:
286280
requires:
287281
- test-pkl-0-25-3
288-
- test-pkl-0-27-2
282+
- test-pkl-0-29-0
289283
- test-license-headers
290284
- test-format
291285
filters:
@@ -296,7 +290,7 @@ workflows:
296290
- pkl-gen-swift-linux-aarch64:
297291
requires:
298292
- test-pkl-0-25-3
299-
- test-pkl-0-27-2
293+
- test-pkl-0-29-0
300294
- test-license-headers
301295
- test-format
302296
filters:

Sources/PklSwift/Decoder/PklDecoder.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public enum PklValueType: UInt8, Decodable {
3131
case regex = 0xB
3232
case `class` = 0xC
3333
case `typealias` = 0xD
34+
case function = 0xE
35+
case bytes = 0xF
3436
case objectMemberProperty = 0x10
3537
case objectMemberEntry = 0x11
3638
case objectMemberElement = 0x12
@@ -235,6 +237,15 @@ extension _PklDecoder {
235237
case .dataSize:
236238
let decoder = try _PklDecoder(value: propertyValue)
237239
return try PklAny(value: DataSize(from: decoder))
240+
case .bytes:
241+
guard case .bin(let bytes) = value[1] else {
242+
throw DecodingError.dataCorrupted(
243+
.init(
244+
codingPath: codingPath,
245+
debugDescription: "Expected binary type but got \(value[1].debugDescription)"
246+
))
247+
}
248+
return PklAny(value: bytes)
238249
default:
239250
throw DecodingError.dataCorrupted(
240251
.init(

Sources/PklSwift/Decoder/PklUnkeyedDecodingContainer.swift

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,34 @@ extension _PklDecoder {
2424

2525
var currentIndex: Int
2626

27-
var members: [MessagePackValue]
27+
// either [members] or [bytes] are set
28+
var members: [MessagePackValue]?
29+
30+
var bytes: [UInt8]?
2831

2932
var isAtEnd: Bool {
30-
self.currentIndex >= self.members.count
33+
let length = self.members?.count ?? self.bytes!.count
34+
return self.currentIndex >= length
3135
}
3236

3337
init(value: [MessagePackValue], codingPath: [CodingKey]) throws {
3438
func error(_ debugDescription: String) -> DecodingError {
3539
.dataCorrupted(.init(codingPath: codingPath, debugDescription: debugDescription))
3640
}
41+
3742
guard value.count > 0 else {
3843
throw error("Expected at least a type marker, but got 0 values")
3944
}
4045
guard let pklType = try? value[0].decode(PklValueType.self) else {
4146
throw error("Failed to decode type marker from '\(value[0])'")
4247
}
4348

44-
if value.count == 2 {
49+
if pklType == .bytes {
50+
guard case .bin(let bytes) = value[1] else {
51+
throw error("Expected binary but got \(value[1].debugDataTypeDescription)")
52+
}
53+
self.bytes = bytes
54+
} else if value.count == 2 {
4555
guard [.list, .listing, .set].contains(pklType) else {
4656
throw error("Expected either list, listing, or set, but got \(pklType)")
4757
}
@@ -58,16 +68,29 @@ extension _PklDecoder {
5868
}
5969

6070
func decodeNil() throws -> Bool {
71+
if self.bytes != nil {
72+
return false
73+
}
74+
let members = self.members!
6175
defer { currentIndex += 1 }
62-
switch self.members[self.currentIndex] {
76+
switch members[self.currentIndex] {
6377
case .nil: return true
6478
default: return false
6579
}
6680
}
6781

82+
private func decodeBytes<T>(_: T.Type, _ bytes: [UInt8]) throws -> T where T: Decodable {
83+
let value = bytes[self.currentIndex]
84+
return value as! T
85+
}
86+
6887
func decode<T>(_ type: T.Type) throws -> T where T: Decodable {
6988
defer { currentIndex += 1 }
70-
let value = self.members[self.currentIndex]
89+
if let bytes = self.bytes {
90+
return try self.decodeBytes(type, bytes)
91+
}
92+
93+
let value = self.members![self.currentIndex]
7194

7295
// special case for polymorphic types
7396
if type == PklAny.self,
@@ -80,8 +103,11 @@ extension _PklDecoder {
80103
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer<
81104
NestedKey
82105
> where NestedKey: CodingKey {
106+
if self.bytes != nil {
107+
throw self.error("Cannot decode byte into \(type) because the decoded value is a byte array.")
108+
}
83109
defer { currentIndex += 1 }
84-
let value = self.members[self.currentIndex]
110+
let value = self.members![self.currentIndex]
85111
var nestedCodingPath = self.codingPath
86112
if let key = NestedKey(intValue: currentIndex) {
87113
nestedCodingPath.append(key)
@@ -92,8 +118,11 @@ extension _PklDecoder {
92118
}
93119

94120
func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
121+
if self.bytes != nil {
122+
throw self.error("Cannot create nested UnkeyedDecodingContainer; the decoded value is a byte array.")
123+
}
95124
defer { currentIndex += 1 }
96-
let value = self.members[self.currentIndex]
125+
let value = self.members![self.currentIndex]
97126
var nestedCodingPath = self.codingPath
98127
if let key = _PklKey(intValue: currentIndex) {
99128
nestedCodingPath.append(key)
@@ -108,5 +137,9 @@ extension _PklDecoder {
108137
func superDecoder() throws -> Decoder {
109138
fatalError("TODO")
110139
}
140+
141+
private func error(_ debugDescription: String) -> DecodingError {
142+
.dataCorrupted(.init(codingPath: self.codingPath, debugDescription: debugDescription))
143+
}
111144
}
112145
}

Sources/PklSwift/Evaluator.swift

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ public struct Evaluator {
112112
try await self.evaluateExpression(source: source, expression: "output.text", as: String.self)
113113
}
114114

115+
/// Evaluates the provided module's `output.bytes` property, and returns the result as `[UInt8]`.
116+
///
117+
/// - Parameters:
118+
/// - source: The module source to be evaluated.
119+
/// - Returns: A [UInt8] representing the byte array of the module.
120+
/// - Throws: ``PklError`` if an error occurs during evaluation.
121+
public func evaluateOutputBytes(source: ModuleSource) async throws -> [UInt8] {
122+
try await self.evaluateExpression(source: source, expression: "output.bytes", as: [UInt8].self)
123+
}
124+
115125
/// Evaluates the provided module's `output.value` property, and decodes the result as type `type`.
116126
///
117127
/// - Parameters:
@@ -124,18 +134,33 @@ public struct Evaluator {
124134
try await self.evaluateExpression(source: source, expression: "output.value", as: type)
125135
}
126136

127-
/// Evaluates the `output.files` property of the given module.
137+
/// Evaluates the `output.files` property of the given module, returning each file's contents as a string.
128138
///
129-
/// - Parameter source: The module to be evaluated.
130-
/// - Returns: A dictionary whose keys are the filenames, and values are the file contents.
139+
/// - Parameters:
140+
/// - source: The module to be evaluated.
141+
/// - Returns: A dictionary whose keys are the filenames, and values are the file contents as a string.
131142
/// - Throws: ``PklError`` if an error occurs during evaluation.
132143
public func evaluateOutputFiles(source: ModuleSource) async throws -> [String: String] {
133144
try await self.evaluateExpression(
134-
source: source, expression: "output.files.toMap().mapValues((_, it) -> it.text)",
145+
source: source, expression: "output.files?.toMap()?.mapValues((_, it) -> it.text) ?? Map()",
135146
as: [String: String].self
136147
)
137148
}
138149

150+
/// Evaluates the `output.files` property of the given module, returning each file's contents as a byte array.
151+
///
152+
/// - Parameters:
153+
/// - source: The module to be evaluated.
154+
/// - Returns: A dictionary whose keys are the filenames, and values are the file contents as bytes.
155+
/// - Throws: ``PklError`` if an error occurs during evaluation.
156+
public func evaluateOutputFilesBytes(source: ModuleSource) async throws -> [String: [UInt8]] {
157+
try await self.evaluateExpression(
158+
source: source,
159+
expression: "output.files?.toMap()?.mapValues((_, it) -> it.bytes) ?? Map()",
160+
as: [String: [UInt8]].self
161+
)
162+
}
163+
139164
/// Evaluates the provided `expression` within `module`, and decodes the result as type `type`.
140165
///
141166
/// - Parameters:

Sources/PklSwift/EvaluatorManager.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,9 +376,13 @@ enum PklBugError: Error {
376376
let pklVersion0_25 = SemanticVersion("0.25.0")!
377377
let pklVersion0_26 = SemanticVersion("0.26.0")!
378378
let pklVersion0_27 = SemanticVersion("0.27.0")!
379+
let pklVersion0_28 = SemanticVersion("0.28.0")!
380+
let pklVersion0_29 = SemanticVersion("0.29.0")!
379381

380382
let supportedPklVersions = [
381383
pklVersion0_25,
382384
pklVersion0_26,
383385
pklVersion0_27,
386+
pklVersion0_28,
387+
pklVersion0_29,
384388
]

Sources/PklSwift/EvaluatorOptions.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,15 @@ extension EvaluatorOptions {
262262
options.allowedResources = evaluatorSettings.allowedResources ?? self.allowedResources
263263
options.cacheDir = evaluatorSettings.noCache != nil ? nil : (evaluatorSettings.moduleCacheDir ?? self.cacheDir)
264264
options.rootDir = evaluatorSettings.rootDir ?? self.rootDir
265-
options.http = evaluatorSettings.http ?? self.http
265+
if let http = evaluatorSettings.http {
266+
options.http = .init()
267+
if let proxy = http.proxy {
268+
options.http!.proxy = .init()
269+
options.http!.proxy!.noProxy = proxy.noProxy ?? self.http?.proxy?.noProxy
270+
options.http!.proxy!.address = proxy.address ?? self.http?.proxy?.address
271+
}
272+
options.http!.rewrites = http.rewrites ?? self.http?.rewrites
273+
}
266274
options.externalModuleReaders = evaluatorSettings.externalModuleReaders ?? options.externalModuleReaders
267275
options.externalResourceReaders = evaluatorSettings.externalResourceReaders ?? options.externalResourceReaders
268276
return options

Sources/PklSwift/PklEvaluatorSettings.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,23 @@ public struct Http: Codable, Hashable {
6262
/// If `nil`, uses the operating system's proxy configuration.
6363
/// Configuration of the HTTP proxy to use.
6464
var proxy: Proxy?
65+
66+
/// HTTP URL rewrite rules.
67+
///
68+
/// Added in Pkl 0.29.
69+
/// This field is ignored if targeting Pkl 0.28 and older.
70+
///
71+
/// Each key-value pair designates a source prefix to a target prefix.
72+
/// Each rewrite rule must start with `http://` or `https://`, and end with `/`.
73+
///
74+
/// This option is often used for setting up package mirroring.
75+
///
76+
/// The following example will rewrite a request https://example.com/foo/bar to https://my.other.website/foo/bar:
77+
///
78+
/// Rewrites: [
79+
/// "https://example.com/": "https://my.other.website/"
80+
/// ]
81+
var rewrites: [String: String]?
6582
}
6683

6784
/// Settings that control how Pkl talks to HTTP proxies.

Tests/PklSwiftTests/Decoder/PklDecoderTest.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,12 @@ final class PklDecoderTests: XCTestCase {
103103
let value = try PklDecoder.decode([Int: Int].self, from: bytes)
104104
XCTAssertEqual(value, [1: 1])
105105
}
106+
107+
func testDecodeBytes() throws {
108+
let bytes: [UInt8] = [
109+
0x92, 0x0F, 0xC4, 0x4, 0x1, 0x2, 0x3, 0xFF,
110+
]
111+
let value = try PklDecoder.decode([UInt8].self, from: bytes)
112+
XCTAssertEqual(value, [1, 2, 3, 255])
113+
}
106114
}

0 commit comments

Comments
 (0)