Compare commits

...

35 Commits
0.2.0 ... main

Author SHA1 Message Date
Daniel Chao d6f08700d6
Prepare 0.4.2 release (#48) 2025-04-14 10:09:19 -07:00
Daniel Chao cf45d102cb
Fix Swift mapping of ints within unions (#47)
This fixes an issue where a union with an int member cannot decode
any value that is out of the Int8 bounds.

Also: make SingleValueDecodingContainer throw `typeMismatch` errors
when the expected type doesn't match.
2025-02-26 09:12:56 -08:00
Daniel Chao c514843f01
Add pkl-lsp to ignored dirs (#46) 2025-02-26 07:13:12 -08:00
Daniel Chao 9c36448681
Make enums of string literals conform to CodingKeyRepresentable (#45)
Fixes an issue where mappings of enums fail to be decoded into Swift
2025-02-25 07:10:50 -08:00
Daniel Chao 799b6696d2
Swiftformat check (#43)
* Add makefile and CI rules to run the swift formatter
* Run swift format on all files
2025-02-24 19:09:46 -08:00
Kushal Pisavadia b7e694ea3c
Configure `hawkeye` to update license headers (#38)
Adds configuration to format/check license headers with hawkeye.

Also, runs the formatter on the repo.
2025-02-17 12:45:41 -08:00
Daniel Chao dda82f32a7
Add DocC auto generation (#41)
This will add pkl-swift docs to the Swift Package Index.

See: https://swiftpackageindex.com/blog/auto-generating-auto-hosting-and-auto-updating-docc-documentation

This also adds `swift-docc-plugin` as a dependency, which enables
running command `swift package generate-documentation --target PklSwift`.
2025-02-12 06:28:28 -08:00
Daniel Chao 3c5da314c9
Prepare 0.4.1 release (#40) 2025-02-04 09:53:29 -08:00
Daniel Chao b253e83f7b
Fix enum decoding (#39)
Fixes an issue where `5.0` in Pkl can possibly be cast to `Int`
2025-02-03 10:50:36 -08:00
Kushal Pisavadia f0bd189f48
Make sure CircleCI config is up-to-date (#37) 2025-02-03 13:43:40 +01:00
Daniel Chao c280a0e632
Add PR links (#34) 2025-01-24 08:10:27 -08:00
Daniel Chao 162bf61a96
Prepare 0.4.0 release (#33) 2025-01-23 08:42:40 -08:00
Daniel Chao 52d5fbc5e1
Add color to PklEvaluatorSettings (#32)
This adds a field that was added in Pkl 0.27
2025-01-23 06:59:31 -08:00
Josh B 64c66aea8e
Implement SPICE-0009 External Readers (#26)
* Add `EvaluatorOptions.externalModuleReaders` and `EvaluatorOptions.externalResourceReaders`.
* Add `ExternalReaderClient` to host the child process side of the external reader workflow.
2025-01-17 06:34:34 -08:00
Javier Cuesta 7d451c59cd
Make PathElement initializer explicitly public (#31)
Also, fix doc comments
2024-12-13 14:18:23 -08:00
Josh B 1d0bf43b4b
Fix temporary directory creation to avoid failing pkl-core tests (#28) 2024-12-04 16:54:32 +00:00
Daniel Chao 44a04408df
Prepare 0.3.0 release (#27) 2024-10-10 09:20:14 -07:00
Kila2 e6f6aa818f
Fix build for windows (#24) 2024-10-09 17:36:22 -07:00
XIAO YU 8f050f5938
Optimize memory allocation for array in PklDecoder.decodePolymorphic (#25) 2024-08-13 13:53:04 +02:00
Philip K.F. Hölzenspies 48bdb4f503
Add Pkl 0.26 features (#23)
* Add Pkl 0.26 features
* Add multi-version support
* Bump Pkl version in CI
* Update CI
* Split tests across different Pkl versions

Co-authored-by: Daniel Chao <daniel.h.chao@gmail.com>
2024-07-02 12:36:59 +01:00
Philip K.F. Hölzenspies b23fd2369f
Remove custom Pair type (#21) 2024-05-03 16:31:40 +01:00
Tkng 2bcb25f0b7
Update README.adoc (#16) 2024-03-11 14:50:23 +01:00
Yuki Yamamoto 28d1782e50
Remove iOS from supported platform (#14) 2024-02-13 10:45:58 +01:00
Krzysztof Zabłocki cec993dc2c
type id's should not generate as mutable var (#3)
Co-authored-by: Philip K.F. Hölzenspies <holzensp@gmail.com>
2024-02-06 16:45:48 +00:00
Shinichiro Oba a754b2a010
Fix the Changelog URL (#12) 2024-02-06 16:45:28 +00:00
Shinichiro Oba efbca4eb59
Use `static loadFrom(source:)` in the quickstart (#10) 2024-02-06 17:39:18 +01:00
Daniel Chao 7d573f7c5f
Prepare 0.2.3 release (#9) 2024-02-04 11:46:50 -08:00
Daniel Chao f20974be23
Fix module name (#8)
This adds one more fix: there is a straggler module that was missing a
name and a license header
2024-02-04 11:42:46 -08:00
Daniel Chao b24a83feea
Prepare 0.2.2 release (#7) 2024-02-04 11:30:01 -08:00
Daniel Chao 522fa12db3
Fix license header (#6)
This fixes the license header on locks.swift and
adds appropriate attribution.
2024-02-04 11:24:10 -08:00
Dan Chao c273ade98c Update CircleCI: fix release name and add license header 2024-02-04 11:22:23 -08:00
josephplusplus 4ddc17a565
Allow redirect to macOS install cURL (#2) 2024-02-04 11:18:47 -08:00
Daniel Chao 3c2ba3ad75
Fix: module names should start with pkl.swift (#5) 2024-02-04 11:16:03 -08:00
Islon Scherer 13f33a0343 Prepare 0.2.1 release 2024-02-02 18:16:35 +01:00
Islon Scherer 72b92a5f59 fix packageZipUrl 2024-02-02 17:57:40 +01:00
131 changed files with 2308 additions and 796 deletions

View File

@ -1,4 +1,21 @@
amends ".../pkl-project-commons/packages/pkl.impl.circleci/PklCI.pkl"
// ===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
amends "package://pkg.pkl-lang.org/pkl-project-commons/pkl.impl.circleci@1.1.1#/PklCI.pkl"
import "pkl:semver"
local swiftTest = new RunStep {
name = "swift test"
@ -8,59 +25,78 @@ local swiftTest = new RunStep {
"""
}
local pklVersion = "0.25.1"
local pklBinary = "https://repo1.maven.org/maven2/org/pkl-lang/pkl-cli-linux-amd64/\(pklVersion)/pkl-cli-linux-amd64-\(pklVersion).bin"
local class PklDistribution {
/// The version of this distribution
version: String(semver.isValid(this))
local downloadPkl = new RunStep {
name = "Downloading pkl"
command = """
mkdir /tmp/pkl
curl -L "\(pklBinary)" > /tmp/pkl/pkl
chmod +x /tmp/pkl/pkl
echo 'export PKL_EXEC=/tmp/pkl/pkl' >> $BASH_ENV
"""
/// Normalized version for use in task names
fixed normalizedVersion: String = version.replaceAll(".", "-")
/// The URL to download this distribution
fixed downloadUrl: String = "https://github.com/apple/pkl/releases/download/\(version)/pkl-linux-amd64"
fixed downloadRunStep: RunStep = new {
name = "Downloading pkl-\(version)"
command = """
PKL=$(mktemp /tmp/pkl-\(version)-XXXXXX)
curl -L "\(downloadUrl)" > $PKL
chmod +x $PKL
echo "export PKL_EXEC=$PKL" >> $BASH_ENV
"""
}
}
local pklCurrent: PklDistribution = new {
version = "0.27.2"
}
local pklDistributions: Listing<PklDistribution> = new {
new { version = "0.25.3" }
pklCurrent
}
local testJobs = jobs.keys.filter((it) -> it.startsWith("test"))
main {
jobs {
"test"
...testJobs
}
}
prb {
jobs {
"test"
...testJobs
}
}
release {
jobs {
"test"
...testJobs
new {
["pkl-package"] {
requires {
"test"
...testJobs
}
}
}
new {
["pkl-gen-swift-macos"] {
requires {
"test"
...testJobs
}
}
}
new {
["pkl-gen-swift-linux-amd64"] {
requires {
"test"
...testJobs
}
}
}
new {
["pkl-gen-swift-linux-aarch64"] {
requires {
"test"
...testJobs
}
}
}
@ -85,22 +121,53 @@ triggerDocsBuild = "release"
triggerPackageDocsBuild = "release"
jobs {
["test"] {
for (distribution in pklDistributions) {
["test-pkl-\(distribution.normalizedVersion)"] {
docker {
new {
image = "swift:5.9-rhel-ubi9"
}
}
resource_class = "xlarge"
steps {
"checkout"
distribution.downloadRunStep
swiftTest
new RunStep { command = "make circleci-config" }
new RunStep { command = "make test-snippets" }
new RunStep { command = "make test-pkl" }
new RunStep { command = "make generate-fixtures" }
new StoreTestResults {
path = ".out/test-results/"
}
}
}
}
["test-license-headers"] {
docker {
new {
image = "ghcr.io/korandoru/hawkeye"
}
}
steps {
"checkout"
new RunStep {
command = "/bin/hawkeye check --fail-if-unknown"
}
}
}
["test-format"] {
docker {
new {
image = "swift:5.9-rhel-ubi9"
}
}
resource_class = "xlarge"
steps {
"checkout"
downloadPkl
swiftTest
new RunStep { command = "make test-snippets" }
new RunStep { command = "make test-pkl" }
new RunStep { command = "make generate-fixtures" }
new StoreTestResults {
path = ".out/test-results/"
new RunStep {
command = "make swiftformat-lint"
}
}
}
@ -153,7 +220,7 @@ jobs {
}
steps {
"checkout"
downloadPkl
pklCurrent.downloadRunStep
new RunStep {
// TODO remove skip-publish-check after initial release
command = #"$PKL_EXEC project package --skip-publish-check --output-path out/pkl-package/ codegen/src/"#
@ -190,7 +257,7 @@ jobs {
echo "Creating release for Pkl package"
gh release create "pkl.swift@${CIRCLE_TAG}" \
--title "Pkl package ${CIRCLE_TAG}" \
--title "pkl.swift@${CIRCLE_TAG}" \
--target "${CIRCLE_SHA1}" \
--notes "This holds the release assets for the pkl.swift Pkl package." \
--repo "${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}" \
@ -201,7 +268,7 @@ jobs {
--title "${CIRCLE_TAG}" \
--target "${CIRCLE_SHA1}" \
--verify-tag \
--notes "Release notes: https://pkl-lang.org/swift/CHANGELOG.html#release-${CIRCLE_TAG}" \
--notes "Release notes: https://pkl-lang.org/swift/current/CHANGELOG.html#release-${CIRCLE_TAG}" \
--repo "${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}" \
out/pkl-gen-swift/*
"""#

View File

@ -3,21 +3,23 @@ version: '2.1'
orbs:
pr-approval: apple/pr-approval@0.1.0
jobs:
test:
test-pkl-0-25-3:
steps:
- checkout
- run:
command: |-
mkdir /tmp/pkl
curl -L "https://repo1.maven.org/maven2/org/pkl-lang/pkl-cli-linux-amd64/0.25.1/pkl-cli-linux-amd64-0.25.1.bin" > /tmp/pkl/pkl
chmod +x /tmp/pkl/pkl
echo 'export PKL_EXEC=/tmp/pkl/pkl' >> $BASH_ENV
name: Downloading pkl
PKL=$(mktemp /tmp/pkl-0.25.3-XXXXXX)
curl -L "https://github.com/apple/pkl/releases/download/0.25.3/pkl-linux-amd64" > $PKL
chmod +x $PKL
echo "export PKL_EXEC=$PKL" >> $BASH_ENV
name: Downloading pkl-0.25.3
- run:
command: |-
mkdir -p .out/test-results/
swift test -vv --parallel --num-workers 1 --xunit-output .out/test-results/xunit.xml -Xswiftc -warnings-as-errors
name: swift test
- run:
command: make circleci-config
- run:
command: make test-snippets
- run:
@ -29,6 +31,48 @@ jobs:
resource_class: xlarge
docker:
- image: swift:5.9-rhel-ubi9
test-pkl-0-27-2:
steps:
- checkout
- run:
command: |-
PKL=$(mktemp /tmp/pkl-0.27.2-XXXXXX)
curl -L "https://github.com/apple/pkl/releases/download/0.27.2/pkl-linux-amd64" > $PKL
chmod +x $PKL
echo "export PKL_EXEC=$PKL" >> $BASH_ENV
name: Downloading pkl-0.27.2
- run:
command: |-
mkdir -p .out/test-results/
swift test -vv --parallel --num-workers 1 --xunit-output .out/test-results/xunit.xml -Xswiftc -warnings-as-errors
name: swift test
- run:
command: make circleci-config
- run:
command: make test-snippets
- run:
command: make test-pkl
- run:
command: make generate-fixtures
- store_test_results:
path: .out/test-results/
resource_class: xlarge
docker:
- image: swift:5.9-rhel-ubi9
test-license-headers:
steps:
- checkout
- run:
command: /bin/hawkeye check --fail-if-unknown
docker:
- image: ghcr.io/korandoru/hawkeye
test-format:
steps:
- checkout
- run:
command: make swiftformat-lint
docker:
- image: swift:5.9-rhel-ubi9
pkl-gen-swift-macos:
steps:
- checkout
@ -82,13 +126,13 @@ jobs:
- checkout
- run:
command: |-
mkdir /tmp/pkl
curl -L "https://repo1.maven.org/maven2/org/pkl-lang/pkl-cli-linux-amd64/0.25.1/pkl-cli-linux-amd64-0.25.1.bin" > /tmp/pkl/pkl
chmod +x /tmp/pkl/pkl
echo 'export PKL_EXEC=/tmp/pkl/pkl' >> $BASH_ENV
name: Downloading pkl
PKL=$(mktemp /tmp/pkl-0.27.2-XXXXXX)
curl -L "https://github.com/apple/pkl/releases/download/0.27.2/pkl-linux-amd64" > $PKL
chmod +x $PKL
echo "export PKL_EXEC=$PKL" >> $BASH_ENV
name: Downloading pkl-0.27.2
- run:
command: $PKL_EXEC project package --output-path out/pkl-package/ codegen/src/
command: $PKL_EXEC project package --skip-publish-check --output-path out/pkl-package/ codegen/src/
- persist_to_workspace:
root: '.'
paths:
@ -114,7 +158,7 @@ jobs:
echo "Creating release for Pkl package"
gh release create "pkl.swift@${CIRCLE_TAG}" \
--title "Pkl package ${CIRCLE_TAG}" \
--title "pkl.swift@${CIRCLE_TAG}" \
--target "${CIRCLE_SHA1}" \
--notes "This holds the release assets for the pkl.swift Pkl package." \
--repo "${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}" \
@ -125,7 +169,7 @@ jobs:
--title "${CIRCLE_TAG}" \
--target "${CIRCLE_SHA1}" \
--verify-tag \
--notes "Release notes: https://pkl-lang.org/swift/CHANGELOG.html#release-${CIRCLE_TAG}" \
--notes "Release notes: https://pkl-lang.org/swift/current/CHANGELOG.html#release-${CIRCLE_TAG}" \
--repo "${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}" \
out/pkl-gen-swift/*
name: Do release
@ -164,24 +208,53 @@ workflows:
type: approval
- pr-approval/authenticate:
context: pkl-pr-approval
- test:
- test-pkl-0-25-3:
requires:
- hold
- test-pkl-0-27-2:
requires:
- hold
- test-license-headers:
requires:
- hold
- test-format:
requires:
- hold
- pr-approval/authenticate
when:
matches:
value: << pipeline.git.branch >>
pattern: ^pull/\d+(/head)?$
main:
jobs:
- test
- test-pkl-0-25-3
- test-pkl-0-27-2
- test-license-headers
- test-format
when:
equal:
- main
- << pipeline.git.branch >>
release:
jobs:
- test:
- test-pkl-0-25-3:
filters:
branches:
ignore: /.*/
tags:
only: /^v?\d+\.\d+\.\d+$/
- test-pkl-0-27-2:
filters:
branches:
ignore: /.*/
tags:
only: /^v?\d+\.\d+\.\d+$/
- test-license-headers:
filters:
branches:
ignore: /.*/
tags:
only: /^v?\d+\.\d+\.\d+$/
- test-format:
filters:
branches:
ignore: /.*/
@ -189,7 +262,10 @@ workflows:
only: /^v?\d+\.\d+\.\d+$/
- pkl-package:
requires:
- test
- test-pkl-0-25-3
- test-pkl-0-27-2
- test-license-headers
- test-format
filters:
branches:
ignore: /.*/
@ -197,7 +273,10 @@ workflows:
only: /^v?\d+\.\d+\.\d+$/
- pkl-gen-swift-macos:
requires:
- test
- test-pkl-0-25-3
- test-pkl-0-27-2
- test-license-headers
- test-format
filters:
branches:
ignore: /.*/
@ -205,7 +284,10 @@ workflows:
only: /^v?\d+\.\d+\.\d+$/
- pkl-gen-swift-linux-amd64:
requires:
- test
- test-pkl-0-25-3
- test-pkl-0-27-2
- test-license-headers
- test-format
filters:
branches:
ignore: /.*/
@ -213,7 +295,10 @@ workflows:
only: /^v?\d+\.\d+\.\d+$/
- pkl-gen-swift-linux-aarch64:
requires:
- test
- test-pkl-0-25-3
- test-pkl-0-27-2
- test-license-headers
- test-format
filters:
branches:
ignore: /.*/

4
.gitignore vendored
View File

@ -1,5 +1,6 @@
.DS_Store
.build
.index-build
/Packages
/*.xcodeproj
xcuserdata/
@ -16,4 +17,5 @@ DerivedData/
.out/
out/
.cicd/
.cicd/
.pkl-lsp/

4
.spi.yml Normal file
View File

@ -0,0 +1,4 @@
version: 1
builder:
configs:
- documentation_targets: ['PklSwift']

View File

@ -15,9 +15,8 @@
--guardelse same-line
--nevertrailing filter
--extensionacl on-declarations
--header "// ===----------------------------------------------------------------------===//\n// Copyright © {year} Apple Inc. and the Pkl project authors. All rights reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// https://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// ===----------------------------------------------------------------------===//"
# rules
--disable spaceAroundOperators
--disable wrapMultilineStatementBraces
--disable wrapMultilineStatementBraces

31
DEVELOPMENT.adoc Normal file
View File

@ -0,0 +1,31 @@
= Development
== Running the formatter
This project uses two formatting tools: https://github.com/korandoru/hawkeye[hawkeye] and https://github.com/nicklockwood/SwiftFormat[SwiftFormat].
To install swiftformat:
[source,shell]
----
brew install swiftformat
----
To install hawkeye locally, you will need to install the https://rustup.rs[Rust toolchain], then run:
[source,shell]
----
cargo install hawkeye
----
To run formatters:
[source,shell]
----
make format
# Alternatively, individually:
swiftformat .
hawkeye format
----

View File

@ -108,3 +108,19 @@ pkl-gen-swift-release: $(PKL_GEN_SWIFT_RELEASE)
.PHONY: pkl-gen-swift-release-output
pkl-gen-swift-release-output:
@echo "$(PKL_GEN_SWIFT_RELEASE)" | xargs
.PHONY: circleci-config
circleci-config:
$(PKL_EXEC) eval .circleci/config.pkl -o .circleci/config.yml
git diff --exit-code
swiftformat:
swift package plugin --allow-writing-to-package-directory swiftformat .
swiftformat-lint:
swift package plugin --allow-writing-to-package-directory swiftformat --lint .
license-format: .build/tools/hawkeye
.build/tools/hawkeye format
format: swiftformat license-format

View File

@ -462,4 +462,14 @@ Apache 2.0 - https://github.com/apple/swift-system/blob/main/LICENSE.txt
As an exception, if you use this Software to compile your source code and
portions of this Software are embedded into the binary product as a result,
you may redistribute such product without providing attribution as would
otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
---
This product contains a derivation of the lock implementation and various
scripts from SwiftNIO.
* LICENSE (Apache License 2.0):
* https://www.apache.org/licenses/LICENSE-2.0
* HOMEPAGE:
* https://github.com/apple/swift-nio

View File

@ -1,5 +1,14 @@
{
"pins" : [
{
"identity" : "semanticversion",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SwiftPackageIndex/SemanticVersion",
"state" : {
"revision" : "ea8eea9d89842a29af1b8e6c7677f1c86e72fa42",
"version" : "0.4.0"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
@ -9,6 +18,24 @@
"version" : "1.2.3"
}
},
{
"identity" : "swift-docc-plugin",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-docc-plugin",
"state" : {
"revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64",
"version" : "1.4.3"
}
},
{
"identity" : "swift-docc-symbolkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-docc-symbolkit",
"state" : {
"revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
"version" : "1.0.0"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
@ -17,6 +44,15 @@
"revision" : "025bcb1165deab2e20d4eaba79967ce73013f496",
"version" : "1.2.1"
}
},
{
"identity" : "swiftformat",
"kind" : "remoteSourceControl",
"location" : "https://github.com/nicklockwood/SwiftFormat",
"state" : {
"revision" : "468a7d32dedc8d352c191594b3b45d9fd8ba291b",
"version" : "0.55.5"
}
}
],
"version" : 2

View File

@ -1,4 +1,20 @@
// swift-tools-version: 5.9
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import PackageDescription
let package = Package(
@ -6,7 +22,6 @@ let package = Package(
platforms: [
// required because of `Duration` API
.macOS(.v13),
.iOS(.v13),
],
products: [
.library(
@ -25,11 +40,16 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/apple/swift-system", from: "1.2.1"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.3"),
.package(url: "https://github.com/SwiftPackageIndex/SemanticVersion", from: "0.4.0"),
// to enable `swift package generate-documentation --target PklSwift`
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0"),
// to enable `swift package plugin --allow-writing-to-package-directory swiftformat`
.package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.55.0"),
],
targets: [
.target(
name: "PklSwift",
dependencies: ["MessagePack", "PklSwiftInternals"]
dependencies: ["MessagePack", "PklSwiftInternals", "SemanticVersion"]
),
.target(
name: "PklSwiftInternals"
@ -48,6 +68,10 @@ let package = Package(
],
resources: [.embedInCode("Resources/VERSION.txt")]
),
.executableTarget(
name: "test-external-reader",
dependencies: ["PklSwift"]
),
.testTarget(
name: "PklSwiftTests",
dependencies: [

View File

@ -8,4 +8,4 @@ The full documentation for this library can be found on our link:https://pkl-lan
To get up and going, reference the link:https://pkl-lang.org/swift/current/quickstart.html[quick start guide].
When upgrading pkl-swift, reference the link:https://pkl-lang.org/pkl/swift/current/CHANGELOG.html[changelog] for details.
When upgrading pkl-swift, reference the link:https://pkl-lang.org/swift/current/CHANGELOG.html[changelog] for details.

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
struct AnyCodingKey: CodingKey, Equatable {
let stringValue: String

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
@ -185,7 +185,6 @@ extension MessagePackValue {
case .ext:
// TODO: implement this?
fatalError("Cannot convert \(self) to \(T.self)")
case .timestamp:
// TODO: implement this?
fatalError("Cannot convert \(self) to \(T.self)")

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation

View File

@ -1,22 +1,22 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
#if os(Linux)
#if os(Linux) || os(Windows)
let NSEC_PER_SEC: UInt64 = 1_000_000_000
#endif

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
@ -44,10 +44,13 @@ public final class MessagePackEncoder {
try Box<Data>(data).encode(to: encoder)
case let date as Date:
try Box<Date>(date).encode(to: encoder)
case let bytes as [UInt8]:
try Box<[UInt8]>(bytes).encode(to: encoder)
case let url as URL:
try Box<URL>(url).encode(to: encoder)
case let bytes as [UInt8]:
guard type(of: value) == [UInt8].self else {
fallthrough
}
try Box<[UInt8]>(bytes).encode(to: encoder)
default:
try value.encode(to: encoder)
}

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
@ -304,8 +304,6 @@ extension _MessagePackEncoder.SingleValueContainer: SingleValueEncodingContainer
try self.encode(data)
case let date as Date:
try self.encode(date)
case let bytes as [UInt8]:
try self.encode(bytes)
case let url as URL:
try self.encode(url)
case let bool as Bool:
@ -334,9 +332,16 @@ extension _MessagePackEncoder.SingleValueContainer: SingleValueEncodingContainer
try self.encode(uint64)
case let num as any BinaryInteger & Encodable:
try self.encode(num)
case let bytes as [UInt8]:
guard type(of: value) == [UInt8].self else {
fallthrough
}
try self.encode(bytes)
default:
let writer: Writer = BufferWriter()
let encoder = _MessagePackEncoder()
encoder.userInfo = userInfo
encoder.codingPath = codingPath
try value.encode(to: encoder)
try encoder.write(into: writer)
let data = (writer as! BufferWriter).bytes

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
extension FixedWidthInteger {
init(bytes: [UInt8]) {

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
public protocol Writer {
/// Write the given bytes into an output somewhere.

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
import MessagePack

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation

View File

@ -1,45 +0,0 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
import Foundation
/// Pair is the Swift representation of Pkl's `pkl.Pair`.
public struct Pair<A, B>: Hashable where A: Hashable, B: Hashable {
public init(_ first: A, _ second: B) {
self.first = first
self.second = second
}
/// The value of the first component of this ``Pair``.
let first: A
/// The value of the second component of this ``Pair``.
let second: B
}
extension Pair: CustomStringConvertible where A: CustomStringConvertible, B: CustomStringConvertible {
public var description: String {
"Pair(\(self.first.description), \(self.second.description))"
}
}
extension Pair: Decodable where A: Decodable, B: Decodable {
public init(from decoder: Decoder) throws {
var partsDecoder = try decoder.unkeyedContainer()
self.first = try partsDecoder.decode(A.self)
self.second = try partsDecoder.decode(B.self)
}
}

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
import MessagePack
@ -205,6 +205,7 @@ extension _PklDecoder {
))
}
var arr: [AnyHashable?] = []
arr.reserveCapacity(pklArray.count)
for v in pklArray {
try arr.append(self.decodePolymorphic(v, codingPath: codingPath)?.value)
}

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import MessagePack

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import MessagePack
@ -37,11 +37,13 @@ class PklSingleValueDecodingContainer: SingleValueDecodingContainer {
switch self.value {
case .bool(let value): return value
default:
throw DecodingError.dataCorrupted(
throw DecodingError.typeMismatch(
Bool.self,
.init(
codingPath: self.codingPath,
debugDescription: "Expected a boolean, but got \(self.value.debugDataTypeDescription)"
))
)
)
}
}
@ -49,11 +51,13 @@ class PklSingleValueDecodingContainer: SingleValueDecodingContainer {
switch self.value {
case .string(let value): return value
default:
throw DecodingError.dataCorrupted(
throw DecodingError.typeMismatch(
String.self,
.init(
codingPath: self.codingPath,
debugDescription: "Expected a string, but got \(self.value.debugDataTypeDescription)"
))
)
)
}
}
@ -61,11 +65,13 @@ class PklSingleValueDecodingContainer: SingleValueDecodingContainer {
switch self.value {
case .float(let value): return Double(value)
default:
throw DecodingError.dataCorrupted(
throw DecodingError.typeMismatch(
Double.self,
.init(
codingPath: self.codingPath,
debugDescription: "Expected a float, but got \(self.value.debugDataTypeDescription)"
))
)
)
}
}
@ -73,11 +79,13 @@ class PklSingleValueDecodingContainer: SingleValueDecodingContainer {
switch self.value {
case .float(let value): return Float(value)
default:
throw DecodingError.dataCorrupted(
throw DecodingError.typeMismatch(
Float.self,
.init(
codingPath: self.codingPath,
debugDescription: "Expected a float, but got \(self.value.debugDataTypeDescription)"
))
)
)
}
}
@ -124,7 +132,7 @@ class PklSingleValueDecodingContainer: SingleValueDecodingContainer {
func decode<T>(_ type: T.Type) throws -> T where T: Decodable {
if type == PklAny.self {
guard let result = try _PklDecoder.decodePolymorphic(value, codingPath: codingPath) else {
throw DecodingError.dataCorrupted(.init(codingPath: self.codingPath, debugDescription: "Tried to decode but got nil"))
throw DecodingError.typeMismatch(T.self, .init(codingPath: self.codingPath, debugDescription: "Tried to decode but got nil"))
}
return result as! T
}
@ -135,13 +143,25 @@ class PklSingleValueDecodingContainer: SingleValueDecodingContainer {
private func decodeBinaryInteger<T>() throws -> T where T: BinaryInteger {
switch self.value {
case .int(let value): return T(value)
case .int(let value):
guard let result = T(exactly: value) else {
throw DecodingError.typeMismatch(
T.self,
.init(
codingPath: self.codingPath,
debugDescription: "Cannot fit \(value) into \(String(describing: T.self))"
)
)
}
return result
default:
throw DecodingError.dataCorrupted(
throw DecodingError.typeMismatch(
T.self,
.init(
codingPath: self.codingPath,
debugDescription: "Expected an int, but got \(self.value.debugDataTypeDescription)"
))
)
)
}
}
}

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import MessagePack

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import MessagePack
@ -49,11 +49,6 @@ extension _PklDecoder {
throw error("Expected an array at slot 2, but got \(value[1].debugDataTypeDescription)")
}
self.members = members
} else if value.count == 3 {
guard pklType == .pair else {
throw error("Expected to find a Pair type marker, but found \(pklType)")
}
self.members = Array(value[1...2])
} else {
throw error("Expected 2 or 3 values, but found \(value.count)")
}

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
@ -36,17 +36,14 @@ public func withEvaluator<T>(_ action: (Evaluator) async throws -> T) async thro
try await withEvaluator(options: .preconfigured, action)
}
/// Like ``withProjectEvaluator(projectDir:options:_:)``, but configured with preconfigured otions.
/// Like ``withProjectEvaluator(projectBaseURI:options:_:)``, but configured with preconfigured options.
///
/// - Parameters:
/// - projectDir: The directory containing the PklProject file.
/// - projectBaseURI: The base path containing the PklProject file.
/// - action: The action to perform.
/// - Returns: The result of the action.
public func withProjectEvaluator<T>(
projectDir: String,
_ action: (Evaluator) async throws -> T
) async throws -> T {
try await withProjectEvaluator(projectDir: projectDir, options: .preconfigured, action)
public func withProjectEvaluator<T>(projectBaseURI: URL, _ action: (Evaluator) async throws -> T) async throws -> T {
try await withProjectEvaluator(projectBaseURI: projectBaseURI, options: .preconfigured, action)
}
/// Convenience method for initializing an evaluator from the project.
@ -56,17 +53,17 @@ public func withProjectEvaluator<T>(
///
/// After `action` completes, the evaluator is closed.
/// - Parameters:
/// - projectDir: The directory containing the PklProject file.
/// - projectBaseURI: The base path containing the PklProject file.
/// - options: The base options used to configure the evaluator.
/// - action: The action to perform.
/// - Returns: The result of the action.
public func withProjectEvaluator<T>(
projectDir: String,
projectBaseURI: URL,
options: EvaluatorOptions,
_ action: (Evaluator) async throws -> T
) async throws -> T {
try await withEvaluatorManager { manager in
let evaluator = try await manager.newProjectEvaluator(projectDir: projectDir, options: options)
let evaluator = try await manager.newProjectEvaluator(projectBaseURI: projectBaseURI, options: options)
return try await action(evaluator)
}
}

View File

@ -1,22 +1,31 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
import MessagePack
import SemanticVersion
#if os(Windows)
import WinSDK.System
let ENV_SEPARATOR=";"
let PKL_EXEC_NAME="pkl.exe"
#else
let ENV_SEPARATOR=":"
let PKL_EXEC_NAME="pkl"
#endif
/// Perfoms `action`, returns its result and then closes the manager.
///
/// - Parameter action: The action to perform
@ -37,6 +46,47 @@ public func withEvaluatorManager<T>(_ action: (EvaluatorManager) async throws ->
}
}
func getenv(_ key: String) -> String? {
#if os(Windows)
let key = key.lowercased()
return ProcessInfo.processInfo.environment.first { (envKey: String, _: String) in
key == envKey.lowercased()
}?.value
#else
return ProcessInfo.processInfo.environment[key]
#endif
}
/// Resolve the (CLI) command to invoke Pkl.
///
/// First, checks the `PKL_EXEC` environment variable. If that is not set, searches the `PATH` for a directory
/// containing `pkl`.
func getPklCommand() throws -> [String] {
if let exec = getenv("PKL_EXEC") {
return exec.components(separatedBy: " ")
}
guard let path = getenv("PATH") else {
throw PklError("Unable to read PATH environment variable.")
}
for dir in path.components(separatedBy: ENV_SEPARATOR) {
do {
let contents = try FileManager.default.contentsOfDirectory(atPath: dir)
if let pkl = contents.first(where: { $0 == PKL_EXEC_NAME }) {
let file = NSString.path(withComponents: [dir, pkl])
if FileManager.default.isExecutableFile(atPath: file) {
return [file]
}
}
} catch {
if error._domain == NSCocoaErrorDomain {
continue
}
throw error
}
}
throw PklError("Unable to find `pkl` command on PATH.")
}
/// Provides handlers for managing the lifecycles of Pkl evaluators. If binding to Pkl as a child process, an evaluator
/// manager represents a single child process.
///
@ -55,9 +105,11 @@ public actor EvaluatorManager {
var isClosed: Bool = false
var pklVersion: String?
// note; when our C bindings are released, change `init()` based on compiler flags.
public init() {
self.init(transport: ChildProcessMessageTransport())
self.init(transport: ServerMessageTransport())
}
// Used for testing only.
@ -72,6 +124,31 @@ public actor EvaluatorManager {
}
}
/// Get the semantic version as a String of the Pkl interpreter being used.
func getVersion() throws -> String {
if let pklVersion {
return pklVersion
}
let pklCommand = try getPklCommand()
let process = Process()
process.executableURL = URL(fileURLWithPath: pklCommand[0])
process.arguments = Array(pklCommand.dropFirst()) + ["--version"]
let pipe = Pipe()
process.standardOutput = pipe
debug("Spawning command \(pklCommand[0]) with arguments \(process.arguments!)")
try process.run()
guard let outputData = try pipe.fileHandleForReading.readToEnd(),
let output = String(data: outputData, encoding: .utf8)?.split(separator: " "),
output.count > 2,
output[0] == "Pkl" else {
throw PklError("Could not get version from Pkl binary")
}
self.pklVersion = String(output[1])
return self.pklVersion!
}
private func listenForIncomingMessages() async throws {
for try await message in try self.transport.getMessages() {
debug("EvaluatorManager got message \(message)")
@ -127,24 +204,24 @@ public actor EvaluatorManager {
/// - options: The options used to configure the evaluator.
/// - action: The action to run with the evaluator.
public func withEvaluator<T>(options: EvaluatorOptions, _ action: (Evaluator) async throws -> T) async throws -> T {
let evalautor = try await newEvaluator(options: options)
let evaluator = try await newEvaluator(options: options)
var closed = false
do {
let result = try await action(evalautor)
try await evalautor.close()
let result = try await action(evaluator)
try await evaluator.close()
closed = true
return result
} catch {
if !closed {
try await evalautor.close()
try await evaluator.close()
}
throw error
}
}
/// Convenience method for constructing a project evaluator with preconfigured base options.
public func withProjectEvaluator<T>(projectDir: String, _ action: (Evaluator) async throws -> T) async throws -> T {
try await self.withProjectEvaluator(projectDir: projectDir, options: .preconfigured, action)
public func withProjectEvaluator<T>(projectBaseURI: URL, _ action: (Evaluator) async throws -> T) async throws -> T {
try await self.withProjectEvaluator(projectBaseURI: projectBaseURI, options: .preconfigured, action)
}
/// Constructs an evaluator that is configured by the project within the project dir.
@ -155,20 +232,20 @@ public actor EvaluatorManager {
/// After the action completes or throws, the evaluator is closed.
///
/// - Parameters:
/// - projectDir: The project directory that contains the PklProject file.
/// - projectBaseURI: The project base path that contains the PklProject file.
/// - options: The options used to configure the evaluator.
/// - action: The action to run with the evaluator.
public func withProjectEvaluator<T>(projectDir: String, options: EvaluatorOptions, _ action: (Evaluator) async throws -> T) async throws -> T {
let evalautor = try await newProjectEvaluator(projectDir: projectDir, options: options)
public func withProjectEvaluator<T>(projectBaseURI: URL, options: EvaluatorOptions, _ action: (Evaluator) async throws -> T) async throws -> T {
let evaluator = try await newProjectEvaluator(projectBaseURI: projectBaseURI, options: options)
var closed = false
do {
let result = try await action(evalautor)
try await evalautor.close()
let result = try await action(evaluator)
try await evaluator.close()
closed = true
return result
} catch {
if !closed {
try await evalautor.close()
try await evaluator.close()
}
throw error
}
@ -176,13 +253,21 @@ public actor EvaluatorManager {
/// Creates a new evaluator with the provided options.
///
/// To create an evaluator that understands project dependencies, use ``newProjectEvaluator(projectDir:options:)``.
/// To create an evaluator that understands project dependencies, use
/// ``newProjectEvaluator(projectBaseURI:options:)``.
///
/// - Parameter options: The options used to configure the evaluator.
public func newEvaluator(options: EvaluatorOptions) async throws -> Evaluator {
if self.isClosed {
throw PklError("The evaluator manager is closed")
}
let version = try SemanticVersion(getVersion())!
guard options.http == nil || version >= pklVersion0_26 else {
throw PklError("http options are not supported on Pkl versions lower than 0.26")
}
guard (options.externalModuleReaders == nil && options.externalResourceReaders == nil) || version >= pklVersion0_27 else {
throw PklError("external reader options are not supported on Pkl versions lower than 0.27")
}
let req = options.toMessage()
guard let response = try await ask(req) as? CreateEvaluatorResponse else {
throw PklBugError.invalidMessageCode(
@ -209,15 +294,15 @@ public actor EvaluatorManager {
// Any `evaluatorSettings` set within the PklProject file overwrites any fields set on `options`.
///
/// - Parameters:
/// - projectDir: The project directory containing the `PklProject` file.
/// - projectBaseURI: The project base path containing the `PklProject` file.
/// - options: The base options used to configure the evaluator.
public func newProjectEvaluator(projectDir: String, options: EvaluatorOptions) async throws -> Evaluator {
public func newProjectEvaluator(projectBaseURI: URL, options: EvaluatorOptions) async throws -> Evaluator {
if self.isClosed {
throw PklError("The evaluator manager is closed")
}
return try await self.withEvaluator(options: .preconfigured) { projectEvaluator in
let project = try await projectEvaluator.evaluateOutputValue(
source: .path("\(projectDir)/PklProject"),
source: .path("\(projectBaseURI)/PklProject"),
asType: Project.self
)
return try await self.newEvaluator(options: options.withProject(project))
@ -287,3 +372,13 @@ enum PklBugError: Error {
case invalidEvaluatorId(String)
case unknownMessage(String)
}
let pklVersion0_25 = SemanticVersion("0.25.0")!
let pklVersion0_26 = SemanticVersion("0.26.0")!
let pklVersion0_27 = SemanticVersion("0.27.0")!
let supportedPklVersions = [
pklVersion0_25,
pklVersion0_26,
pklVersion0_27,
]

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
@ -30,8 +30,11 @@ public struct EvaluatorOptions {
cacheDir: String? = nil,
outputFormat: String? = nil,
logger: Logger = Loggers.noop,
projectDir: String? = nil,
declaredProjectDependencies: [String: ProjectDependency]? = nil
projectBaseURI: URL? = nil,
http: Http? = nil,
declaredProjectDependencies: [String: ProjectDependency]? = nil,
externalModuleReaders: [String: ExternalReader]? = nil,
externalResourceReaders: [String: ExternalReader]? = nil
) {
self.allowedModules = allowedModules
self.allowedResources = allowedResources
@ -45,8 +48,11 @@ public struct EvaluatorOptions {
self.cacheDir = cacheDir
self.outputFormat = outputFormat
self.logger = logger
self.projectDir = projectDir
self.projectBaseURI = projectBaseURI
self.http = http
self.declaredProjectDependencies = declaredProjectDependencies
self.externalModuleReaders = externalModuleReaders
self.externalResourceReaders = externalResourceReaders
}
/// Regular expression patterns that control what modules are allowed to be imported in a Pkl program.
@ -106,15 +112,33 @@ public struct EvaluatorOptions {
/// It is meant to be set by lower level logic in Swift code that first evaluates the PklProject,
/// which then configures ``EvaluatorOptions`` accordingly.
///
/// To emulate the CLI's `--project-dir` flag, create an evaluator with ``withProjectEvaluator(projectDir:_:)``,
/// To emulate the CLI's `--project-dir` flag, create an evaluator with ``withProjectEvaluator(projectBaseURI:_:)``,
/// or ``EvaluatorManager/newProjectEvaluator()``.
public var projectDir: String?
public var projectBaseURI: URL?
/// The set of dependencies available to modules within ``projectDir``.
/// Settings that control how Pkl talks to HTTP(S) servers.
///
/// When importing dependencies, a `PklProject.deps.json` file must exist within ``projectDir``
/// Added in Pkl 0.26.
/// These fields are ignored if targeting Pkl 0.25.
public var http: Http?
/// The set of dependencies available to modules within ``projectBaseURI``.
///
/// When importing dependencies, a `PklProject.deps.json` file must exist within ``projectBaseURI``
/// that contains the project's resolved dependencies.
public var declaredProjectDependencies: [String: ProjectDependency]?
/// Registered external commands that implement module reader schemes.
///
/// Added in Pkl 0.27.
/// If the underlying Pkl does not support external readers, evaluation will fail when a registered scheme is used.
public var externalModuleReaders: [String: ExternalReader]?
/// Registered external commands that implement resource reader schemes.
///
/// Added in Pkl 0.27.
/// If the underlying Pkl does not support external readers, evaluation will fail when a registered scheme is used.
public var externalResourceReaders: [String: ExternalReader]?
}
extension EvaluatorOptions {
@ -153,18 +177,21 @@ extension EvaluatorOptions {
rootDir: self.rootDir,
cacheDir: self.cacheDir,
outputFormat: self.outputFormat,
project: self.project()
project: self.project(),
http: self.http,
externalModuleReaders: self.externalModuleReaders,
externalResourceReaders: self.externalResourceReaders
)
}
func project() -> ProjectOrDependency? {
guard let projectDir else {
guard let projectBaseURI else {
return nil
}
return .init(
packageUri: nil,
type: "project",
projectFileUri: "file://\(projectDir)/PklProject",
projectFileUri: "file://\(projectBaseURI)/PklProject",
checksums: nil,
dependencies: self.declaredProjectDependenciesToMessage(self.declaredProjectDependencies)
)
@ -227,28 +254,17 @@ extension EvaluatorOptions {
/// Builds options that configures the evaluator with settings set on the project.
///
/// Skips any settings that are nil.
public func withProjectEvaluatorSettings(_ evaluatorSettings: Project.EvaluatorSettings) -> EvaluatorOptions {
public func withProjectEvaluatorSettings(_ evaluatorSettings: PklEvaluatorSettings) -> EvaluatorOptions {
var options = self
if evaluatorSettings.externalProperties != nil {
options.properties = evaluatorSettings.externalProperties
}
if evaluatorSettings.env != nil {
options.env = evaluatorSettings.env
}
if evaluatorSettings.allowedModules != nil {
options.allowedModules = evaluatorSettings.allowedModules
}
if evaluatorSettings.allowedResources != nil {
options.allowedResources = evaluatorSettings.allowedResources
}
if evaluatorSettings.noCache == true {
options.cacheDir = nil
} else if evaluatorSettings.moduleCacheDir != nil {
options.cacheDir = evaluatorSettings.moduleCacheDir
}
if evaluatorSettings.rootDir != nil {
options.rootDir = evaluatorSettings.rootDir
}
options.properties = evaluatorSettings.externalProperties ?? self.properties
options.env = evaluatorSettings.env ?? self.env
options.allowedModules = evaluatorSettings.allowedModules ?? self.allowedModules
options.allowedResources = evaluatorSettings.allowedResources ?? self.allowedResources
options.cacheDir = evaluatorSettings.noCache != nil ? nil : (evaluatorSettings.moduleCacheDir ?? self.cacheDir)
options.rootDir = evaluatorSettings.rootDir ?? self.rootDir
options.http = evaluatorSettings.http ?? self.http
options.externalModuleReaders = evaluatorSettings.externalModuleReaders ?? options.externalModuleReaders
options.externalResourceReaders = evaluatorSettings.externalResourceReaders ?? options.externalResourceReaders
return options
}
@ -277,7 +293,8 @@ extension EvaluatorOptions {
/// Builds options with dependencies from the input project.
public func withProjectDependencies(_ project: Project) -> EvaluatorOptions {
var options = self
options.projectDir = String(URL(string: project.projectFileUri)!.path.dropLast("/PklProject".count))
options.projectBaseURI = URL(string: project.projectFileUri)!
options.projectBaseURI?.deleteLastPathComponent()
options.declaredProjectDependencies = self.projectDependencies(project)
return options
}

View File

@ -0,0 +1,208 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
import MessagePack
// Example usage:
// import PklSwift
// @main
// struct Main {
// static func main() async throws {
// let client = ExternalReaderClient(
// options: ExternalReaderClientOptions(
// resourceReaders: [MyResourceReader()]
// ))
// try await client.run()
// }
// }
public struct ExternalReaderClientOptions {
/// Reader to receive requests.
public var requestReader: Reader = FileHandle.standardInput
/// Writer to publish responses.
public var responseWriter: Writer = FileHandle.standardOutput
/// Readers that allow reading custom resources in Pkl.
public var moduleReaders: [ModuleReader] = []
/// Readers that allow importing custom modules in Pkl.
public var resourceReaders: [ResourceReader] = []
public init(
requestReader: Reader = FileHandle.standardInput,
responseWriter: Writer = FileHandle.standardOutput,
moduleReaders: [ModuleReader] = [],
resourceReaders: [ResourceReader] = []
) {
self.requestReader = requestReader
self.responseWriter = responseWriter
self.moduleReaders = moduleReaders
self.resourceReaders = resourceReaders
}
}
public class ExternalReaderClient {
private let moduleReaders: [ModuleReader]
private let resourceReaders: [ResourceReader]
private let transport: MessageTransport
public init(options: ExternalReaderClientOptions) {
self.moduleReaders = options.moduleReaders
self.resourceReaders = options.resourceReaders
self.transport = ExternalReaderMessageTransport(
reader: options.requestReader, writer: options.responseWriter
)
}
public func run() async throws {
for try await message in try self.transport.getMessages() {
switch message {
case let message as InitializeModuleReaderRequest:
try self.handleInitializeModuleReaderRequest(request: message)
case let message as InitializeResourceReaderRequest:
try self.handleInitializeResourceReaderRequest(request: message)
case let message as ReadModuleRequest:
try await self.handleReadModuleRequest(request: message)
case let message as ReadResourceRequest:
try await self.handleReadResourceRequest(request: message)
case let message as ListModulesRequest:
try await self.handleListModulesRequest(request: message)
case let message as ListResourcesRequest:
try await self.handleListResourcesRequest(request: message)
case _ as CloseExternalProcess:
self.close()
default:
throw PklBugError.unknownMessage("Got request for unknown message: \(message)")
}
}
}
public func close() {
self.transport.close()
}
func handleInitializeModuleReaderRequest(request: InitializeModuleReaderRequest) throws {
var response = InitializeModuleReaderResponse(requestId: request.requestId, spec: nil)
guard let reader = moduleReaders.first(where: { $0.scheme == request.scheme }) else {
try self.transport.send(response)
return
}
response.spec = reader.toMessage()
try self.transport.send(response)
}
func handleInitializeResourceReaderRequest(request: InitializeResourceReaderRequest) throws {
var response = InitializeResourceReaderResponse(requestId: request.requestId, spec: nil)
guard let reader = resourceReaders.first(where: { $0.scheme == request.scheme }) else {
try self.transport.send(response)
return
}
response.spec = reader.toMessage()
try self.transport.send(response)
}
func handleReadModuleRequest(request: ReadModuleRequest) async throws {
var response = ReadModuleResponse(
requestId: request.requestId,
evaluatorId: request.evaluatorId,
contents: nil,
error: nil
)
guard let reader = moduleReaders.first(where: { $0.scheme == request.uri.scheme }) else {
response.error = "No module reader found for scheme \(request.uri.scheme!)"
try self.transport.send(response)
return
}
do {
let result = try await reader.read(url: request.uri)
response.contents = result
try self.transport.send(response)
} catch {
response.error = "\(error)"
try self.transport.send(response)
}
}
func handleReadResourceRequest(request: ReadResourceRequest) async throws {
var response = ReadResourceResponse(
requestId: request.requestId,
evaluatorId: request.evaluatorId,
contents: nil,
error: nil
)
guard
let reader = resourceReaders.first(where: { $0.scheme == request.uri.scheme }) else {
response.error = "No resource reader found for scheme \(request.uri.scheme!)"
try self.transport.send(response)
return
}
do {
let result = try await reader.read(url: request.uri)
response.contents = result
try self.transport.send(response)
} catch {
response.error = "\(error)"
try self.transport.send(response)
}
}
func handleListModulesRequest(request: ListModulesRequest) async throws {
var response = ListModulesResponse(
requestId: request.requestId,
evaluatorId: request.evaluatorId,
pathElements: nil,
error: nil
)
guard let reader = moduleReaders.first(where: { $0.scheme == request.uri.scheme }) else {
response.error = "No module reader found for scheme \(request.uri.scheme!)"
try self.transport.send(response)
return
}
do {
let elems = try await reader.listElements(uri: request.uri)
response.pathElements = elems.map { $0.toMessage() }
try self.transport.send(response)
} catch {
response.error = "\(error)"
try self.transport.send(response)
}
}
func handleListResourcesRequest(request: ListResourcesRequest) async throws {
var response = ListResourcesResponse(
requestId: request.requestId,
evaluatorId: request.evaluatorId,
pathElements: nil,
error: nil
)
guard
let reader = resourceReaders.first(where: { $0.scheme == request.uri.scheme }) else {
response.error = "No resource reader found for scheme \(request.uri.scheme!)"
try self.transport.send(response)
return
}
do {
let elems = try await reader.listElements(uri: request.uri)
response.pathElements = elems.map { $0.toMessage() }
try self.transport.send(response)
} catch {
response.error = "\(error)"
try self.transport.send(response)
}
}
}

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
import MessagePack

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
import MessagePack
@ -62,8 +62,8 @@ enum MessageType: Int, Codable {
case CREATE_EVALUATOR_REQUEST = 0x20
case CREATE_EVALUATOR_RESPONSE = 0x21
case CLOSE_EVALUATOR = 0x22
case EVALUATOR_REQUEST = 0x23
case EVALUATOR_RESPOSNE = 0x24
case EVALUATE_REQUEST = 0x23
case EVALUATE_RESPONSE = 0x24
case LOG_MESSAGE = 0x25
case READ_RESOURCE_REQUEST = 0x26
case READ_RESOURCE_RESPONSE = 0x27
@ -73,6 +73,11 @@ enum MessageType: Int, Codable {
case LIST_RESOURCES_RESPONSE = 0x2B
case LIST_MODULES_REQUEST = 0x2C
case LIST_MODULES_RESPONSE = 0x2D
case INITIALIZE_MODULE_READER_REQUEST = 0x2E
case INITIALIZE_MODULE_READER_RESPONSE = 0x2F
case INITIALIZE_RESOURCE_READER_REQUEST = 0x30
case INITIALIZE_RESOURCE_READER_RESPONSE = 0x31
case CLOSE_EXTERNAL_PROCESS = 0x32
}
extension MessageType {
@ -85,9 +90,9 @@ extension MessageType {
case is CloseEvaluatorRequest:
return MessageType.CLOSE_EVALUATOR
case is EvaluateRequest:
return MessageType.EVALUATOR_REQUEST
return MessageType.EVALUATE_REQUEST
case is EvaluateResponse:
return MessageType.EVALUATOR_RESPOSNE
return MessageType.EVALUATE_RESPONSE
case is LogMessage:
return MessageType.LOG_MESSAGE
case is ListResourcesRequest:
@ -102,6 +107,16 @@ extension MessageType {
return MessageType.READ_MODULE_RESPONSE
case is ReadResourceResponse:
return MessageType.READ_RESOURCE_RESPONSE
case is InitializeModuleReaderRequest:
return MessageType.INITIALIZE_MODULE_READER_REQUEST
case is InitializeModuleReaderResponse:
return MessageType.INITIALIZE_MODULE_READER_RESPONSE
case is InitializeResourceReaderRequest:
return MessageType.INITIALIZE_RESOURCE_READER_REQUEST
case is InitializeResourceReaderResponse:
return MessageType.INITIALIZE_RESOURCE_READER_RESPONSE
case is CloseExternalProcess:
return MessageType.CLOSE_EXTERNAL_PROCESS
default:
preconditionFailure("Unreachable code")
}
@ -122,6 +137,9 @@ struct CreateEvaluatorRequest: ClientRequestMessage {
var cacheDir: String?
var outputFormat: String?
var project: ProjectOrDependency?
var http: Http?
var externalModuleReaders: [String: ExternalReader]?
var externalResourceReaders: [String: ExternalReader]?
}
struct ProjectOrDependency: Codable {
@ -230,3 +248,25 @@ struct LogMessage: ServerOneWayMessage {
// NOTE: not guaranteed to conform to URL. This might have been transformed by a stack frame transformer.
let frameUri: String
}
struct InitializeModuleReaderRequest: ServerRequestMessage {
var requestId: Int64
let scheme: String
}
struct InitializeModuleReaderResponse: ClientResponseMessage {
var requestId: Int64
var spec: ModuleReaderSpec?
}
struct InitializeResourceReaderRequest: ServerRequestMessage {
var requestId: Int64
let scheme: String
}
struct InitializeResourceReaderResponse: ClientResponseMessage {
var requestId: Int64
var spec: ResourceReaderSpec?
}
struct CloseExternalProcess: ServerOneWayMessage {}

View File

@ -1,21 +1,22 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
import MessagePack
import SemanticVersion
protocol MessageTransport {
/// Send a message to the Pkl server.
@ -30,35 +31,104 @@ protocol MessageTransport {
extension Pipe: Reader {
public func read(into: UnsafeMutableRawBufferPointer) throws -> Int {
let data = try fileHandleForReading.read(upToCount: into.count)
if data == nil {
return 0
}
data!.copyBytes(to: into)
return data!.count
try fileHandleForReading.read(into: into)
}
public func close() throws {
fileHandleForReading.closeFile()
try fileHandleForReading.close()
}
}
extension FileHandle: Reader {
public func read(into: UnsafeMutableRawBufferPointer) throws -> Int {
guard let data = try read(upToCount: into.count) else { return 0 }
data.copyBytes(to: into)
return data.count
}
public func close() throws {
closeFile()
}
}
extension Pipe: Writer {
public func write(_ buffer: UnsafeRawBufferPointer) throws {
try fileHandleForWriting.write(contentsOf: buffer)
try fileHandleForWriting.write(buffer)
}
}
extension FileHandle: Writer {
public func write(_ buffer: UnsafeRawBufferPointer) throws {
try self.write(contentsOf: buffer)
}
}
/// A ``MessageTransport`` base class that implements core message handling logic
public class BaseMessageTransport: MessageTransport {
var reader: Reader!
var writer: Writer!
var encoder: MessagePackEncoder!
var decoder: MessagePackDecoder!
var running: Bool { true }
func send(_ message: ClientMessage) throws {
debug("Sending message: \(message)")
let messageType = MessageType.getMessageType(message)
try self.encoder.encodeArrayHeader(2)
try self.encoder.encode(messageType)
try self.encoder.encode(message)
}
fileprivate func decodeMessage(_ messageType: MessageType) throws -> ServerMessage {
switch messageType {
case MessageType.READ_MODULE_REQUEST:
return try self.decoder.decode(as: ReadModuleRequest.self)
case MessageType.READ_RESOURCE_REQUEST:
return try self.decoder.decode(as: ReadResourceRequest.self)
case MessageType.LIST_MODULES_REQUEST:
return try self.decoder.decode(as: ListModulesRequest.self)
case MessageType.LIST_RESOURCES_REQUEST:
return try self.decoder.decode(as: ListResourcesRequest.self)
default:
throw PklBugError.unknownMessage("Received unexpected message: \(messageType)")
}
}
func close() {}
func getMessages() throws -> AsyncThrowingStream<ServerMessage, Error> {
AsyncThrowingStream { continuation in
Task {
while self.running {
do {
let arrayLength = try decoder.decodeArrayLength()
assert(arrayLength == 2)
let code = try decoder.decode(as: MessageType.self)
let message = try decodeMessage(code)
debug("Received message: \(message)")
continuation.yield(message)
} catch {
if self.running {
continuation.finish(throwing: error)
}
}
}
continuation.finish()
}
}
}
}
/// A ``MessageTransport`` that sends and receives messages by spawning Pkl as a child process.
public class ChildProcessMessageTransport: MessageTransport {
var reader: Pipe!
var writer: Pipe!
var encoder: MessagePackEncoder!
var decoder: MessagePackDecoder!
public class ServerMessageTransport: BaseMessageTransport {
var process: Process?
let pklCommand: [String]?
convenience init() {
override var running: Bool { self.process?.isRunning == true }
override convenience init() {
self.init(pklCommand: nil)
}
@ -66,32 +136,6 @@ public class ChildProcessMessageTransport: MessageTransport {
self.pklCommand = pklCommand
}
private func getPklCommand() throws -> [String] {
if let exec = ProcessInfo.processInfo.environment["PKL_EXEC"] {
return exec.components(separatedBy: " ")
}
guard let path = ProcessInfo.processInfo.environment["PATH"] else {
throw PklError("Unable to find `pkl` command on PATH.")
}
for dir in path.components(separatedBy: ":") {
do {
let contents = try FileManager.default.contentsOfDirectory(atPath: dir)
if let pkl = contents.first(where: { $0 == "pkl" }) {
let file = NSString.path(withComponents: [dir, pkl])
if FileManager.default.isExecutableFile(atPath: file) {
return [file]
}
}
} catch {
if error._domain == NSCocoaErrorDomain {
continue
}
throw error
}
}
throw PklError("Unable to find `pkl` command on PATH.")
}
private func ensureProcessStarted() throws {
if self.process?.isRunning == true { return }
let pklCommand = try getPklCommand()
@ -100,8 +144,8 @@ public class ChildProcessMessageTransport: MessageTransport {
var arguments = Array(pklCommand.dropFirst())
arguments.append("server")
self.process!.arguments = arguments
self.reader = .init()
self.writer = .init()
self.reader = Pipe()
self.writer = Pipe()
self.encoder = .init(writer: self.writer)
self.decoder = .init(reader: self.reader)
self.process!.standardOutput = self.reader
@ -110,17 +154,25 @@ public class ChildProcessMessageTransport: MessageTransport {
try self.process!.run()
}
func send(_ message: ClientMessage) throws {
override func send(_ message: ClientMessage) throws {
try self.ensureProcessStarted()
debug("Sending message: \(message)")
let messageType = MessageType.getMessageType(message)
try self.encoder.encodeArrayHeader(2)
try self.encoder.encode(messageType)
try self.encoder.encode(message)
try super.send(message)
}
func close() {
override fileprivate func decodeMessage(_ messageType: MessageType) throws -> ServerMessage {
switch messageType {
case MessageType.CREATE_EVALUATOR_RESPONSE:
return try self.decoder.decode(as: CreateEvaluatorResponse.self)
case MessageType.EVALUATE_RESPONSE:
return try self.decoder.decode(as: EvaluateResponse.self)
case MessageType.LOG_MESSAGE:
return try self.decoder.decode(as: LogMessage.self)
default:
return try super.decodeMessage(messageType)
}
}
override func close() {
if self.process == nil {
return
}
@ -136,45 +188,38 @@ public class ChildProcessMessageTransport: MessageTransport {
self.process = nil
}
private func decodeMessage(_ messageType: MessageType) throws -> ServerMessage {
override func getMessages() throws -> AsyncThrowingStream<ServerMessage, Error> {
try self.ensureProcessStarted()
return try super.getMessages()
}
}
public class ExternalReaderMessageTransport: BaseMessageTransport {
override var running: Bool { self._running }
private var _running = true
init(reader: Reader, writer: Writer) {
super.init()
self.reader = reader
self.writer = writer
self.encoder = .init(writer: self.writer)
self.decoder = .init(reader: self.reader)
}
override fileprivate func decodeMessage(_ messageType: MessageType) throws -> ServerMessage {
switch messageType {
case MessageType.CREATE_EVALUATOR_RESPONSE:
return try self.decoder.decode(as: CreateEvaluatorResponse.self)
case MessageType.EVALUATOR_RESPOSNE:
return try self.decoder.decode(as: EvaluateResponse.self)
case MessageType.READ_MODULE_REQUEST:
return try self.decoder.decode(as: ReadModuleRequest.self)
case MessageType.LOG_MESSAGE:
return try self.decoder.decode(as: LogMessage.self)
case MessageType.READ_RESOURCE_REQUEST:
return try self.decoder.decode(as: ReadResourceRequest.self)
case MessageType.LIST_MODULES_REQUEST:
return try self.decoder.decode(as: ListModulesRequest.self)
case MessageType.LIST_RESOURCES_REQUEST:
return try self.decoder.decode(as: ListResourcesRequest.self)
case MessageType.INITIALIZE_MODULE_READER_REQUEST:
return try self.decoder.decode(as: InitializeModuleReaderRequest.self)
case MessageType.INITIALIZE_RESOURCE_READER_REQUEST:
return try self.decoder.decode(as: InitializeResourceReaderRequest.self)
case MessageType.CLOSE_EXTERNAL_PROCESS:
return try self.decoder.decode(as: CloseExternalProcess.self)
default:
fatalError("Unreachable code")
return try super.decodeMessage(messageType)
}
}
func getMessages() throws -> AsyncThrowingStream<ServerMessage, Error> {
try self.ensureProcessStarted()
return AsyncThrowingStream { continuation in
Task {
while self.process?.isRunning == true {
do {
let arrayLength = try decoder.decodeArrayLength()
assert(arrayLength == 2)
let code = try decoder.decode(as: MessageType.self)
let message = try decodeMessage(code)
debug("Received message: \(message)")
continuation.yield(message)
} catch {
continuation.finish(throwing: error)
}
}
continuation.finish()
}
}
override func close() {
self._running = false
}
}

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
@ -36,7 +36,7 @@ extension ModuleSource {
/// - Parameter path: The file path component.
public static func path(_ path: String) -> ModuleSource {
let path = resolvePaths(path)
return ModuleSource(uri: URL(string: "file://\(path)")!, text: nil)
return ModuleSource(uri: URL(fileURLWithPath: path), text: nil)
}
/// Creates a synthetic ``ModuleSource`` with the given text.

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation

View File

@ -0,0 +1,114 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
/// The Swift representation of standard library module `pkl.EvaluatorSettings`.
public struct PklEvaluatorSettings: Decodable, Hashable {
let externalProperties: [String: String]?
let env: [String: String]?
let allowedModules: [String]?
let allowedResources: [String]?
let noCache: Bool?
let modulePath: [String]?
let timeout: Duration?
let moduleCacheDir: String?
let rootDir: String?
let http: Http?
/// Added in Pkl 0.27
let externalModuleReaders: [String: ExternalReader]?
/// Added in Pkl 0.27
let externalResourceReaders: [String: ExternalReader]?
/// Whether to format messages and test results with ANSI color colors.
///
/// Added in Pkl 0.27
let color: PklEvaluatorSettingsColor?
}
public enum PklEvaluatorSettingsColor: String, CaseIterable, Decodable, Hashable {
/// Never format.
case never
/// Format if the process' stdin, stdout, or stderr are connected to a console.
case auto
/// Always format.
case always
}
/// Settings that control how Pkl talks to HTTP(S) servers.
public struct Http: Codable, Hashable {
/// PEM format certificates to trust when making HTTP requests.
///
/// If empty, Pkl will trust its own built-in certificates.
var caCertificates: [UInt8]?
/// Configuration of the HTTP proxy to use.
///
/// If `nil`, uses the operating system's proxy configuration.
/// Configuration of the HTTP proxy to use.
var proxy: Proxy?
}
/// Settings that control how Pkl talks to HTTP proxies.
public struct Proxy: Codable, Hashable {
/// The proxy to use for HTTP(S) connections.
///
/// Only HTTP proxies are supported.
/// The address must start with `"http://"`, and cannot contain anything other than a host and an optional port.
///
/// Example:
/// ```
/// "http://my.proxy.example.com:5080"
/// ```
var address: String?
/// Hosts to which all connections should bypass a proxy.
///
///
/// Values can be either hostnames, or IP addresses.
/// IP addresses can optionally be provided using
/// [CIDR notation](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation).
///
/// The value `"*"` is a wildcard that disables proxying for all hosts.
///
/// A hostname matches all subdomains.
/// For example, `example.com` matches `foo.example.com`, but not `fooexample.com`.
/// A hostname that is prefixed with a dot matches the hostname itself,
/// so `.example.com` matches `example.com`.
///
/// Hostnames do not match their resolved IP addresses.
/// For example, the hostname `localhost` will not match `127.0.0.1`.
///
/// Optionally, a port can be specified.
/// If a port is omitted, all ports are matched.
///
/// Example:
///
/// ```
/// [ "127.0.0.1",
/// "169.254.0.0/16",
/// "example.com",
/// "localhost:5050" ]
/// ```
var noProxy: [String]?
}
public struct ExternalReader: Codable, Hashable {
var executable: String
var arguments: [String]? = nil
}

View File

@ -1,28 +1,26 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
import Foundation
//===----------------------------------------------------------------------===//
/// The Swift representation of `pkl.Project`
public struct Project: PklRegisteredType, Hashable, DependencyDeclaredInProjectFile {
public static var registeredIdentifier: String = "pkl.Project"
public static let registeredIdentifier: String = "pkl.Project"
let package: Package?
let evaluatorSettings: Project.EvaluatorSettings
let evaluatorSettings: PklEvaluatorSettings
let projectFileUri: String
@ -52,7 +50,7 @@ extension Project: Decodable {
public init(from decoder: Decoder) throws {
let dec = try decoder.container(keyedBy: PklCodingKey.self)
let package = try dec.decode(Package?.self, forKey: PklCodingKey(stringValue: "package")!)
let evaluatorSettings = try dec.decode(EvaluatorSettings.self, forKey: PklCodingKey(stringValue: "evaluatorSettings")!)
let evaluatorSettings = try dec.decode(PklEvaluatorSettings.self, forKey: PklCodingKey(stringValue: "evaluatorSettings")!)
let projectFileUri = try dec.decode(String.self, forKey: PklCodingKey(stringValue: "projectFileUri")!)
let tests = try dec.decode([String].self, forKey: PklCodingKey(stringValue: "tests")!)
let dependencies = try dec.decode([String: PklAny].self, forKey: PklCodingKey(stringValue: "dependencies")!)
@ -70,7 +68,7 @@ extension Project: Decodable {
extension Project {
/// The Swift representation of `pkl.Project#RemoteDependency`
public struct RemoteDependency: PklRegisteredType, DependencyDeclaredInProjectFile, Decodable, Hashable {
public static var registeredIdentifier: String = "pkl.Project#RemoteDependency"
public static let registeredIdentifier: String = "pkl.Project#RemoteDependency"
let uri: String
let checksums: Checksums?
@ -78,7 +76,7 @@ extension Project {
/// The Swift representation of `pkl.Project#Package`
public struct Package: PklRegisteredType, Hashable {
public static var registeredIdentifier: String = "pkl.Project#Package"
public static let registeredIdentifier: String = "pkl.Project#Package"
let name: String
let baseUri: String
@ -98,16 +96,11 @@ extension Project {
let uri: String
}
/// The Swift representation of `pkl.Project#EvaluatorSettings
public struct EvaluatorSettings: Decodable, Hashable {
let externalProperties: [String: String]?
let env: [String: String]?
let allowedModules: [String]?
let allowedResources: [String]?
let noCache: Bool?
let modulePath: [String]?
let timeout: Duration?
let moduleCacheDir: String?
let rootDir: String?
}
@available(
*,
deprecated,
message: "Replaced by PklEvaluatorSettings, independent of Project",
renamed: "PklEvaluatorSettings"
)
public typealias EvaluatorSettings = PklEvaluatorSettings
}

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
@ -35,8 +35,8 @@ public protocol BaseReader {
/// birds:catalog/swallow.pkl
/// ```
///
/// The first URI conveys name "fred.pkl" within parent "/persons/". The second URI
/// conveys the name "persons/fred.pkl" with no hierarchical meaning.
/// The first URI conveys name "swallow.pkl" within parent "/catalog/". The second URI
/// conveys the name "catalog/swallow.pkl" with no hierarchical meaning.
var hasHierarchicalUris: Bool { get }
/// Returns elements at a specified path.
@ -107,6 +107,11 @@ public struct PathElement {
/// Whether the element is a directory or not.
public let isDirectory: Bool
public init(name: String, isDirectory: Bool) {
self.name = name
self.isDirectory = isDirectory
}
}
extension PathElement {

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import MessagePack
import PklSwiftInternals

View File

@ -1,25 +1,20 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif
let pklDebug = ProcessInfo.processInfo.environment["PKL_DEBUG"]
@ -80,7 +75,7 @@ public func mapEquals<K>(map1: [K: any DynamicallyEquatable], map2: [K: any Dyna
public func resolvePaths(_ paths: String...) -> String {
var result = FileManager.default.currentDirectoryPath
for path in paths {
if path.starts(with: "/") {
if path.starts(with: "/") || URL(fileURLWithPath: path).path == path {
result = path
} else {
result = NSString.path(withComponents: [result, path])
@ -90,3 +85,12 @@ public func resolvePaths(_ paths: String...) -> String {
}
public let absoluteUriRegex = try! Regex("\\w+:")
public func tempDir() throws -> URL {
try (FileManager.default.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: URL(fileURLWithPath: "/"), create: true))
}
public func tempFile(suffix: String) throws -> URL {
let fileName = ProcessInfo.processInfo.globallyUniqueString + suffix
return try (tempDir()).appendingPathComponent(fileName)
}

View File

@ -1,28 +1,28 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftPkl open source project
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2022 Apple Inc. and the SwiftPkl project authors
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftPkl project authors
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//

View File

@ -1,12 +1,18 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 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
//
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Discovery.h"

View File

@ -1,12 +1,18 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 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
//
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !defined(PKLS_DEFINES_H)
#define PKLS_DEFINES_H

View File

@ -1,12 +1,18 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 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
//
/*
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !defined(PKLS_DISCOVERY_H)
#define PKLS_DISCOVERY_H

View File

@ -1,4 +1,4 @@
// Code generated from Pkl module `temp.pkl.swift.GeneratorSettings`. DO NOT EDIT.
// Code generated from Pkl module `pkl.swift.GeneratorSettings`. DO NOT EDIT.
import PklSwift
public enum GeneratorSettings {}
@ -6,7 +6,7 @@ public enum GeneratorSettings {}
extension GeneratorSettings {
/// Settings used to configure code generation.
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "temp.pkl.swift.GeneratorSettings"
public static let registeredIdentifier: String = "pkl.swift.GeneratorSettings"
/// The set of modules to turn into Swift code.
///

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import ArgumentParser
import Foundation
@ -77,11 +77,6 @@ struct PklGenSwift: AsyncParsableCommand {
)
var pklInputModules: [String] = []
func tempFile() -> URL {
let fileName = ProcessInfo.processInfo.globallyUniqueString + ".pkl"
return URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
}
private func generateScriptUrl() -> String {
if let generateScript = self.generateScript {
return URL(fileURLWithPath: generateScript).path

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
import PklSwift
@ -41,11 +41,6 @@ public struct PklSwiftGenerator {
}
}
private func tempFile() -> URL {
let fileName = ProcessInfo.processInfo.globallyUniqueString + ".pkl"
return URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(fileName)
}
private mutating func runModule(evaluator: Evaluator, pklInputModule: String) async throws {
let out = resolvePaths(self.settings.outputPath ?? ".out")
let moduleToEvaluate = """
@ -55,7 +50,7 @@ public struct PklSwiftGenerator {
moduleToGenerate = theModule
"""
let tempFile = tempFile()
let tempFile = try tempFile(suffix: ".pkl")
try moduleToEvaluate.write(to: tempFile, atomically: true, encoding: .utf8)
let files = try await evaluator.evaluateOutputFiles(source: .url(tempFile))
for (filename, contents) in files {

View File

@ -0,0 +1,51 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import Foundation
import PklSwift
@main
struct TestExternalReader {
static func main() async throws {
let client = ExternalReaderClient(
options: ExternalReaderClientOptions(
resourceReaders: [FibReader()]
))
try await client.run()
}
}
struct FibReader: ResourceReader {
var scheme: String { "fib" }
var isGlobbable: Bool { false }
var hasHierarchicalUris: Bool { false }
func listElements(uri: URL) async throws -> [PathElement] { throw PklError("not implemented") }
func read(url: URL) async throws -> [UInt8] {
let key = url.absoluteString.dropFirst(self.scheme.count + 1)
guard let n = Int(key), n > 0 else {
throw PklError("input uri must be in format fib:<positive integer>")
}
return Array(String(fibonacci(n: n)).utf8)
}
}
func fibonacci(n: Int) -> Int {
var (a, b) = (0, 1)
for _ in 0..<n {
(a, b) = (b, a + b)
}
return a
}

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import XCTest

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import XCTest

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import XCTest

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import PklSwift
import XCTest

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import XCTest
@ -43,6 +43,11 @@ final class PklDecoderTests: XCTestCase {
XCTAssertEqual(value, 42)
}
func testDecodeInt8Overflow() throws {
let bytes: [UInt8] = [0xD2, 0xFF, 0x00, 0x00, 0x00]
XCTAssertThrowsError(try PklDecoder.decode(Int8.self, from: bytes), "Cannot fit -16777216 into Int8")
}
func testDecodeString() throws {
let bytes: [UInt8] = [
0xD9, 0x27, 0x74, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6B, 0x20, 0x66, 0x6F, 0x78,

View File

@ -1,20 +1,20 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
import SemanticVersion
import XCTest
@testable import PklSwift
@ -55,10 +55,10 @@ class EvaluatorManagerTest: XCTestCase {
let manager1 = EvaluatorManager()
let manager2 = EvaluatorManager()
let manager3 = EvaluatorManager()
async let evalautor1 = try manager1.newEvaluator(options: .preconfigured).evaluateOutputText(source: .text("res = \"evaluator 1\""))
async let evalautor2 = try manager2.newEvaluator(options: .preconfigured).evaluateOutputText(source: .text("res = \"evaluator 2\""))
async let evalautor3 = try manager3.newEvaluator(options: .preconfigured).evaluateOutputText(source: .text("res = \"evaluator 3\""))
let (result1, result2, result3) = try await (evalautor1, evalautor2, evalautor3)
async let evaluator1 = try manager1.newEvaluator(options: .preconfigured).evaluateOutputText(source: .text("res = \"evaluator 1\""))
async let evaluator2 = try manager2.newEvaluator(options: .preconfigured).evaluateOutputText(source: .text("res = \"evaluator 2\""))
async let evaluator3 = try manager3.newEvaluator(options: .preconfigured).evaluateOutputText(source: .text("res = \"evaluator 3\""))
let (result1, result2, result3) = try await (evaluator1, evaluator2, evaluator3)
XCTAssertEqual(result1, "res = \"evaluator 1\"\n")
XCTAssertEqual(result2, "res = \"evaluator 2\"\n")
XCTAssertEqual(result3, "res = \"evaluator 3\"\n")

View File

@ -1,22 +1,22 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
@testable import MessagePack
@testable import PklSwift
import SemanticVersion
import XCTest
class TestLogger: Logger {
@ -91,6 +91,33 @@ final class PklSwiftTests: XCTestCase {
XCTAssertEqual(output, "foo = 1\n")
}
func testVersionCoverage() async throws {
let output = try await SemanticVersion(EvaluatorManager().getVersion())!
XCTAssert(supportedPklVersions.contains { $0.major == output.major && $0.minor == output.minor })
}
func testCustomProxyOptions() async throws {
let version = try await SemanticVersion(EvaluatorManager().getVersion())!
let expected = version < pklVersion0_26
? "http options are not supported on Pkl versions lower than 0.26"
: "ConnectException: Error connecting to host `example.com`"
var options = EvaluatorOptions.preconfigured
options.http = .init(
caCertificates: nil,
proxy: .init(
address: "http://localhost:1",
noProxy: ["myhost.com:1337", "myotherhost.org:42"]
)
)
do {
let evaluator = try await manager.newEvaluator(options: options)
let _ = try await evaluator.evaluateOutputText(source: .uri("https://example.com")!)
XCTFail("Should have thrown an error")
} catch {
XCTAssert("\(error)".contains(expected))
}
}
func testCustomModuleReader() async throws {
let reader = VirtualModuleReader(
read: { _ in
@ -278,10 +305,10 @@ final class PklSwiftTests: XCTestCase {
XCTAssertEqual(logger.logLines, [#"pkl: TRACE: "Hello there" = "Hello there" (repl:text)\#n"#])
}
// TODO re-enable this test when packages are available
// TODO: re-enable this test when packages are available
//
// func testWithProject() async throws {
// let project1Dir = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("project1")
// let project1Dir = (try tempDir()).appendingPathComponent("project1")
// try FileManager.default.createDirectory(at: project1Dir, withIntermediateDirectories: true)
// try """
// amends "pkl:Project"

View File

@ -0,0 +1,65 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import SemanticVersion
import XCTest
@testable import PklSwift
class ExternalReaderClientTest: XCTestCase {
func testE2E() async throws {
let version = try await SemanticVersion(EvaluatorManager().getVersion())!
guard version >= pklVersion0_27 else {
throw XCTSkip("External readers require Pkl 0.27 or later.")
}
let tempDir = try tempDir()
let testFile = tempDir.appendingPathComponent("test.pkl")
try #"""
import "pkl:test"
fib5 = read("fib:5").text.toInt()
fib10 = read("fib:10").text.toInt()
fib20 = read("fib:20").text.toInt()
fibErrA = test.catch(() -> read("fib:%20"))
fibErrB = test.catch(() -> read("fib:abc"))
fibErrC = test.catch(() -> read("fib:-10"))
"""#.write(to: testFile, atomically: true, encoding: .utf8)
let expectedResult = """
fib5 = 5
fib10 = 55
fib20 = 6765
fibErrA = "I/O error reading resource `fib:%20`. IOException: PklError(message: \\"input uri must be in format fib:<positive integer>\\")"
fibErrB = "I/O error reading resource `fib:abc`. IOException: PklError(message: \\"input uri must be in format fib:<positive integer>\\")"
fibErrC = "I/O error reading resource `fib:-10`. IOException: PklError(message: \\"input uri must be in format fib:<positive integer>\\")"
"""
let opts = EvaluatorOptions(
allowedModules: ["file:", "repl:text"],
allowedResources: ["fib:", "prop:"],
externalResourceReaders: [
"fib": ExternalReader(executable: "./.build/debug/test-external-reader"),
]
)
try await withEvaluator(options: opts) { evaluator in
let result = try await evaluator.evaluateOutputText(source: .url(testFile))
XCTAssertEqual(result.trimmingCharacters(in: .whitespacesAndNewlines), expectedResult.trimmingCharacters(in: .whitespacesAndNewlines))
}
}
}

View File

@ -1,3 +1,2 @@
res1: Pair<Int, String> = Pair(42, "Hello")
res2: Duration = 10.h
res3: DataSize = 1.2345.gib
res1: Duration = 10.h
res2: DataSize = 1.2345.gib

View File

@ -5,7 +5,7 @@ public enum AnyType {}
extension AnyType {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "AnyType"
public static let registeredIdentifier: String = "AnyType"
public var bird: AnyHashable?
@ -63,7 +63,7 @@ extension AnyType {
}
public struct Bird: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "AnyType#Bird"
public static let registeredIdentifier: String = "AnyType#Bird"
public var species: String

View File

@ -5,18 +5,15 @@ public enum ApiTypes {}
extension ApiTypes {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "ApiTypes"
public static let registeredIdentifier: String = "ApiTypes"
public var res1: Pair<Int, String>
public var res1: Duration
public var res2: Duration
public var res2: DataSize
public var res3: DataSize
public init(res1: Pair<Int, String>, res2: Duration, res3: DataSize) {
public init(res1: Duration, res2: DataSize) {
self.res1 = res1
self.res2 = res2
self.res3 = res3
}
}

View File

@ -5,7 +5,7 @@ public enum Classes {}
extension Classes {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "Classes"
public static let registeredIdentifier: String = "Classes"
public var animals: [Animal]
@ -15,7 +15,7 @@ extension Classes {
}
public struct Animal: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "Classes#Animal"
public static let registeredIdentifier: String = "Classes#Animal"
public var name: String

View File

@ -5,7 +5,7 @@ public enum Collections {}
extension Collections {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "Collections"
public static let registeredIdentifier: String = "Collections"
public var res1: [Int]

View File

@ -5,7 +5,7 @@ public enum ExtendedModule {}
extension ExtendedModule {
public struct Module: OpenModule.Module {
public static var registeredIdentifier: String = "ExtendedModule"
public static let registeredIdentifier: String = "ExtendedModule"
public var foo: String

View File

@ -13,7 +13,7 @@ extension OpenModule {
public typealias Module = OpenModule_Module
public struct ModuleImpl: Module {
public static var registeredIdentifier: String = "OpenModule"
public static let registeredIdentifier: String = "OpenModule"
public var foo: String

View File

@ -7,6 +7,9 @@ public protocol UnionTypes_Animal: PklRegisteredType, DynamicallyEquatable, Hash
var name: String { get }
}
public protocol UnionTypes_Shape: PklRegisteredType, DynamicallyEquatable, Hashable {
}
extension UnionTypes {
public enum Fruit: Decodable, Hashable {
case banana(Banana)
@ -14,8 +17,9 @@ extension UnionTypes {
case apple(Apple)
public init(from decoder: Decoder) throws {
let decoded = try decoder.singleValueContainer().decode(PklSwift.PklAny.self).value
switch decoded {
let container = try decoder.singleValueContainer()
let value = try container.decode(PklSwift.PklAny.self).value
switch value?.base {
case let decoded as Banana:
self = Fruit.banana(decoded)
case let decoded as Grape:
@ -27,14 +31,14 @@ extension UnionTypes {
Fruit.self,
.init(
codingPath: decoder.codingPath,
debugDescription: "Expected type Fruit, but got \(String(describing: decoded))"
debugDescription: "Expected type Fruit, but got \(String(describing: value))"
)
)
}
}
}
public enum City: String, CaseIterable, Decodable, Hashable {
public enum City: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable {
case sanFrancisco = "San Francisco"
case tokyo = "Tokyo"
case zurich = "Zurich"
@ -46,8 +50,9 @@ extension UnionTypes {
case donkey(Donkey)
public init(from decoder: Decoder) throws {
let decoded = try decoder.singleValueContainer().decode(PklSwift.PklAny.self).value
switch decoded {
let container = try decoder.singleValueContainer()
let value = try container.decode(PklSwift.PklAny.self).value
switch value?.base {
case let decoded as Zebra:
self = ZebraOrDonkey.zebra(decoded)
case let decoded as Donkey:
@ -57,7 +62,7 @@ extension UnionTypes {
ZebraOrDonkey.self,
.init(
codingPath: decoder.codingPath,
debugDescription: "Expected type ZebraOrDonkey, but got \(String(describing: decoded))"
debugDescription: "Expected type ZebraOrDonkey, but got \(String(describing: value))"
)
)
}
@ -80,8 +85,9 @@ extension UnionTypes {
}
public init(from decoder: Decoder) throws {
let decoded = try decoder.singleValueContainer().decode(PklSwift.PklAny.self).value
switch decoded {
let container = try decoder.singleValueContainer()
let value = try container.decode(PklSwift.PklAny.self).value
switch value?.base {
case let decoded as any Animal:
self = AnimalOrString.animal(decoded)
case let decoded as String:
@ -91,7 +97,7 @@ extension UnionTypes {
AnimalOrString.self,
.init(
codingPath: decoder.codingPath,
debugDescription: "Expected type AnimalOrString, but got \(String(describing: decoded))"
debugDescription: "Expected type AnimalOrString, but got \(String(describing: value))"
)
)
}
@ -107,8 +113,116 @@ extension UnionTypes {
}
}
public enum IntOrFloat: Decodable, Hashable {
case int(Int)
case float64(Float64)
private static func decodeNumeric(from decoder: Decoder, _ container: any SingleValueDecodingContainer) -> IntOrFloat? {
return (try? .int(container.decode(Int.self)))
?? (try? .float64(container.decode(Float64.self)))
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let decoded = IntOrFloat.decodeNumeric(from: decoder, container)
if decoded != nil {
self = decoded!
return
}
let value = try container.decode(PklSwift.PklAny.self).value
throw DecodingError.typeMismatch(
IntOrFloat.self,
.init(
codingPath: decoder.codingPath,
debugDescription: "Expected type IntOrFloat, but got \(String(describing: value))"
)
)
}
}
public enum Environment: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable {
case dev = "dev"
case prod = "prod"
case qa = "qa"
}
public enum AnimalOrShape: Decodable, Hashable {
case animal(any Animal)
case shape(any Shape)
public static func ==(lhs: AnimalOrShape, rhs: AnimalOrShape) -> Bool {
switch (lhs, rhs) {
case let (.animal(a), .animal(b)):
return a.isDynamicallyEqual(to: b)
case let (.shape(a), .shape(b)):
return a.isDynamicallyEqual(to: b)
default:
return false
}
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let value = try container.decode(PklSwift.PklAny.self).value
switch value?.base {
case let decoded as any Animal:
self = AnimalOrShape.animal(decoded)
case let decoded as any Shape:
self = AnimalOrShape.shape(decoded)
default:
throw DecodingError.typeMismatch(
AnimalOrShape.self,
.init(
codingPath: decoder.codingPath,
debugDescription: "Expected type AnimalOrShape, but got \(String(describing: value))"
)
)
}
}
public func hash(into hasher: inout Hasher) {
switch self {
case let .animal(value):
hasher.combine(value)
case let .shape(value):
hasher.combine(value)
}
}
}
public enum Numbers: Decodable, Hashable {
case int8(Int8)
case int16(Int16)
case int32(Int32)
case int(Int)
private static func decodeNumeric(from decoder: Decoder, _ container: any SingleValueDecodingContainer) -> Numbers? {
return (try? .int8(container.decode(Int8.self)))
?? (try? .int16(container.decode(Int16.self)))
?? (try? .int32(container.decode(Int32.self)))
?? (try? .int(container.decode(Int.self)))
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let decoded = Numbers.decodeNumeric(from: decoder, container)
if decoded != nil {
self = decoded!
return
}
let value = try container.decode(PklSwift.PklAny.self).value
throw DecodingError.typeMismatch(
Numbers.self,
.init(
codingPath: decoder.codingPath,
debugDescription: "Expected type Numbers, but got \(String(describing: value))"
)
)
}
}
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "UnionTypes"
public static let registeredIdentifier: String = "UnionTypes"
public var fruit1: Fruit
@ -132,6 +246,26 @@ extension UnionTypes {
public var animalOrString2: AnimalOrString
public var intOrFloat1: IntOrFloat
public var intOrFloat2: IntOrFloat
public var intOrFloat3: IntOrFloat
public var config: [Environment: String]
public var animalOrShape1: AnimalOrShape
public var animalOrShape2: AnimalOrShape
public var numbers1: Numbers
public var numbers2: Numbers
public var numbers3: Numbers
public var numbers4: Numbers
public init(
fruit1: Fruit,
fruit2: Fruit,
@ -143,7 +277,17 @@ extension UnionTypes {
animal1: ZebraOrDonkey,
animal2: ZebraOrDonkey,
animalOrString1: AnimalOrString,
animalOrString2: AnimalOrString
animalOrString2: AnimalOrString,
intOrFloat1: IntOrFloat,
intOrFloat2: IntOrFloat,
intOrFloat3: IntOrFloat,
config: [Environment: String],
animalOrShape1: AnimalOrShape,
animalOrShape2: AnimalOrShape,
numbers1: Numbers,
numbers2: Numbers,
numbers3: Numbers,
numbers4: Numbers
) {
self.fruit1 = fruit1
self.fruit2 = fruit2
@ -156,11 +300,21 @@ extension UnionTypes {
self.animal2 = animal2
self.animalOrString1 = animalOrString1
self.animalOrString2 = animalOrString2
self.intOrFloat1 = intOrFloat1
self.intOrFloat2 = intOrFloat2
self.intOrFloat3 = intOrFloat3
self.config = config
self.animalOrShape1 = animalOrShape1
self.animalOrShape2 = animalOrShape2
self.numbers1 = numbers1
self.numbers2 = numbers2
self.numbers3 = numbers3
self.numbers4 = numbers4
}
}
public struct Banana: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "UnionTypes#Banana"
public static let registeredIdentifier: String = "UnionTypes#Banana"
public var isRipe: Bool
@ -170,7 +324,7 @@ extension UnionTypes {
}
public struct Grape: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "UnionTypes#Grape"
public static let registeredIdentifier: String = "UnionTypes#Grape"
public var isUsedForWine: Bool
@ -180,7 +334,7 @@ extension UnionTypes {
}
public struct Apple: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "UnionTypes#Apple"
public static let registeredIdentifier: String = "UnionTypes#Apple"
public var isRed: Bool
@ -191,8 +345,20 @@ extension UnionTypes {
public typealias Animal = UnionTypes_Animal
public typealias Shape = UnionTypes_Shape
public struct Square: Shape {
public static let registeredIdentifier: String = "UnionTypes#Square"
public var corners: Int
public init(corners: Int) {
self.corners = corners
}
}
public struct Zebra: Animal {
public static var registeredIdentifier: String = "UnionTypes#Zebra"
public static let registeredIdentifier: String = "UnionTypes#Zebra"
public var name: String
@ -202,7 +368,7 @@ extension UnionTypes {
}
public struct Donkey: Animal {
public static var registeredIdentifier: String = "UnionTypes#Donkey"
public static let registeredIdentifier: String = "UnionTypes#Donkey"
public var name: String

View File

@ -9,7 +9,7 @@ public protocol pkl_swift_example_Poly_Animal: pkl_swift_lib1_Being {
extension pkl_swift_example_Poly {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "pkl.swift.example.Poly"
public static let registeredIdentifier: String = "pkl.swift.example.Poly"
public var beings: [any pkl_swift_lib1.Being]
@ -56,7 +56,7 @@ extension pkl_swift_example_Poly {
}
public struct Dog: Animal {
public static var registeredIdentifier: String = "pkl.swift.example.Poly#Dog"
public static let registeredIdentifier: String = "pkl.swift.example.Poly#Dog"
public var barks: Bool
@ -103,7 +103,7 @@ extension pkl_swift_example_Poly {
public typealias Animal = pkl_swift_example_Poly_Animal
public struct AnimalImpl: Animal {
public static var registeredIdentifier: String = "pkl.swift.example.Poly#Animal"
public static let registeredIdentifier: String = "pkl.swift.example.Poly#Animal"
public var name: String
@ -116,7 +116,7 @@ extension pkl_swift_example_Poly {
}
public struct Bird: pkl_swift_lib1.Being {
public static var registeredIdentifier: String = "pkl.swift.example.Poly#Bird"
public static let registeredIdentifier: String = "pkl.swift.example.Poly#Bird"
public var name: String

View File

@ -9,7 +9,7 @@ public protocol pkl_swift_lib1_Being: PklRegisteredType, DynamicallyEquatable, H
extension pkl_swift_lib1 {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "pkl.swift.lib1"
public static let registeredIdentifier: String = "pkl.swift.lib1"
public init() {}
}

View File

@ -1,3 +1,5 @@
import "pkl:math"
class Banana {
isRipe: Boolean
}
@ -18,6 +20,12 @@ typealias Fruit = Banana | Grape | Apple
typealias City = "San Francisco"|"Tokyo"|"Zurich"|"London"
abstract class Shape
class Square extends Shape {
fixed corners: Int = 4
}
fruit1: Fruit = new Banana {
isRipe = true
}
@ -52,4 +60,34 @@ typealias AnimalOrString = Animal|String
animalOrString1: AnimalOrString = new Zebra {}
animalOrString2: AnimalOrString = "Zebra"
animalOrString2: AnimalOrString = "Zebra"
typealias IntOrFloat = Int | Float
intOrFloat1: IntOrFloat = 5.0
intOrFloat2: IntOrFloat = 5.5
intOrFloat3: IntOrFloat = 128
typealias Environment = "dev"|"prod"|"qa"
config: Mapping<Environment, String> = new {
["dev"] = "Imaginary Service Company (ISC) configuration"
}
typealias AnimalOrShape = Animal|Shape
animalOrShape1: AnimalOrShape = new Donkey {}
animalOrShape2: AnimalOrShape = new Square {}
typealias Numbers = Int8 | Int16 | Int32 | Int
numbers1: Numbers = 5
numbers2: Numbers = 128
numbers3: Numbers = 32768
numbers4: Numbers = math.maxInt

View File

@ -1,18 +1,18 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import PklSwift
import XCTest
@ -77,9 +77,8 @@ class FixturesTest: XCTestCase {
XCTAssertEqual(
result,
ApiTypes.Module(
res1: Pair(42, "Hello"),
res2: .hours(10),
res3: .gibibytes(1.2345)
res1: .hours(10),
res2: .gibibytes(1.2345)
)
)
}
@ -161,7 +160,17 @@ class FixturesTest: XCTestCase {
animal1: .zebra(UnionTypes.Zebra(name: "Zebra")),
animal2: .donkey(UnionTypes.Donkey(name: "Donkey")),
animalOrString1: .animal(UnionTypes.Zebra(name: "Zebra")),
animalOrString2: .string("Zebra")
animalOrString2: .string("Zebra"),
intOrFloat1: .float64(5.0),
intOrFloat2: .float64(5.5),
intOrFloat3: .int(128),
config: [.dev : "Imaginary Service Company (ISC) configuration"],
animalOrShape1: .animal(UnionTypes.Donkey(name: "Donkey")),
animalOrShape2: .shape(UnionTypes.Square(corners: 4)),
numbers1: .int8(5),
numbers2: .int16(128),
numbers3: .int32(32768),
numbers4: .int(Int.max)
))
}
}

View File

@ -1,32 +1,34 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
import Foundation
import SemanticVersion
import XCTest
@testable import PklSwift
class ProjectTest: XCTestCase {
func testLoadProject() async throws {
let tempDir = NSTemporaryDirectory()
try FileManager.default.createDirectory(atPath: tempDir + "/subdir", withIntermediateDirectories: true)
let otherProjectFile = URL(fileURLWithPath: tempDir, isDirectory: true)
.appendingPathComponent("subdir/PklProject")
let version = try await SemanticVersion(EvaluatorManager().getVersion())!
let tempDir = try tempDir()
let subDir = tempDir.appendingPathComponent("subdir")
try FileManager.default.createDirectory(at: subDir, withIntermediateDirectories: true)
let otherProjectFile = subDir.appendingPathComponent("PklProject")
try #"""
@ModuleInfo { minPklVersion = "0.25.0" }
amends "pkl:Project"
package {
@ -37,8 +39,53 @@ class ProjectTest: XCTestCase {
}
"""#.write(to: otherProjectFile, atomically: true, encoding: .utf8)
let file = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
.appendingPathComponent("PklProject")
let file = try (PklSwift.tempDir()).appendingPathComponent("PklProject")
let httpSetting = version < pklVersion0_26 ? "" : """
http {
proxy {
address = "http://localhost:1"
noProxy {
"example.com"
"foo.bar.org"
}
}
}
"""
let externalReaderSettings = version < pklVersion0_27 ? "" : """
externalModuleReaders {
["scheme1"] {
executable = "reader1"
}
["scheme2"] {
executable = "reader2"
arguments { "with"; "args" }
}
}
externalResourceReaders {
["scheme3"] {
executable = "reader3"
}
["scheme4"] {
executable = "reader4"
arguments { "with"; "args" }
}
}
"""
let colorSetting = version < pklVersion0_27 ? "" : #"color = "always""#
let httpExpectation = version < pklVersion0_26 ? nil : Http(
caCertificates: nil,
proxy: .init(address: "http://localhost:1", noProxy: ["example.com", "foo.bar.org"])
)
let externalModuleReadersExpectation = version < pklVersion0_27 ? nil : [
"scheme1": ExternalReader(executable: "reader1"),
"scheme2": ExternalReader(executable: "reader2", arguments: ["with", "args"]),
]
let externalResourceReadersExpectation = version < pklVersion0_27 ? nil : [
"scheme3": ExternalReader(executable: "reader3"),
"scheme4": ExternalReader(executable: "reader4", arguments: ["with", "args"]),
]
let color: PklEvaluatorSettingsColor? = version < pklVersion0_27 ? nil : .always
try #"""
amends "pkl:Project"
@ -83,6 +130,9 @@ class ProjectTest: XCTestCase {
timeout = 5.min
moduleCacheDir = "/bar/buzz"
rootDir = "/buzzy"
\#(externalReaderSettings)
\#(httpSetting)
\#(colorSetting)
}
dependencies {
@ -97,7 +147,7 @@ class ProjectTest: XCTestCase {
source: .url(file),
asType: Project.self
)
let expectedSettings = PklSwift.Project.EvaluatorSettings(
let expectedSettings = PklSwift.PklEvaluatorSettings(
externalProperties: ["myprop": "1"],
env: ["myenv": "2"],
allowedModules: ["foo:"],
@ -106,7 +156,11 @@ class ProjectTest: XCTestCase {
modulePath: ["/bar/baz"],
timeout: .minutes(5),
moduleCacheDir: "/bar/buzz",
rootDir: "/buzzy"
rootDir: "/buzzy",
http: httpExpectation,
externalModuleReaders: externalModuleReadersExpectation,
externalResourceReaders: externalResourceReadersExpectation,
color: color
)
let expectedPackage = PklSwift.Project.Package(
name: "hawk",
@ -160,7 +214,11 @@ class ProjectTest: XCTestCase {
modulePath: nil,
timeout: nil,
moduleCacheDir: nil,
rootDir: nil
rootDir: nil,
http: nil,
externalModuleReaders: nil,
externalResourceReaders: nil,
color: nil
),
projectFileUri: "\(otherProjectFile)",
tests: [],

View File

@ -1 +1 @@
0.2.0
0.4.2

View File

@ -9,7 +9,7 @@ public protocol Classes_Animal: PklRegisteredType, DynamicallyEquatable, Hashabl
extension Classes {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "Classes"
public static let registeredIdentifier: String = "Classes"
public var animals: [any Animal]
@ -38,7 +38,7 @@ extension Classes {
public typealias Animal = Classes_Animal
public struct AnimalImpl: Animal {
public static var registeredIdentifier: String = "Classes#Animal"
public static let registeredIdentifier: String = "Classes#Animal"
public var name: String

View File

@ -10,7 +10,7 @@ extension EmptyOpenModule {
public typealias Module = EmptyOpenModule_Module
public struct ModuleImpl: Module {
public static var registeredIdentifier: String = "EmptyOpenModule"
public static let registeredIdentifier: String = "EmptyOpenModule"
public init() {}
}

View File

@ -5,7 +5,7 @@ public enum Enums {}
extension Enums {
/// City is one of these four fantastic cities
public enum City: String, CaseIterable, Decodable, Hashable {
public enum City: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable {
case sanFrancisco = "San Francisco"
case london = "London"
case zurich = "Zurich"
@ -19,8 +19,9 @@ extension Enums {
case monkey(Monkey)
public init(from decoder: Decoder) throws {
let decoded = try decoder.singleValueContainer().decode(PklSwift.PklAny.self).value
switch decoded {
let container = try decoder.singleValueContainer()
let value = try container.decode(PklSwift.PklAny.self).value
switch value?.base {
case let decoded as Horse:
self = Animal.horse(decoded)
case let decoded as Zebra:
@ -32,7 +33,7 @@ extension Enums {
Animal.self,
.init(
codingPath: decoder.codingPath,
debugDescription: "Expected type Animal, but got \(String(describing: decoded))"
debugDescription: "Expected type Animal, but got \(String(describing: value))"
)
)
}
@ -45,8 +46,9 @@ extension Enums {
case arrayString([String])
public init(from decoder: Decoder) throws {
let decoded = try decoder.singleValueContainer().decode(PklSwift.PklAny.self).value
switch decoded {
let container = try decoder.singleValueContainer()
let value = try container.decode(PklSwift.PklAny.self).value
switch value?.base {
case let decoded as [String: String]:
self = DictOrArray.dictionaryStringString(decoded)
case let decoded as [String]:
@ -56,7 +58,7 @@ extension Enums {
DictOrArray.self,
.init(
codingPath: decoder.codingPath,
debugDescription: "Expected type DictOrArray, but got \(String(describing: decoded))"
debugDescription: "Expected type DictOrArray, but got \(String(describing: value))"
)
)
}
@ -69,8 +71,9 @@ extension Enums {
case string(String)
public init(from decoder: Decoder) throws {
let decoded = try decoder.singleValueContainer().decode(PklSwift.PklAny.self).value
switch decoded {
let container = try decoder.singleValueContainer()
let value = try container.decode(PklSwift.PklAny.self).value
switch value?.base {
case let decoded as Horse:
self = HorseOrBug.horse(decoded)
case let decoded as String:
@ -82,7 +85,7 @@ extension Enums {
HorseOrBug.self,
.init(
codingPath: decoder.codingPath,
debugDescription: "Expected type HorseOrBug, but got \(String(describing: decoded))"
debugDescription: "Expected type HorseOrBug, but got \(String(describing: value))"
)
)
}
@ -94,8 +97,9 @@ extension Enums {
case zebra(Zebra)
public init(from decoder: Decoder) throws {
let decoded = try decoder.singleValueContainer().decode(PklSwift.PklAny.self).value
switch decoded {
let container = try decoder.singleValueContainer()
let value = try container.decode(PklSwift.PklAny.self).value
switch value?.base {
case let decoded as Horse?:
self = MaybeHorseOrDefinitelyZebra.horse(decoded)
case let decoded as Zebra:
@ -105,7 +109,7 @@ extension Enums {
MaybeHorseOrDefinitelyZebra.self,
.init(
codingPath: decoder.codingPath,
debugDescription: "Expected type MaybeHorseOrDefinitelyZebra, but got \(String(describing: decoded))"
debugDescription: "Expected type MaybeHorseOrDefinitelyZebra, but got \(String(describing: value))"
)
)
}
@ -113,7 +117,7 @@ extension Enums {
}
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "Enums"
public static let registeredIdentifier: String = "Enums"
/// City of tomorrow!
public var city: City
@ -145,7 +149,7 @@ extension Enums {
}
public struct Horse: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "Enums#Horse"
public static let registeredIdentifier: String = "Enums#Horse"
public var neigh: Bool
@ -155,7 +159,7 @@ extension Enums {
}
public struct Zebra: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "Enums#Zebra"
public static let registeredIdentifier: String = "Enums#Zebra"
public var stripes: String
@ -165,7 +169,7 @@ extension Enums {
}
public struct Monkey: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "Enums#Monkey"
public static let registeredIdentifier: String = "Enums#Monkey"
public var tail: String

View File

@ -4,13 +4,13 @@ import PklSwift
public enum ExplicitlyCoolName {}
extension ExplicitlyCoolName {
public enum ConfigType: String, CaseIterable, Decodable, Hashable {
public enum ConfigType: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable {
case one = "one"
case two = "two"
}
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "ExplicitName"
public static let registeredIdentifier: String = "ExplicitName"
public var MyCoolProp: SomethingVeryFunny
@ -24,7 +24,7 @@ extension ExplicitlyCoolName {
}
public struct SomethingVeryFunny: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "ExplicitName#SomethingFunny"
public static let registeredIdentifier: String = "ExplicitName#SomethingFunny"
public init() {}
}

View File

@ -5,7 +5,7 @@ public enum ExtendModule {}
extension ExtendModule {
public struct Module: MyModule.Module {
public static var registeredIdentifier: String = "ExtendModule"
public static let registeredIdentifier: String = "ExtendModule"
public var bar: String

View File

@ -9,7 +9,7 @@ public protocol ExtendingOpenClass_MyOpenClass: PklRegisteredType, DynamicallyEq
extension ExtendingOpenClass {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "ExtendingOpenClass"
public static let registeredIdentifier: String = "ExtendingOpenClass"
public var res1: MyClass
@ -22,7 +22,7 @@ extension ExtendingOpenClass {
}
public struct MyClass: MyOpenClass {
public static var registeredIdentifier: String = "ExtendingOpenClass#MyClass"
public static let registeredIdentifier: String = "ExtendingOpenClass#MyClass"
public var myBoolean: Bool
@ -37,7 +37,7 @@ extension ExtendingOpenClass {
public typealias MyOpenClass = ExtendingOpenClass_MyOpenClass
public struct MyOpenClassImpl: MyOpenClass {
public static var registeredIdentifier: String = "ExtendingOpenClass#MyOpenClass"
public static let registeredIdentifier: String = "ExtendingOpenClass#MyOpenClass"
public var myStr: String
@ -47,7 +47,7 @@ extension ExtendingOpenClass {
}
public struct MyClass2: lib3.GoGoGo {
public static var registeredIdentifier: String = "ExtendingOpenClass#MyClass2"
public static let registeredIdentifier: String = "ExtendingOpenClass#MyClass2"
public var myBoolean: Bool

View File

@ -9,7 +9,7 @@ public protocol ExtendsAbstractClass_A: PklRegisteredType, DynamicallyEquatable,
extension ExtendsAbstractClass {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "ExtendsAbstractClass"
public static let registeredIdentifier: String = "ExtendsAbstractClass"
public var a: any A
@ -36,7 +36,7 @@ extension ExtendsAbstractClass {
public typealias A = ExtendsAbstractClass_A
public struct B: A {
public static var registeredIdentifier: String = "ExtendsAbstractClass#B"
public static let registeredIdentifier: String = "ExtendsAbstractClass#B"
public var c: String

View File

@ -13,7 +13,7 @@ public protocol Foo_Being: PklRegisteredType, DynamicallyEquatable, Hashable {
extension Foo {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "Foo"
public static let registeredIdentifier: String = "Foo"
public var animals: [any Animal]
@ -42,7 +42,7 @@ extension Foo {
public typealias Animal = Foo_Animal
public struct AnimalImpl: Animal {
public static var registeredIdentifier: String = "Foo#Animal"
public static let registeredIdentifier: String = "Foo#Animal"
public var name: String
@ -57,7 +57,7 @@ extension Foo {
public typealias Being = Foo_Being
public struct Bird: Animal {
public static var registeredIdentifier: String = "Foo#Bird"
public static let registeredIdentifier: String = "Foo#Bird"
public var flies: Bool
@ -73,7 +73,7 @@ extension Foo {
}
public struct Dog: Animal {
public static var registeredIdentifier: String = "Foo#Dog"
public static let registeredIdentifier: String = "Foo#Dog"
public var barks: Bool

View File

@ -5,7 +5,7 @@ public enum HiddenProperties {}
extension HiddenProperties {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "HiddenProperties"
public static let registeredIdentifier: String = "HiddenProperties"
public var propC: String

View File

@ -5,7 +5,7 @@ public enum Imports {}
extension Imports {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "Imports"
public static let registeredIdentifier: String = "Imports"
public var foo: Foo.Module

View File

@ -11,7 +11,7 @@ extension MyModule {
public typealias Module = MyModule_Module
public struct ModuleImpl: Module {
public static var registeredIdentifier: String = "MyModule"
public static let registeredIdentifier: String = "MyModule"
public var foo: String

View File

@ -11,7 +11,7 @@ extension Override2 {
public typealias Module = Override2_Module
public struct ModuleImpl: Module {
public static var registeredIdentifier: String = "Override2"
public static let registeredIdentifier: String = "Override2"
/// Doc comments
public var foo: String
@ -22,7 +22,7 @@ extension Override2 {
}
public struct MySubclass: Module {
public static var registeredIdentifier: String = "Override2#MySubclass"
public static let registeredIdentifier: String = "Override2#MySubclass"
/// Doc comments
public var foo: String

View File

@ -5,7 +5,7 @@ public enum TypeAliased {}
extension TypeAliased {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "TypeAliased"
public static let registeredIdentifier: String = "TypeAliased"
public var myMap: StringyMap

View File

@ -4,14 +4,14 @@ import PklSwift
public enum UnionNameKeyword {}
extension UnionNameKeyword {
public enum `Type`: String, CaseIterable, Decodable, Hashable {
public enum `Type`: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable {
case one = "one"
case two = "two"
case three = "three"
}
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "UnionNameKeyword"
public static let registeredIdentifier: String = "UnionNameKeyword"
public var type: `Type`

View File

@ -5,13 +5,13 @@ public enum com_example_ExtendedSimple {}
extension com_example_ExtendedSimple {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "com.example.ExtendedSimple"
public static let registeredIdentifier: String = "com.example.ExtendedSimple"
public init() {}
}
public struct ExtendedSimple: com_example_Simple.Person {
public static var registeredIdentifier: String = "com.example.ExtendedSimple#ExtendedSimple"
public static let registeredIdentifier: String = "com.example.ExtendedSimple#ExtendedSimple"
public var eyeColor: String

View File

@ -15,7 +15,7 @@ public protocol com_example_Simple_OpenClassExtendingOpenClass: PklRegisteredTyp
extension com_example_Simple {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "com.example.Simple"
public static let registeredIdentifier: String = "com.example.Simple"
/// This is truly a person.
public var person: any Person
@ -43,7 +43,7 @@ extension com_example_Simple {
public typealias Person = com_example_Simple_Person
public struct PersonImpl: Person {
public static var registeredIdentifier: String = "com.example.Simple#Person"
public static let registeredIdentifier: String = "com.example.Simple#Person"
/// The name of the person
public var theName: String
@ -63,7 +63,7 @@ extension com_example_Simple {
}
public struct ThePerson: Person {
public static var registeredIdentifier: String = "com.example.Simple#ThePerson"
public static let registeredIdentifier: String = "com.example.Simple#ThePerson"
public var the: String
@ -89,7 +89,7 @@ extension com_example_Simple {
public typealias OpenClassExtendingOpenClass = com_example_Simple_OpenClassExtendingOpenClass
public struct OpenClassExtendingOpenClassImpl: OpenClassExtendingOpenClass {
public static var registeredIdentifier: String = "com.example.Simple#OpenClassExtendingOpenClass"
public static let registeredIdentifier: String = "com.example.Simple#OpenClassExtendingOpenClass"
public var someOtherProp: Bool?
@ -99,7 +99,7 @@ extension com_example_Simple {
}
public struct ClassWithReallyLongConstructor: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "com.example.Simple#ClassWithReallyLongConstructor"
public static let registeredIdentifier: String = "com.example.Simple#ClassWithReallyLongConstructor"
public var theProperty1: String

View File

@ -11,7 +11,7 @@ extension lib3 {
public typealias GoGoGo = lib3_GoGoGo
public struct GoGoGoImpl: GoGoGo {
public static var registeredIdentifier: String = "lib3#GoGoGo"
public static let registeredIdentifier: String = "lib3#GoGoGo"
public var duck: String
@ -21,7 +21,7 @@ extension lib3 {
}
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "lib3"
public static let registeredIdentifier: String = "lib3"
public init() {}
}

View File

@ -9,7 +9,7 @@ public protocol override_Foo: PklRegisteredType, DynamicallyEquatable, Hashable
extension override {
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "override"
public static let registeredIdentifier: String = "override"
public var foo: any Foo
@ -36,7 +36,7 @@ extension override {
public typealias Foo = override_Foo
public struct Bar: Foo {
public static var registeredIdentifier: String = "override#Bar"
public static let registeredIdentifier: String = "override#Bar"
public var myProp: String

View File

@ -5,28 +5,28 @@ public enum union {}
extension union {
/// City; e.g. where people live
public enum City: String, CaseIterable, Decodable, Hashable {
public enum City: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable {
case sanFrancisco = "San Francisco"
case london = "London"
case = "上海"
}
/// Locale that contains cities and towns
public enum County: String, CaseIterable, Decodable, Hashable {
public enum County: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable {
case sanFrancisco = "San Francisco"
case sanMateo = "San Mateo"
case yolo = "Yolo"
}
/// Noodles
public enum Noodles: String, CaseIterable, Decodable, Hashable {
public enum Noodles: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable {
case = "拉面"
case = "刀切面"
case 线 = "面线"
case = "意大利面"
}
public enum AccountDisposition: String, CaseIterable, Decodable, Hashable {
public enum AccountDisposition: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable {
case empty = ""
case icloud3 = "icloud3"
case prod = "prod"
@ -34,7 +34,7 @@ extension union {
}
public struct Module: PklRegisteredType, Decodable, Hashable {
public static var registeredIdentifier: String = "union"
public static let registeredIdentifier: String = "union"
/// A city
public var city: City

View File

@ -1,22 +1,23 @@
// ===----------------------------------------------------------------------===//
// Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
/// Generates Swift sources from Pkl
@swift.Module { name = "pkl_gen_swift" }
@ModuleInfo { minPklVersion = "0.24.0" }
module temp.pkl.swift.Generator
module pkl.swift.Generator
import "pkl:reflect"
import "internal/gatherer.pkl"
@ -53,7 +54,7 @@ function isEnumLike(decl: reflect.TypeDeclaration) =
function getSwiftModuleName(decl: reflect.TypeDeclaration): String? =
decl.enclosingDeclaration
.annotations
.findOrNull((it) -> it.getClass().toString() == "temp.pkl.swift.swift#Module")
.findOrNull((it) -> it.getClass().toString() == "pkl.swift.swift#Module")
?.name
function gatherEnums(decl: List<reflect.TypeDeclaration>): List<SwiftMapping> =

View File

@ -1,7 +1,23 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
/// Settings used to configure code generation.
@swift.Module { name = "pkl_gen_swift" }
@swift.Name { value = "GeneratorSettings" }
module temp.pkl.swift.GeneratorSettings
module pkl.swift.GeneratorSettings
import "pkl:reflect"
import "swift.pkl"

View File

@ -1,9 +1,25 @@
//===----------------------------------------------------------------------===//
// Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
amends "pkl:Project"
package {
name = "pkl.swift"
baseUri = "package://pkg.pkl-lang.org/pkl-swift/\(name)"
packageZipUrl = "https://github.com/apple/pkl-swift/releases/\(name)@\(version).zip"
packageZipUrl = "https://github.com/apple/pkl-swift/releases/download/\(name)@\(version)/\(name)@\(version).zip"
version = read("../../VERSION.txt").text
authors {
"The Pkl Authors <pkl-oss@group.apple.com>"

Some files were not shown because too many files have changed in this diff Show More