├── VERSION.txt ├── Sources ├── pkl-gen-swift │ ├── Resources │ │ └── VERSION.txt │ ├── Generated │ │ └── GeneratorSettings.pkl.swift │ └── PklSwiftGenerator.swift ├── PklSwiftInternals │ └── include │ │ ├── Defines.h │ │ └── Discovery.h ├── MessagePack │ ├── FixedWidthInteger.swift │ ├── AnyCodingKey.swift │ ├── Box.swift │ ├── Decoder │ │ ├── KeyedSingleDecodingContainer.swift │ │ ├── UnkeyedMapDecodingContainer.swift │ │ ├── UnkeyedDecodingContainer.swift │ │ └── _MessagePackDecoder.swift │ ├── Encoder │ │ ├── UnkeyedEncodingContainer.swift │ │ └── KeyedEncodingContainer.swift │ └── IO.swift ├── PklSwift │ ├── PklCodingKey.swift │ ├── API │ │ ├── Class.swift │ │ └── TypeAlias.swift │ ├── ModuleSource.swift │ ├── Logger.swift │ └── Project.swift └── test-external-reader │ └── TestExternalReader.swift ├── codegen ├── snippet-tests │ ├── input │ │ ├── Imports.pkl │ │ ├── TypeAliased.pkl │ │ ├── support │ │ │ ├── DoNotCollect.pkl │ │ │ ├── OpenModule.pkl │ │ │ ├── lib3.pkl │ │ │ ├── lib2.pkl │ │ │ └── lib.pkl │ │ ├── NoCollectThroughHidden.pkl │ │ ├── ExtendedSimple.pkl │ │ ├── Foo.pkl │ │ ├── Simple.pkl │ │ ├── EmptyOpenModule.pkl │ │ ├── Enums.pkl │ │ ├── ExtendModule.pkl │ │ ├── UnionNameKeyword.pkl │ │ ├── HiddenProperties.pkl │ │ ├── Override.pkl │ │ ├── ExtendAbstractClass.pkl │ │ ├── Override2.pkl │ │ ├── ExtendOpenClass.pkl │ │ ├── ExplicitName.pkl │ │ ├── Unions.pkl │ │ └── Classes.pkl │ ├── test.pkl │ └── output │ │ ├── EmptyOpenModule.pkl.swift │ │ ├── MyModule.pkl.swift │ │ ├── Override2.pkl.swift │ │ ├── Imports.pkl.swift │ │ ├── NoCollectThroughHidden.pkl.swift │ │ ├── HiddenProperties.pkl.swift │ │ ├── ExtendModule.pkl.swift │ │ ├── TypeAliased.pkl.swift │ │ ├── UnionNameKeyword.pkl.swift │ │ ├── lib3.pkl.swift │ │ ├── ExplicitlyCoolName.pkl.swift │ │ ├── com_example_ExtendedSimple.pkl.swift │ │ ├── override.pkl.swift │ │ ├── ExtendsAbstractClass.pkl.swift │ │ ├── union.pkl.swift │ │ ├── ExtendingOpenClass.pkl.swift │ │ └── Foo.pkl.swift └── src │ ├── PklProject.deps.json │ ├── tests │ ├── fixtures │ │ ├── types3.pkl │ │ ├── types4.pkl │ │ ├── types2.pkl │ │ └── types.pkl │ ├── ClassGen.pkl │ ├── gatherer.pkl │ └── utils.pkl │ ├── internal │ ├── TypeAliasGen.pkl │ └── Gen.pkl │ ├── PklProject │ ├── swift.pkl │ ├── GeneratorSettings.pkl │ └── Generator.pkl ├── Tests └── PklSwiftTests │ ├── Fixtures │ ├── Collections2.pkl │ ├── ExtendedModule.pkl │ ├── lib1.pkl │ ├── Imports │ │ └── UnusedClassDefs.pkl │ ├── UnusedClass.pkl │ ├── ApiTypes.pkl │ ├── AnyType.pkl │ ├── Collections.pkl │ ├── Generated │ │ ├── OpenModule.pkl.swift │ │ ├── Collections2.pkl.swift │ │ ├── ExtendedModule.pkl.swift │ │ ├── UnusedClass.pkl.swift │ │ ├── pkl_swift_lib1.pkl.swift │ │ ├── UnusedClassDefs.pkl.swift │ │ ├── Classes.pkl.swift │ │ ├── ApiTypes.pkl.swift │ │ ├── Collections.pkl.swift │ │ └── AnyType.pkl.swift │ ├── OpenModule.pkl │ ├── Classes.pkl │ └── UnionTypes.pkl │ ├── API │ ├── DataSizeTests.swift │ └── DurationTests.swift │ └── ExternalReaderClientTest.swift ├── docs ├── modules │ └── ROOT │ │ ├── partials │ │ └── component-attributes.adoc │ │ └── pages │ │ ├── index.adoc │ │ ├── external-readers.adoc │ │ ├── quickstart.adoc │ │ └── codegen.adoc ├── antora.yml └── nav.adoc ├── .mailmap ├── .spi.yml ├── .github ├── dependabot.yml ├── PklProject ├── workflows │ ├── test_report.yml │ └── __lockfile__.yml └── PklProject.deps.json ├── .gitignore ├── MAINTAINERS.adoc ├── scripts ├── pkl-header-style.toml ├── license-header.txt └── test-snippets.sh ├── .swiftformat ├── README.adoc ├── licenserc.toml ├── DEVELOPMENT.adoc ├── SECURITY.adoc ├── generator-settings.pkl ├── CONTRIBUTING.adoc ├── Package.resolved ├── CODE_OF_CONDUCT.adoc ├── Makefile └── Package.swift /VERSION.txt: -------------------------------------------------------------------------------- 1 | 0.7.2 2 | -------------------------------------------------------------------------------- /Sources/pkl-gen-swift/Resources/VERSION.txt: -------------------------------------------------------------------------------- 1 | ../../../VERSION.txt -------------------------------------------------------------------------------- /codegen/snippet-tests/input/Imports.pkl: -------------------------------------------------------------------------------- 1 | import "Foo.pkl" 2 | 3 | foo: Foo 4 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Collections2.pkl: -------------------------------------------------------------------------------- 1 | res: Bytes = Bytes(1, 2, 3, 255) 2 | -------------------------------------------------------------------------------- /docs/modules/ROOT/partials/component-attributes.adoc: -------------------------------------------------------------------------------- 1 | :pkl-swift-version: 0.7.2 2 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Jen Basch <421772+HT154@users.noreply.github.com> 2 | Jen Basch 3 | -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | builder: 3 | configs: 4 | - documentation_targets: ['PklSwift'] 5 | -------------------------------------------------------------------------------- /codegen/src/PklProject.deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "resolvedDependencies": {} 4 | } 5 | -------------------------------------------------------------------------------- /docs/antora.yml: -------------------------------------------------------------------------------- 1 | name: swift 2 | title: Pkl Swift Bindings 3 | version: 0.7.2 4 | nav: 5 | - nav.adoc 6 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/ExtendedModule.pkl: -------------------------------------------------------------------------------- 1 | extends "OpenModule.pkl" 2 | 3 | foo = "foo" 4 | 5 | bar = 10 6 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/TypeAliased.pkl: -------------------------------------------------------------------------------- 1 | typealias StringyMap = Mapping 2 | 3 | myMap: StringyMap 4 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/lib1.pkl: -------------------------------------------------------------------------------- 1 | module pkl.swift.lib1 2 | 3 | abstract class Being { 4 | exists: Boolean = true 5 | } 6 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Imports/UnusedClassDefs.pkl: -------------------------------------------------------------------------------- 1 | typealias ReferencedAlias = String 2 | 3 | class ThisClassShouldAlsoGenerate {} 4 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/support/DoNotCollect.pkl: -------------------------------------------------------------------------------- 1 | /// This module should not be discovered during codegen 2 | module DoNotCollect 3 | 4 | foo: String 5 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/NoCollectThroughHidden.pkl: -------------------------------------------------------------------------------- 1 | module NoCollectThroughHidden 2 | 3 | import "support/DoNotCollect.pkl" 4 | 5 | hidden doNotCollect: DoNotCollect 6 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/ExtendedSimple.pkl: -------------------------------------------------------------------------------- 1 | module com.example.ExtendedSimple 2 | 3 | import "Simple.pkl" 4 | 5 | class ExtendedSimple extends Simple.Person { 6 | eyeColor: String 7 | } 8 | -------------------------------------------------------------------------------- /docs/nav.adoc: -------------------------------------------------------------------------------- 1 | * xref:ROOT:quickstart.adoc[Quickstart] 2 | * xref:ROOT:evaluation.adoc[Evaluator API] 3 | * xref:ROOT:codegen.adoc[Code Generation] 4 | * xref:ROOT:external-readers.adoc[External Readers] 5 | * xref:ROOT:CHANGELOG.adoc[Changelog] 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | ignore: 6 | - dependency-name: '*' 7 | update-types: 8 | - version-update:semver-major 9 | schedule: 10 | interval: weekly 11 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/Foo.pkl: -------------------------------------------------------------------------------- 1 | animals: Listing 2 | 3 | abstract class Being { 4 | exists: Boolean 5 | } 6 | 7 | open class Animal extends Being { 8 | name: String 9 | } 10 | 11 | class Bird extends Animal { 12 | flies: Boolean 13 | } 14 | 15 | class Dog extends Animal { 16 | barks: Boolean 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .build 3 | .index-build 4 | /Packages 5 | /*.xcodeproj 6 | xcuserdata/ 7 | DerivedData/ 8 | .swiftpm/config/registries.json 9 | .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata 10 | .netrc 11 | 12 | .idea/ 13 | .vscode/ 14 | .swiftpm 15 | 16 | .env 17 | .out/ 18 | out/ 19 | 20 | .cicd/ 21 | .pkl-lsp/ 22 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/UnusedClass.pkl: -------------------------------------------------------------------------------- 1 | import "Imports/UnusedClassDefs.pkl" as defs 2 | 3 | // This test imports UnusedClassDefs, but it ONLY references the typealias and 4 | // not any of the classes. This caused the Module to be elided by the generator, 5 | // which results in a compilation failure. 6 | referencedAlias: defs.ReferencedAlias 7 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/ApiTypes.pkl: -------------------------------------------------------------------------------- 1 | import "pkl:base" 2 | 3 | res1: Duration = 10.h 4 | res2: DataSize = 1.2345.gib 5 | 6 | stringClass: Class = String 7 | baseModuleClass: Class = base.getClass() 8 | uint8TypeAlias: TypeAlias = UInt8 9 | fooClass: Class = Foo 10 | barTypeAlias: TypeAlias = Bar 11 | 12 | class Foo {} 13 | typealias Bar = Foo 14 | -------------------------------------------------------------------------------- /codegen/snippet-tests/test.pkl: -------------------------------------------------------------------------------- 1 | amends "pkl:test" 2 | 3 | import "pkl:reflect" 4 | 5 | import "../src/Generator.pkl" 6 | 7 | facts { 8 | for (_, mod in import*("input/*.pkl")) { 9 | [reflect.Module(mod).name] { 10 | for (name, output in new Generator { moduleToGenerate = mod }.output.files!!) { 11 | read?("output/\(name)")?.text == output.text 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /MAINTAINERS.adoc: -------------------------------------------------------------------------------- 1 | = MAINTAINERS 2 | 3 | This page lists all active Maintainers of this repository. 4 | 5 | See link:CONTRIBUTING.adoc[] for general contribution guidelines. 6 | 7 | == Maintainers (in alphabetical order) 8 | 9 | * https://github.com/bioball[Daniel Chao] 10 | * https://github.com/stackoverflow[Islon Scherer] 11 | * https://github.com/HT154[Jen Basch] 12 | * https://github.com/holzensp[Philip Hölzenspies] 13 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/AnyType.pkl: -------------------------------------------------------------------------------- 1 | class Bird { 2 | species: String 3 | } 4 | 5 | bird: Any = new Bird { 6 | species = "Owl" 7 | } 8 | 9 | primitive: Any = "foo" 10 | 11 | primitive2: Any = 12 12 | 13 | array: Any = new Listing { 14 | 1 15 | 2 16 | } 17 | 18 | set: Any = Set(5, 6) 19 | 20 | mapping: Any = new Mapping { 21 | ["1"] = 12 22 | [12] = "1" 23 | } 24 | 25 | nullable: Any = null 26 | 27 | duration: Any = 5.min 28 | 29 | dataSize: Any = 10.mb 30 | -------------------------------------------------------------------------------- /scripts/pkl-header-style.toml: -------------------------------------------------------------------------------- 1 | [SWIFT_STYLE] 2 | firstLine = '//===----------------------------------------------------------------------===//' 3 | endLine = "//===----------------------------------------------------------------------===//\n" 4 | beforeEachLine = '// ' 5 | afterEachLine = '' 6 | allowBlankLines = false 7 | multipleLines = true 8 | padLines = false 9 | firstLineDetectionPattern = '//\s?===' 10 | lastLineDetectionPattern = '//\s?===' 11 | skipLinePattern = '// swift-tools-version' 12 | -------------------------------------------------------------------------------- /.swiftformat: -------------------------------------------------------------------------------- 1 | # file options 2 | 3 | --swiftversion 5.7 4 | --exclude .build 5 | --exclude codegen 6 | --exclude Tests/PklSwiftTests/Fixtures/ 7 | --exclude Sources/*/Generated/ 8 | 9 | # format options 10 | 11 | --self insert 12 | --patternlet inline 13 | --stripunusedargs unnamed-only 14 | --ifdef no-indent 15 | --guardelse same-line 16 | --nevertrailing filter 17 | --extensionacl on-declarations 18 | 19 | # rules 20 | 21 | --disable spaceAroundOperators 22 | --disable wrapMultilineStatementBraces 23 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/EmptyOpenModule.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `EmptyOpenModule`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum EmptyOpenModule {} 5 | 6 | public protocol EmptyOpenModule_Module: PklRegisteredType, DynamicallyEquatable, Hashable, Sendable { 7 | } 8 | 9 | extension EmptyOpenModule { 10 | public typealias Module = EmptyOpenModule_Module 11 | 12 | public struct ModuleImpl: Module { 13 | public static let registeredIdentifier: String = "EmptyOpenModule" 14 | 15 | public init() {} 16 | } 17 | } -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/index.adoc: -------------------------------------------------------------------------------- 1 | = Integration with Swift 2 | 3 | Pkl provides a rich integration with Swift. Our integration allows you to embed the Pkl runtime into your Swift program, and also provides code generation from Pkl source code. 4 | 5 | To get started, reference the xref:quickstart.adoc[Quickstart guide]. 6 | 7 | [source,swift] 8 | ---- 9 | @main 10 | class MyApp { 11 | static func main() async throws { 12 | let config = try await MyConfig.loadFrom(source: .path("config.pkl")) 13 | print("I'm running on host \(config.host)") 14 | } 15 | } 16 | ---- 17 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = pkl-swift 2 | 3 | This library exposes Swift bindings for the Pkl configuration language. 4 | 5 | It allows you to embed Pkl into your Swift application, complete with code generation for full type safety and ease of use. 6 | 7 | The full documentation for this library can be found on our link:https://pkl-lang.org/swift/current/index.html[documentation site]. 8 | 9 | To get up and going, reference the link:https://pkl-lang.org/swift/current/quickstart.html[quick start guide]. 10 | 11 | When upgrading pkl-swift, reference the link:https://pkl-lang.org/swift/current/CHANGELOG.html[changelog] for details. 12 | -------------------------------------------------------------------------------- /licenserc.toml: -------------------------------------------------------------------------------- 1 | additionalHeaders = ["scripts/pkl-header-style.toml"] 2 | 3 | headerPath = "scripts/license-header.txt" 4 | 5 | includes = [ 6 | "*.swift", 7 | "*.pkl", 8 | "*.h", 9 | "*.cpp", 10 | "*.sh", 11 | "PklProject", 12 | ] 13 | 14 | excludes = [ 15 | "Sources/pkl-gen-swift/Generated", 16 | "Tests/PklSwiftTests/Fixtures", 17 | "codegen/snippet-tests", 18 | "doc", 19 | ] 20 | 21 | [git] 22 | attrs = 'enable' 23 | ignore = 'enable' 24 | 25 | [properties] 26 | copyrightOwner = "Apple Inc. and the Pkl project authors" 27 | 28 | [mapping.SWIFT_STYLE] 29 | extensions = ["swift"] 30 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/MyModule.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `MyModule`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum MyModule {} 5 | 6 | public protocol MyModule_Module: PklRegisteredType, DynamicallyEquatable, Hashable, Sendable { 7 | var foo: String { get } 8 | } 9 | 10 | extension MyModule { 11 | public typealias Module = MyModule_Module 12 | 13 | public struct ModuleImpl: Module { 14 | public static let registeredIdentifier: String = "MyModule" 15 | 16 | public var foo: String 17 | 18 | public init(foo: String) { 19 | self.foo = foo 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /codegen/snippet-tests/input/Simple.pkl: -------------------------------------------------------------------------------- 1 | module com.example.Simple 2 | 3 | /// This is truly a person. 4 | person: Person 5 | 6 | open class Person { 7 | /// The name of the person 8 | `the name`: String 9 | 10 | /// Some name that matches a keyword 11 | enum: String 12 | } 13 | 14 | class ThePerson extends Person { 15 | the: String 16 | } 17 | 18 | open class OpenClassExtendingOpenClass { 19 | someOtherProp: Boolean? 20 | } 21 | 22 | class ClassWithReallyLongConstructor { 23 | theProperty1: String 24 | 25 | theProperty2: String 26 | 27 | theProperty3: String 28 | 29 | theProperty4: String 30 | 31 | theProperty5: String 32 | 33 | theProperty6: String 34 | } 35 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Collections.pkl: -------------------------------------------------------------------------------- 1 | res1: List = List(1, 2, 3) 2 | 3 | res2: Listing = new { 2; 3; 4 } 4 | 5 | res3: List> = List(List(1), List(2), List(3)) 6 | 7 | res4: Listing> = new { new { 1 }; new { 2 }; new { 3 } } 8 | 9 | res5: Mapping = new { [1] = true; [2] = false } 10 | 11 | res6: Mapping> = new { 12 | [1] { 13 | [1] = true 14 | } 15 | [2] { 16 | [2] = true 17 | } 18 | [3] { 19 | [3] = true 20 | } 21 | } 22 | 23 | res7: Map = Map(1, true, 2, false) 24 | 25 | res8: Map> = Map(1, Map(1, true), 2, Map(2, false)) 26 | 27 | res9: Set = Set("one", "two", "three") 28 | 29 | res10: Set = Set(1, 2, 3) 30 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Generated/OpenModule.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `OpenModule`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum OpenModule {} 5 | 6 | public protocol OpenModule_Module: PklRegisteredType, DynamicallyEquatable, Hashable, Sendable { 7 | var foo: String { get } 8 | 9 | var bar: Int { get } 10 | } 11 | 12 | extension OpenModule { 13 | public typealias Module = OpenModule_Module 14 | 15 | public struct ModuleImpl: Module { 16 | public static let registeredIdentifier: String = "OpenModule" 17 | 18 | public var foo: String 19 | 20 | public var bar: Int 21 | 22 | public init(foo: String, bar: Int) { 23 | self.foo = foo 24 | self.bar = bar 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /DEVELOPMENT.adoc: -------------------------------------------------------------------------------- 1 | = Development 2 | 3 | == Running the formatter 4 | 5 | This project uses two formatting tools: https://github.com/korandoru/hawkeye[hawkeye] and https://github.com/nicklockwood/SwiftFormat[SwiftFormat]. 6 | 7 | To install swiftformat: 8 | 9 | [source,shell] 10 | ---- 11 | brew install swiftformat 12 | ---- 13 | 14 | To install hawkeye locally, you will need to install the https://rustup.rs[Rust toolchain], then run: 15 | 16 | [source,shell] 17 | ---- 18 | cargo install hawkeye 19 | ---- 20 | 21 | To run formatters: 22 | 23 | [source,shell] 24 | ---- 25 | make format 26 | 27 | # Alternatively, individually: 28 | swiftformat . 29 | 30 | hawkeye format 31 | ---- 32 | 33 | To regenerate all the test fixtures after changes to the codegen run `make generate-fixtures`. 34 | -------------------------------------------------------------------------------- /SECURITY.adoc: -------------------------------------------------------------------------------- 1 | = Security 2 | 3 | For the protection of our community, the Pkl team does not disclose, discuss, or confirm security issues until our investigation is complete and any necessary updates are generally available. 4 | 5 | == Reporting a security vulnerability 6 | 7 | If you have discovered a security vulnerability within the Pkl Swift project, please report it to us. 8 | We welcome reports from everyone, including security researchers, developers, and users. 9 | 10 | Security vulnerabilities may be reported on the link:https://security.apple.com/submit[Report a vulnerability] form. 11 | When submitting a vulnerability, select "Apple Devices and Software" as the affected platform, and "Open Source" as the affected area. 12 | 13 | For more information, see https://pkl-lang.org/security.html. 14 | -------------------------------------------------------------------------------- /scripts/license-header.txt: -------------------------------------------------------------------------------- 1 | Copyright ©{{ " " }}{%- if attrs.git_file_modified_year != attrs.git_file_created_year -%}{{ attrs.git_file_created_year }}-{{ attrs.git_file_modified_year }}{%- else -%}{{ attrs.git_file_created_year }}{%- endif -%}{{ " " }}{{ props["copyrightOwner"] }}. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | https://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /codegen/src/tests/fixtures/types3.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | class Bike 18 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/EmptyOpenModule.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | open module EmptyOpenModule 17 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/support/OpenModule.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | open module MyModule 17 | 18 | foo: String 19 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/Enums.pkl: -------------------------------------------------------------------------------- 1 | /// City of tomorrow! 2 | city: City 3 | 4 | /// The animal 5 | animal: Animal 6 | 7 | /// Some dictionary or array type. 8 | dictOrArray: DictOrArray 9 | 10 | /// Bugs are cool. 11 | bug: BugBug 12 | 13 | horseOrBug: HorseOrBug 14 | 15 | /// City is one of these four fantastic cities 16 | typealias City = "San Francisco" | "London" | "Zurich" | "Cupertino" 17 | 18 | class Horse { 19 | neigh: Boolean 20 | } 21 | 22 | class Zebra { 23 | stripes: String 24 | } 25 | 26 | class Monkey { 27 | tail: String 28 | } 29 | 30 | /// Animal is either a horse, monkey, or zebra 31 | typealias Animal = Horse | Zebra | Monkey 32 | 33 | /// Either a dictionary or an array. 34 | typealias DictOrArray = Mapping | Listing 35 | 36 | typealias BugBug = "bug bug" | "bugBug" 37 | 38 | typealias HorseOrBug = Horse | "bug bug" | "bugBug" 39 | 40 | typealias MaybeHorseOrDefinitelyZebra = Horse? | Zebra 41 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/ExtendModule.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | extends "support/OpenModule.pkl" 17 | 18 | bar: String 19 | -------------------------------------------------------------------------------- /codegen/src/tests/fixtures/types4.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | class Foo { 18 | bar: Listing 19 | } 20 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/OpenModule.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | open module OpenModule 17 | 18 | foo: String 19 | 20 | bar: Int 21 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/support/lib3.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module lib3 17 | 18 | open class GoGoGo { 19 | duck: "quack" 20 | } 21 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/support/lib2.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module lib2 17 | 18 | typealias Cities = "London" | "San Francisco" | "Los Angeles" 19 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/Override2.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `Override2`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum Override2 {} 5 | 6 | public protocol Override2_Module: PklRegisteredType, DynamicallyEquatable, Hashable, Sendable { 7 | var foo: String { get } 8 | } 9 | 10 | extension Override2 { 11 | public typealias Module = Override2_Module 12 | 13 | public struct ModuleImpl: Module { 14 | public static let registeredIdentifier: String = "Override2" 15 | 16 | /// Doc comments 17 | public var foo: String 18 | 19 | public init(foo: String) { 20 | self.foo = foo 21 | } 22 | } 23 | 24 | public struct MySubclass: Module { 25 | public static let registeredIdentifier: String = "Override2#MySubclass" 26 | 27 | /// Doc comments 28 | public var foo: String 29 | 30 | public init(foo: String) { 31 | self.foo = foo 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /codegen/snippet-tests/input/UnionNameKeyword.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module UnionNameKeyword 17 | 18 | typealias Type = "one" | "two" | "three" 19 | 20 | type: Type 21 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/support/lib.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module lib 17 | 18 | open class MyClass { 19 | thing: String 20 | } 21 | 22 | typealias MyEnum = "one" | "two" | "three" 23 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/HiddenProperties.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module HiddenProperties 17 | 18 | hidden propA: String 19 | 20 | hidden propB: String 21 | 22 | propC: String = propA + propB 23 | -------------------------------------------------------------------------------- /codegen/src/tests/fixtures/types2.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import "types3.pkl" 18 | 19 | class Person { 20 | bikes: Listing 21 | otherbikes: Listing 22 | } 23 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/Override.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module `override` 17 | 18 | abstract class Foo { 19 | myProp: String 20 | } 21 | 22 | class Bar extends Foo { 23 | myProp = "Bar" 24 | } 25 | 26 | foo: Foo 27 | -------------------------------------------------------------------------------- /scripts/test-snippets.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -e 17 | 18 | SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)" 19 | 20 | make generate-snippets 21 | 22 | diff=$(git diff "$SCRIPT_DIR"/../codegen/snippet-tests/) 23 | 24 | if [[ ! -z "$diff" ]]; then 25 | echo "Error: Snippet tests contains changes!" 26 | echo "$diff" 27 | exit 1 28 | fi 29 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/ExtendAbstractClass.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module ExtendsAbstractClass 17 | 18 | abstract class A { 19 | b: String 20 | } 21 | 22 | class B extends A { 23 | b = "hi" 24 | c: String 25 | } 26 | 27 | a: A 28 | -------------------------------------------------------------------------------- /generator-settings.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | amends "codegen/src/GeneratorSettings.pkl" 18 | 19 | generateScript = "codegen/src/Generator.pkl" 20 | 21 | projectDir = "codegen/src" 22 | 23 | outputPath = "Sources/pkl-gen-swift/Generated" 24 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Classes.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module Classes 17 | 18 | animals: Listing = new { 19 | new { name = "Uni" } 20 | new { name = "Wally" } 21 | new { name = "Mouse" } 22 | } 23 | 24 | class Animal { 25 | name: String 26 | } 27 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/Override2.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | open module Override2 17 | 18 | import "Override2.pkl" 19 | 20 | /// Doc comments 21 | foo: String 22 | 23 | class MySubclass extends Override2 { 24 | /// Different doc comments 25 | foo: String 26 | } 27 | -------------------------------------------------------------------------------- /.github/PklProject: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | amends "pkl:Project" 18 | 19 | dependencies { 20 | ["pkl.impl.ghactions"] { 21 | uri = "package://pkg.pkl-lang.org/pkl-project-commons/pkl.impl.ghactions@1.1.6" 22 | } 23 | ["com.github.actions"] { 24 | uri = "package://pkg.pkl-lang.org/pkl-pantry/com.github.actions@1.3.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.github/workflows/test_report.yml: -------------------------------------------------------------------------------- 1 | # Generated from Workflow.pkl. DO NOT EDIT. 2 | name: PR Test Reports 3 | 'on': 4 | workflow_run: 5 | types: 6 | - completed 7 | workflows: 8 | - Pull Request 9 | permissions: 10 | contents: read 11 | jobs: 12 | test-results: 13 | name: Test Results 14 | if: github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure' 15 | permissions: 16 | actions: read 17 | checks: write 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Download artifacts 21 | uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11 22 | with: 23 | path: artifacts 24 | name: test-results-.* 25 | name_is_regexp: true 26 | run_id: ${{ github.event.workflow_run.id }} 27 | - name: Publish test results 28 | uses: EnricoMi/publish-unit-test-result-action@34d7c956a59aed1bfebf31df77b8de55db9bbaaf # v2 29 | with: 30 | commit: ${{ github.event.workflow_run.head_sha }} 31 | comment_mode: 'off' 32 | files: artifacts/**/*.xml 33 | event_file: artifacts/test-results-event-file/event.json 34 | event_name: ${{ github.event.workflow_run.event }} 35 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/ExtendOpenClass.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module ExtendingOpenClass 17 | 18 | import "support/lib3.pkl" 19 | 20 | res1: MyClass 21 | 22 | res2: MyClass2 23 | 24 | open class MyOpenClass { 25 | myStr: String 26 | } 27 | 28 | class MyClass extends MyOpenClass { 29 | myStr = "mystr" 30 | myBoolean: Boolean 31 | } 32 | 33 | class MyClass2 extends lib3.GoGoGo { 34 | myBoolean: Boolean 35 | } 36 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/ExplicitName.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | @swift.Module { name = "SwiftExplicitName" } 17 | @swift.Name { value = "ExplicitlyCoolName" } 18 | module ExplicitName 19 | 20 | import ".../src/swift.pkl" 21 | 22 | @swift.Name { value = "MyCoolProp" } 23 | myProp: SomethingFunny 24 | 25 | @swift.Name { value = "SomethingVeryFunny" } 26 | class SomethingFunny {} 27 | 28 | @swift.Name { value = "ConfigType" } 29 | typealias Type = "one" | "two" 30 | -------------------------------------------------------------------------------- /Sources/PklSwiftInternals/include/Defines.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #if !defined(PKLS_DEFINES_H) 18 | #define PKLS_DEFINES_H 19 | 20 | #define PKLS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin") 21 | #define PKLS_ASSUME_NONNULL_END _Pragma("clang assume_nonnull end") 22 | 23 | #if defined(__cplusplus) 24 | #define PKLS_EXTERN extern "C" 25 | #else 26 | #define PKLS_EXTERN extern 27 | #endif 28 | 29 | #if defined(_WIN32) 30 | #define PKLS_IMPORT_FROM_STDLIB PKLS_EXTERN __declspec(dllimport) 31 | #else 32 | #define PKLS_IMPORT_FROM_STDLIB PKLS_EXTERN 33 | #endif 34 | 35 | #endif // PKLS_DEFINES_H 36 | -------------------------------------------------------------------------------- /codegen/src/tests/ClassGen.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | amends "pkl:test" 18 | 19 | import "pkl:reflect" 20 | 21 | import ".../internal/ClassGen.pkl" 22 | 23 | local class Person { 24 | name: String 25 | hobbies: List 26 | hidden talent: String 27 | } 28 | 29 | facts { 30 | ["getFields ignores hidden members"] { 31 | local fields = ClassGen.getProperties(reflect.Class(Person), List()) 32 | fields.keys == Set("name", "hobbies") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /codegen/src/internal/TypeAliasGen.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | @Unlisted 18 | module pkl.swift.internal.TypeAliasGen 19 | 20 | extends "Gen.pkl" 21 | 22 | import "pkl:reflect" 23 | 24 | import "Type.pkl" 25 | import "typegen.pkl" 26 | 27 | typealiaz: reflect.TypeAlias = mapping.source as reflect.TypeAlias 28 | 29 | type: Type = typegen.generateType(typealiaz.referent, typealiaz, mappings) 30 | 31 | contents = "public typealias \(mapping.name) = \(type.render(namespaceName))" 32 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/Imports.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `Imports`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum Imports {} 5 | 6 | extension Imports { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "Imports" 9 | 10 | public var foo: Foo.Module 11 | 12 | public init(foo: Foo.Module) { 13 | self.foo = foo 14 | } 15 | } 16 | 17 | /// Load the Pkl module at the given source and evaluate it into `Imports.Module`. 18 | /// 19 | /// - Parameter source: The source of the Pkl module. 20 | public static func loadFrom(source: ModuleSource) async throws -> Imports.Module { 21 | try await PklSwift.withEvaluator { evaluator in 22 | try await loadFrom(evaluator: evaluator, source: source) 23 | } 24 | } 25 | 26 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 27 | /// `Imports.Module`. 28 | /// 29 | /// - Parameter evaluator: The evaluator to use for evaluation. 30 | /// - Parameter source: The module to evaluate. 31 | public static func loadFrom( 32 | evaluator: PklSwift.Evaluator, 33 | source: PklSwift.ModuleSource 34 | ) async throws -> Imports.Module { 35 | try await evaluator.evaluateModule(source: source, as: Module.self) 36 | } 37 | } -------------------------------------------------------------------------------- /.github/PklProject.deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "resolvedDependencies": { 4 | "package://pkg.pkl-lang.org/pkl-pantry/com.github.actions@1": { 5 | "type": "remote", 6 | "uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/com.github.actions@1.3.0", 7 | "checksums": { 8 | "sha256": "76174cb974310b3d952280b76ed224eb4ee6fd5419bf696ad9a66fba44bd427d" 9 | } 10 | }, 11 | "package://pkg.pkl-lang.org/pkl-project-commons/pkl.impl.ghactions@1": { 12 | "type": "remote", 13 | "uri": "projectpackage://pkg.pkl-lang.org/pkl-project-commons/pkl.impl.ghactions@1.1.6", 14 | "checksums": { 15 | "sha256": "efe36e694f45b0804c5fcc746774727c016c866478b8c1eb0ea86d00c8bd8cf2" 16 | } 17 | }, 18 | "package://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.deepToTyped@1": { 19 | "type": "remote", 20 | "uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/pkl.experimental.deepToTyped@1.1.1", 21 | "checksums": { 22 | "sha256": "1e6e29b441ffdee2605d317f6543a4a604aab5af472b63f0c47d92a3b4b36f7f" 23 | } 24 | }, 25 | "package://pkg.pkl-lang.org/pkl-pantry/com.github.dependabot@1": { 26 | "type": "remote", 27 | "uri": "projectpackage://pkg.pkl-lang.org/pkl-pantry/com.github.dependabot@1.0.0", 28 | "checksums": { 29 | "sha256": "02ef6f25bfca5b1d095db73ea15de79d2d2c6832ebcab61e6aba90554382abcb" 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/Unions.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module union 17 | 18 | /// A city 19 | city: City 20 | 21 | /// County 22 | county: County 23 | 24 | /// Noodles 25 | noodle: Noodles 26 | 27 | /// Account disposition 28 | disposition: AccountDisposition 29 | 30 | /// City; e.g. where people live 31 | typealias City = "San Francisco" | "London" | "上海" 32 | 33 | /// Locale that contains cities and towns 34 | typealias County = "San Francisco" | "San Mateo" | "Yolo" 35 | 36 | /// Noodles 37 | typealias Noodles = "拉面" | "刀切面" | "面线" | "意大利面" 38 | 39 | typealias AccountDisposition = "" | "icloud3" | "prod" | "shared" 40 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/NoCollectThroughHidden.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `NoCollectThroughHidden`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum NoCollectThroughHidden {} 5 | 6 | extension NoCollectThroughHidden { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "NoCollectThroughHidden" 9 | 10 | public init() {} 11 | } 12 | 13 | /// Load the Pkl module at the given source and evaluate it into `NoCollectThroughHidden.Module`. 14 | /// 15 | /// - Parameter source: The source of the Pkl module. 16 | public static func loadFrom(source: ModuleSource) async throws -> NoCollectThroughHidden.Module { 17 | try await PklSwift.withEvaluator { evaluator in 18 | try await loadFrom(evaluator: evaluator, source: source) 19 | } 20 | } 21 | 22 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 23 | /// `NoCollectThroughHidden.Module`. 24 | /// 25 | /// - Parameter evaluator: The evaluator to use for evaluation. 26 | /// - Parameter source: The module to evaluate. 27 | public static func loadFrom( 28 | evaluator: PklSwift.Evaluator, 29 | source: PklSwift.ModuleSource 30 | ) async throws -> NoCollectThroughHidden.Module { 31 | try await evaluator.evaluateModule(source: source, as: Module.self) 32 | } 33 | } -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Generated/Collections2.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `Collections2`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum Collections2 {} 5 | 6 | extension Collections2 { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "Collections2" 9 | 10 | public var res: [UInt8] 11 | 12 | public init(res: [UInt8]) { 13 | self.res = res 14 | } 15 | } 16 | 17 | /// Load the Pkl module at the given source and evaluate it into `Collections2.Module`. 18 | /// 19 | /// - Parameter source: The source of the Pkl module. 20 | public static func loadFrom(source: ModuleSource) async throws -> Collections2.Module { 21 | try await PklSwift.withEvaluator { evaluator in 22 | try await loadFrom(evaluator: evaluator, source: source) 23 | } 24 | } 25 | 26 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 27 | /// `Collections2.Module`. 28 | /// 29 | /// - Parameter evaluator: The evaluator to use for evaluation. 30 | /// - Parameter source: The module to evaluate. 31 | public static func loadFrom( 32 | evaluator: PklSwift.Evaluator, 33 | source: PklSwift.ModuleSource 34 | ) async throws -> Collections2.Module { 35 | try await evaluator.evaluateModule(source: source, as: Module.self) 36 | } 37 | } -------------------------------------------------------------------------------- /.github/workflows/__lockfile__.yml: -------------------------------------------------------------------------------- 1 | #file: noinspection MandatoryParamsAbsent,UndefinedAction 2 | # This is a fake workflow that never runs. 3 | # It's used to pin actions to specific git SHAs when generating actual workflows. 4 | # It also gets updated by dependabot (see .github/dependabot.yml). 5 | # Generated from Workflow.pkl. DO NOT EDIT. 6 | name: __lockfile__ 7 | 'on': 8 | push: 9 | branches-ignore: 10 | - '**' 11 | tags-ignore: 12 | - '**' 13 | jobs: 14 | locks: 15 | if: 'false' 16 | runs-on: nothing 17 | steps: 18 | - name: EnricoMi/publish-unit-test-result-action@v2 19 | uses: EnricoMi/publish-unit-test-result-action@34d7c956a59aed1bfebf31df77b8de55db9bbaaf # v2 20 | - name: actions/checkout@v6 21 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 22 | - name: actions/create-github-app-token@v2 23 | uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2 24 | - name: actions/download-artifact@v6 25 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6 26 | - name: actions/upload-artifact@v5 27 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 28 | - name: dawidd6/action-download-artifact@v11 29 | uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11 30 | - name: ruby/setup-ruby@v1 31 | uses: ruby/setup-ruby@211ffaaa5f8dda97e9e8bca4e70d0fbaf2f8c41c # v1 32 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/HiddenProperties.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `HiddenProperties`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum HiddenProperties {} 5 | 6 | extension HiddenProperties { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "HiddenProperties" 9 | 10 | public var propC: String 11 | 12 | public init(propC: String) { 13 | self.propC = propC 14 | } 15 | } 16 | 17 | /// Load the Pkl module at the given source and evaluate it into `HiddenProperties.Module`. 18 | /// 19 | /// - Parameter source: The source of the Pkl module. 20 | public static func loadFrom(source: ModuleSource) async throws -> HiddenProperties.Module { 21 | try await PklSwift.withEvaluator { evaluator in 22 | try await loadFrom(evaluator: evaluator, source: source) 23 | } 24 | } 25 | 26 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 27 | /// `HiddenProperties.Module`. 28 | /// 29 | /// - Parameter evaluator: The evaluator to use for evaluation. 30 | /// - Parameter source: The module to evaluate. 31 | public static func loadFrom( 32 | evaluator: PklSwift.Evaluator, 33 | source: PklSwift.ModuleSource 34 | ) async throws -> HiddenProperties.Module { 35 | try await evaluator.evaluateModule(source: source, as: Module.self) 36 | } 37 | } -------------------------------------------------------------------------------- /codegen/snippet-tests/output/ExtendModule.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `ExtendModule`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum ExtendModule {} 5 | 6 | extension ExtendModule { 7 | public struct Module: MyModule.Module { 8 | public static let registeredIdentifier: String = "ExtendModule" 9 | 10 | public var bar: String 11 | 12 | public var foo: String 13 | 14 | public init(bar: String, foo: String) { 15 | self.bar = bar 16 | self.foo = foo 17 | } 18 | } 19 | 20 | /// Load the Pkl module at the given source and evaluate it into `ExtendModule.Module`. 21 | /// 22 | /// - Parameter source: The source of the Pkl module. 23 | public static func loadFrom(source: ModuleSource) async throws -> ExtendModule.Module { 24 | try await PklSwift.withEvaluator { evaluator in 25 | try await loadFrom(evaluator: evaluator, source: source) 26 | } 27 | } 28 | 29 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 30 | /// `ExtendModule.Module`. 31 | /// 32 | /// - Parameter evaluator: The evaluator to use for evaluation. 33 | /// - Parameter source: The module to evaluate. 34 | public static func loadFrom( 35 | evaluator: PklSwift.Evaluator, 36 | source: PklSwift.ModuleSource 37 | ) async throws -> ExtendModule.Module { 38 | try await evaluator.evaluateModule(source: source, as: Module.self) 39 | } 40 | } -------------------------------------------------------------------------------- /codegen/snippet-tests/output/TypeAliased.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `TypeAliased`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum TypeAliased {} 5 | 6 | extension TypeAliased { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "TypeAliased" 9 | 10 | public var myMap: StringyMap 11 | 12 | public init(myMap: StringyMap) { 13 | self.myMap = myMap 14 | } 15 | } 16 | 17 | public typealias StringyMap = [String: String] 18 | 19 | /// Load the Pkl module at the given source and evaluate it into `TypeAliased.Module`. 20 | /// 21 | /// - Parameter source: The source of the Pkl module. 22 | public static func loadFrom(source: ModuleSource) async throws -> TypeAliased.Module { 23 | try await PklSwift.withEvaluator { evaluator in 24 | try await loadFrom(evaluator: evaluator, source: source) 25 | } 26 | } 27 | 28 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 29 | /// `TypeAliased.Module`. 30 | /// 31 | /// - Parameter evaluator: The evaluator to use for evaluation. 32 | /// - Parameter source: The module to evaluate. 33 | public static func loadFrom( 34 | evaluator: PklSwift.Evaluator, 35 | source: PklSwift.ModuleSource 36 | ) async throws -> TypeAliased.Module { 37 | try await evaluator.evaluateModule(source: source, as: Module.self) 38 | } 39 | } -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Generated/ExtendedModule.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `ExtendedModule`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum ExtendedModule {} 5 | 6 | extension ExtendedModule { 7 | public struct Module: OpenModule.Module { 8 | public static let registeredIdentifier: String = "ExtendedModule" 9 | 10 | public var foo: String 11 | 12 | public var bar: Int 13 | 14 | public init(foo: String, bar: Int) { 15 | self.foo = foo 16 | self.bar = bar 17 | } 18 | } 19 | 20 | /// Load the Pkl module at the given source and evaluate it into `ExtendedModule.Module`. 21 | /// 22 | /// - Parameter source: The source of the Pkl module. 23 | public static func loadFrom(source: ModuleSource) async throws -> ExtendedModule.Module { 24 | try await PklSwift.withEvaluator { evaluator in 25 | try await loadFrom(evaluator: evaluator, source: source) 26 | } 27 | } 28 | 29 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 30 | /// `ExtendedModule.Module`. 31 | /// 32 | /// - Parameter evaluator: The evaluator to use for evaluation. 33 | /// - Parameter source: The module to evaluate. 34 | public static func loadFrom( 35 | evaluator: PklSwift.Evaluator, 36 | source: PklSwift.ModuleSource 37 | ) async throws -> ExtendedModule.Module { 38 | try await evaluator.evaluateModule(source: source, as: Module.self) 39 | } 40 | } -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Generated/UnusedClass.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `UnusedClass`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum UnusedClass {} 5 | 6 | extension UnusedClass { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "UnusedClass" 9 | 10 | public var referencedAlias: UnusedClassDefs.ReferencedAlias 11 | 12 | public init(referencedAlias: UnusedClassDefs.ReferencedAlias) { 13 | self.referencedAlias = referencedAlias 14 | } 15 | } 16 | 17 | /// Load the Pkl module at the given source and evaluate it into `UnusedClass.Module`. 18 | /// 19 | /// - Parameter source: The source of the Pkl module. 20 | public static func loadFrom(source: ModuleSource) async throws -> UnusedClass.Module { 21 | try await PklSwift.withEvaluator { evaluator in 22 | try await loadFrom(evaluator: evaluator, source: source) 23 | } 24 | } 25 | 26 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 27 | /// `UnusedClass.Module`. 28 | /// 29 | /// - Parameter evaluator: The evaluator to use for evaluation. 30 | /// - Parameter source: The module to evaluate. 31 | public static func loadFrom( 32 | evaluator: PklSwift.Evaluator, 33 | source: PklSwift.ModuleSource 34 | ) async throws -> UnusedClass.Module { 35 | try await evaluator.evaluateModule(source: source, as: Module.self) 36 | } 37 | } -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/external-readers.adoc: -------------------------------------------------------------------------------- 1 | = External Readers 2 | 3 | pkl-swift provides APIs that aid in implementing xref:main:language-reference:index.adoc#external-readers[External Readers]. 4 | In this mode of execution, the program built with pkl-swift runs as a child process of the Pkl evaluator, rather than a parent process. 5 | The `PklSwift.ExternalReaderClient` type provides a set of tools for building external readers. 6 | 7 | Much like implementing xref:ROOT:evaluation.adoc#custom-readers[Custom Readers], external readers are implemented by providing one or more instances of the `PklSwift.ResourceReader` and `pkl.ModuleReader` protocols. 8 | 9 | == Example 10 | 11 | .main.swift 12 | [source,swift] 13 | ---- 14 | import Foundation 15 | import PklSwift 16 | 17 | @main struct Main { 18 | static func main() async throws { 19 | let client = ExternalReaderClient( 20 | options: ExternalReaderClientOptions( 21 | resourceReaders: [MyResourceReader()] 22 | )) 23 | try await client.run() 24 | } 25 | } 26 | 27 | struct MyResourceReader: ResourceReader { 28 | var scheme: String { "env2" } 29 | var isGlobbable: Bool { false } 30 | var hasHierarchicalUris: Bool { false } 31 | func listElements(uri: URL) async throws -> [PathElement] { throw PklError("not implemented") } 32 | func read(url: URL) async throws -> [UInt8] { 33 | let key = url.absoluteString.dropFirst(scheme.count + 1) 34 | return Array((ProcessInfo.processInfo.environment[String(key)] ?? "").utf8) 35 | } 36 | } 37 | ---- 38 | -------------------------------------------------------------------------------- /Sources/MessagePack/FixedWidthInteger.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | extension FixedWidthInteger { 18 | init(bytes: [UInt8]) { 19 | self = 20 | bytes.withUnsafeBufferPointer { 21 | $0.baseAddress!.withMemoryRebound(to: Self.self, capacity: 1) { 22 | $0.pointee 23 | } 24 | }.bigEndian 25 | } 26 | 27 | var bytes: [UInt8] { 28 | let capacity = MemoryLayout.size 29 | var mutableValue = self.bigEndian 30 | return withUnsafePointer(to: &mutableValue) { 31 | $0.withMemoryRebound(to: UInt8.self, capacity: capacity) { 32 | Array(UnsafeBufferPointer(start: $0, count: capacity)) 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/API/DataSizeTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import XCTest 18 | 19 | @testable import PklSwift 20 | 21 | class DataSizeTests: XCTestCase { 22 | func testStringify() { 23 | XCTAssertEqual(String(describing: DataSize.kilobytes(15)), "15.kb") 24 | XCTAssertEqual(String(describing: DataSize.kilobytes(3.1415926)), "3.1415926.kb") 25 | XCTAssertEqual(String(describing: DataSize.kilobytes(1_000_000_000_000)), "1000000000000.kb") 26 | } 27 | 28 | func testToUnit() { 29 | let oneKb = DataSize.kilobytes(1000) 30 | XCTAssertEqual(oneKb.toUnit(.mb), .megabytes(1)) 31 | XCTAssertEqual(oneKb.toUnit(.b), .bytes(1_000_000)) 32 | XCTAssertEqual(oneKb.toUnit(.gb), .gigabytes(0.001)) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Generated/pkl_swift_lib1.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `pkl.swift.lib1`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum pkl_swift_lib1 {} 5 | 6 | public protocol pkl_swift_lib1_Being: PklRegisteredType, DynamicallyEquatable, Hashable, Sendable { 7 | var exists: Bool { get } 8 | } 9 | 10 | extension pkl_swift_lib1 { 11 | public typealias Being = pkl_swift_lib1_Being 12 | 13 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 14 | public static let registeredIdentifier: String = "pkl.swift.lib1" 15 | 16 | public init() {} 17 | } 18 | 19 | /// Load the Pkl module at the given source and evaluate it into `pkl_swift_lib1.Module`. 20 | /// 21 | /// - Parameter source: The source of the Pkl module. 22 | public static func loadFrom(source: ModuleSource) async throws -> pkl_swift_lib1.Module { 23 | try await PklSwift.withEvaluator { evaluator in 24 | try await loadFrom(evaluator: evaluator, source: source) 25 | } 26 | } 27 | 28 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 29 | /// `pkl_swift_lib1.Module`. 30 | /// 31 | /// - Parameter evaluator: The evaluator to use for evaluation. 32 | /// - Parameter source: The module to evaluate. 33 | public static func loadFrom( 34 | evaluator: PklSwift.Evaluator, 35 | source: PklSwift.ModuleSource 36 | ) async throws -> pkl_swift_lib1.Module { 37 | try await evaluator.evaluateModule(source: source, as: Module.self) 38 | } 39 | } -------------------------------------------------------------------------------- /Tests/PklSwiftTests/API/DurationTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import PklSwift 18 | import XCTest 19 | 20 | class DurationTests: XCTestCase { 21 | func testStringify() { 22 | XCTAssertEqual(String(describing: Duration(42, unit: .ms)), "42.ms") 23 | XCTAssertEqual(String(describing: Duration(42, unit: .min)), "42.min") 24 | XCTAssertEqual(String(describing: Duration(42, unit: .d)), "42.d") 25 | } 26 | 27 | func testToUnit() { 28 | let phiDays = Duration(1.618, unit: .d) 29 | XCTAssertEqual(phiDays.toUnit(.h), Duration(38.832, unit: .h)) 30 | XCTAssertEqual(phiDays.toUnit(.minutes), Duration(2329.92, unit: .min)) 31 | XCTAssertEqual(phiDays.toUnit(.milliseconds), Duration(139_795_200, unit: .ms)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CONTRIBUTING.adoc: -------------------------------------------------------------------------------- 1 | :uri-github-issue-pkl-swift: https://github.com/apple/pkl-swift/issues/new 2 | :uri-seven-rules: https://cbea.ms/git-commit/#seven-rules 3 | 4 | = Pkl Swift Contributors Guide 5 | 6 | Welcome to the Pkl community, and thank you for contributing! 7 | This guide explains how to get involved. 8 | 9 | * <> 10 | * <> 11 | * <> 12 | 13 | == Licensing 14 | 15 | Pkl Swift is released under the Apache 2.0 license. 16 | This is why we require that, by submitting a pull request, you acknowledge that you have the right to license your contribution to Apple and the community, and agree that your contribution is licensed under the Apache 2.0 license. 17 | 18 | == Issue Tracking 19 | 20 | To file a bug or feature request, use {uri-github-issue-pkl-swift}[GitHub]. 21 | Be sure to include the following information: 22 | 23 | * Context 24 | ** What are/were you trying to achieve? 25 | ** What's the impact of this bug/feature? 26 | 27 | == Pull Requests 28 | 29 | When preparing a pull request, follow this checklist: 30 | 31 | * Imitate the conventions of surrounding code. 32 | * Follow the {uri-seven-rules}[seven rules] of great Git commit messages: 33 | ** Separate subject from body with a blank line. 34 | ** Limit the subject line to 50 characters. 35 | ** Capitalize the subject line. 36 | ** Do not end the subject line with a period. 37 | ** Use the imperative mood in the subject line. 38 | ** Wrap the body at 72 characters. 39 | ** Use the body to explain what and why vs. how. 40 | 41 | == Maintainers 42 | 43 | The project’s maintainers (those with write access to the upstream repository) are listed in link:MAINTAINERS.adoc[]. 44 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Generated/UnusedClassDefs.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `UnusedClassDefs`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum UnusedClassDefs {} 5 | 6 | extension UnusedClassDefs { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "UnusedClassDefs" 9 | 10 | public init() {} 11 | } 12 | 13 | public struct ThisClassShouldAlsoGenerate: PklRegisteredType, Decodable, Hashable, Sendable { 14 | public static let registeredIdentifier: String = "UnusedClassDefs#ThisClassShouldAlsoGenerate" 15 | 16 | public init() {} 17 | } 18 | 19 | public typealias ReferencedAlias = String 20 | 21 | /// Load the Pkl module at the given source and evaluate it into `UnusedClassDefs.Module`. 22 | /// 23 | /// - Parameter source: The source of the Pkl module. 24 | public static func loadFrom(source: ModuleSource) async throws -> UnusedClassDefs.Module { 25 | try await PklSwift.withEvaluator { evaluator in 26 | try await loadFrom(evaluator: evaluator, source: source) 27 | } 28 | } 29 | 30 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 31 | /// `UnusedClassDefs.Module`. 32 | /// 33 | /// - Parameter evaluator: The evaluator to use for evaluation. 34 | /// - Parameter source: The module to evaluate. 35 | public static func loadFrom( 36 | evaluator: PklSwift.Evaluator, 37 | source: PklSwift.ModuleSource 38 | ) async throws -> UnusedClassDefs.Module { 39 | try await evaluator.evaluateModule(source: source, as: Module.self) 40 | } 41 | } -------------------------------------------------------------------------------- /codegen/snippet-tests/output/UnionNameKeyword.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `UnionNameKeyword`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum UnionNameKeyword {} 5 | 6 | extension UnionNameKeyword { 7 | public enum `Type`: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable, Sendable { 8 | case one = "one" 9 | case two = "two" 10 | case three = "three" 11 | } 12 | 13 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 14 | public static let registeredIdentifier: String = "UnionNameKeyword" 15 | 16 | public var type: `Type` 17 | 18 | public init(type: `Type`) { 19 | self.type = type 20 | } 21 | } 22 | 23 | /// Load the Pkl module at the given source and evaluate it into `UnionNameKeyword.Module`. 24 | /// 25 | /// - Parameter source: The source of the Pkl module. 26 | public static func loadFrom(source: ModuleSource) async throws -> UnionNameKeyword.Module { 27 | try await PklSwift.withEvaluator { evaluator in 28 | try await loadFrom(evaluator: evaluator, source: source) 29 | } 30 | } 31 | 32 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 33 | /// `UnionNameKeyword.Module`. 34 | /// 35 | /// - Parameter evaluator: The evaluator to use for evaluation. 36 | /// - Parameter source: The module to evaluate. 37 | public static func loadFrom( 38 | evaluator: PklSwift.Evaluator, 39 | source: PklSwift.ModuleSource 40 | ) async throws -> UnionNameKeyword.Module { 41 | try await evaluator.evaluateModule(source: source, as: Module.self) 42 | } 43 | } -------------------------------------------------------------------------------- /codegen/src/PklProject: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | amends "pkl:Project" 18 | 19 | package { 20 | name = "pkl.swift" 21 | baseUri = "package://pkg.pkl-lang.org/pkl-swift/\(name)" 22 | packageZipUrl = 23 | "https://github.com/apple/pkl-swift/releases/download/\(name)@\(version)/\(name)@\(version).zip" 24 | version = read("../../VERSION.txt").text.trim() 25 | authors { 26 | "The Pkl Authors " 27 | } 28 | sourceCodeUrlScheme = 29 | "https://github.com/apple/pkl-swift/blob/\(version)/codegen/src%{path}#L%{line}-L%{endLine}" 30 | sourceCode = "https://github.com/apple/pkl-swift" 31 | description = "Pkl bindings for the Swift programming language" 32 | license = "Apache-2.0" 33 | exclude { 34 | "tests" 35 | "tests/**" 36 | } 37 | } 38 | 39 | tests { 40 | for (key, _ in import*("tests/*.pkl")) { 41 | key 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Generated/Classes.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `Classes`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum Classes {} 5 | 6 | extension Classes { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "Classes" 9 | 10 | public var animals: [Animal] 11 | 12 | public init(animals: [Animal]) { 13 | self.animals = animals 14 | } 15 | } 16 | 17 | public struct Animal: PklRegisteredType, Decodable, Hashable, Sendable { 18 | public static let registeredIdentifier: String = "Classes#Animal" 19 | 20 | public var name: String 21 | 22 | public init(name: String) { 23 | self.name = name 24 | } 25 | } 26 | 27 | /// Load the Pkl module at the given source and evaluate it into `Classes.Module`. 28 | /// 29 | /// - Parameter source: The source of the Pkl module. 30 | public static func loadFrom(source: ModuleSource) async throws -> Classes.Module { 31 | try await PklSwift.withEvaluator { evaluator in 32 | try await loadFrom(evaluator: evaluator, source: source) 33 | } 34 | } 35 | 36 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 37 | /// `Classes.Module`. 38 | /// 39 | /// - Parameter evaluator: The evaluator to use for evaluation. 40 | /// - Parameter source: The module to evaluate. 41 | public static func loadFrom( 42 | evaluator: PklSwift.Evaluator, 43 | source: PklSwift.ModuleSource 44 | ) async throws -> Classes.Module { 45 | try await evaluator.evaluateModule(source: source, as: Module.self) 46 | } 47 | } -------------------------------------------------------------------------------- /codegen/snippet-tests/output/lib3.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `lib3`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum lib3 {} 5 | 6 | public protocol lib3_GoGoGo: PklRegisteredType, DynamicallyEquatable, Hashable, Sendable { 7 | var duck: String { get } 8 | } 9 | 10 | extension lib3 { 11 | public typealias GoGoGo = lib3_GoGoGo 12 | 13 | public struct GoGoGoImpl: GoGoGo { 14 | public static let registeredIdentifier: String = "lib3#GoGoGo" 15 | 16 | public var duck: String 17 | 18 | public init(duck: String) { 19 | self.duck = duck 20 | } 21 | } 22 | 23 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 24 | public static let registeredIdentifier: String = "lib3" 25 | 26 | public init() {} 27 | } 28 | 29 | /// Load the Pkl module at the given source and evaluate it into `lib3.Module`. 30 | /// 31 | /// - Parameter source: The source of the Pkl module. 32 | public static func loadFrom(source: ModuleSource) async throws -> lib3.Module { 33 | try await PklSwift.withEvaluator { evaluator in 34 | try await loadFrom(evaluator: evaluator, source: source) 35 | } 36 | } 37 | 38 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 39 | /// `lib3.Module`. 40 | /// 41 | /// - Parameter evaluator: The evaluator to use for evaluation. 42 | /// - Parameter source: The module to evaluate. 43 | public static func loadFrom( 44 | evaluator: PklSwift.Evaluator, 45 | source: PklSwift.ModuleSource 46 | ) async throws -> lib3.Module { 47 | try await evaluator.evaluateModule(source: source, as: Module.self) 48 | } 49 | } -------------------------------------------------------------------------------- /codegen/src/tests/fixtures/types.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | class Bike { 18 | isFixie: Boolean 19 | } 20 | 21 | abstract class Being { 22 | isAlive: Boolean 23 | } 24 | 25 | /// A Person! 26 | open class Person extends Being { 27 | bike: Bike 28 | 29 | /// The person's first name 30 | firstName: UInt16? 31 | 32 | /// The person's last name 33 | lastName: Mapping 34 | } 35 | 36 | typealias BugKind = "butterfly" | "beetle\"" | "beetle one" | "beetle_one" 37 | 38 | class Bug { 39 | /// The owner of this bug. 40 | owner: Person? 41 | 42 | secondOwner: Person 43 | 44 | /// The age of this bug 45 | age: Int? 46 | 47 | /// How long the bug holds its breath for 48 | holdsBreathFor: Duration 49 | 50 | size: DataSize 51 | 52 | kind: BugKind 53 | 54 | bugClass: Class 55 | 56 | bugTypeAlias: TypeAlias 57 | } 58 | 59 | class Cyclic { 60 | a: String 61 | 62 | b: Int 63 | 64 | myself: Cyclic 65 | } 66 | -------------------------------------------------------------------------------- /Sources/MessagePack/AnyCodingKey.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | struct AnyCodingKey: CodingKey, Equatable { 18 | let stringValue: String 19 | let intValue: Int? 20 | 21 | init?(stringValue: String) { 22 | self.stringValue = stringValue 23 | self.intValue = nil 24 | } 25 | 26 | init?(intValue: Int) { 27 | self.stringValue = "\(intValue)" 28 | self.intValue = intValue 29 | } 30 | 31 | init(idx: Int) { 32 | self.intValue = idx 33 | self.stringValue = "idx \(idx)" 34 | } 35 | 36 | init(_ base: some CodingKey) { 37 | if let intValue = base.intValue { 38 | self.init(intValue: intValue)! 39 | } else { 40 | self.init(stringValue: base.stringValue)! 41 | } 42 | } 43 | } 44 | 45 | extension AnyCodingKey: Hashable { 46 | func hash(into hasher: inout Hasher) { 47 | self.intValue?.hash(into: &hasher) ?? self.stringValue.hash(into: &hasher) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/PklSwift/PklCodingKey.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | public struct PklCodingKey: CodingKey, Equatable, Sendable { 20 | public let stringValue: String 21 | public let intValue: Int? 22 | 23 | public init?(stringValue: String) { 24 | self.stringValue = stringValue 25 | self.intValue = nil 26 | } 27 | 28 | public init?(intValue: Int) { 29 | self.stringValue = "\(intValue)" 30 | self.intValue = intValue 31 | } 32 | 33 | public init(string: String) { 34 | self.stringValue = string 35 | self.intValue = nil 36 | } 37 | 38 | init(_ base: some CodingKey) { 39 | if let intValue = base.intValue { 40 | self.init(intValue: intValue)! 41 | } else { 42 | self.init(stringValue: base.stringValue)! 43 | } 44 | } 45 | } 46 | 47 | extension PklCodingKey: Hashable { 48 | public func hash(into hasher: inout Hasher) { 49 | self.intValue?.hash(into: &hasher) ?? self.stringValue.hash(into: &hasher) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /codegen/src/tests/gatherer.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | amends "pkl:test" 18 | 19 | import "pkl:reflect" 20 | 21 | import "../internal/gatherer.pkl" 22 | import "fixtures/types.pkl" 23 | import "fixtures/types2.pkl" 24 | import "fixtures/types4.pkl" 25 | 26 | // it's important that these classes are defined in another module because they gather the type 27 | // declarations of their enclosing module. 28 | facts { 29 | ["gather type declarations"] { 30 | gatherer.gatherTypeDeclarations(reflect.Class(types.Bug), List()).map((c) -> c.name) 31 | == List("Bug", "Person", "Bike", "ModuleClass", "Being", "Cyclic", "BugKind") 32 | } 33 | ["gather type declarations - listing arguments"] { 34 | gatherer.gatherTypeDeclarations(reflect.Class(types2.Person), List()).map((c) -> c.name) 35 | == List("Person", "Bike", "ModuleClass", "ModuleClass") 36 | } 37 | ["gather type declarations - type params with unions"] { 38 | gatherer.gatherTypeDeclarations(reflect.Class(types4.Foo), List()).map((c) -> c.name) 39 | == List("Foo", "ModuleClass") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "semanticversion", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/SwiftPackageIndex/SemanticVersion", 7 | "state" : { 8 | "revision" : "ea8eea9d89842a29af1b8e6c7677f1c86e72fa42", 9 | "version" : "0.4.0" 10 | } 11 | }, 12 | { 13 | "identity" : "swift-argument-parser", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/apple/swift-argument-parser", 16 | "state" : { 17 | "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", 18 | "version" : "1.2.3" 19 | } 20 | }, 21 | { 22 | "identity" : "swift-docc-plugin", 23 | "kind" : "remoteSourceControl", 24 | "location" : "https://github.com/swiftlang/swift-docc-plugin", 25 | "state" : { 26 | "revision" : "85e4bb4e1cd62cec64a4b8e769dcefdf0c5b9d64", 27 | "version" : "1.4.3" 28 | } 29 | }, 30 | { 31 | "identity" : "swift-docc-symbolkit", 32 | "kind" : "remoteSourceControl", 33 | "location" : "https://github.com/swiftlang/swift-docc-symbolkit", 34 | "state" : { 35 | "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34", 36 | "version" : "1.0.0" 37 | } 38 | }, 39 | { 40 | "identity" : "swift-system", 41 | "kind" : "remoteSourceControl", 42 | "location" : "https://github.com/apple/swift-system", 43 | "state" : { 44 | "revision" : "025bcb1165deab2e20d4eaba79967ce73013f496", 45 | "version" : "1.2.1" 46 | } 47 | }, 48 | { 49 | "identity" : "swiftformat", 50 | "kind" : "remoteSourceControl", 51 | "location" : "https://github.com/nicklockwood/SwiftFormat", 52 | "state" : { 53 | "revision" : "468a7d32dedc8d352c191594b3b45d9fd8ba291b", 54 | "version" : "0.55.5" 55 | } 56 | } 57 | ], 58 | "version" : 2 59 | } 60 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/UnionTypes.pkl: -------------------------------------------------------------------------------- 1 | import "pkl:math" 2 | 3 | class Banana { 4 | isRipe: Boolean 5 | } 6 | 7 | class Grape { 8 | isUsedForWine: Boolean 9 | } 10 | 11 | class Apple { 12 | isRed: Boolean 13 | } 14 | 15 | abstract class Animal { 16 | name: String 17 | } 18 | 19 | typealias Fruit = Banana | Grape | Apple 20 | 21 | typealias City = "San Francisco" | "Tokyo" | "Zurich" | "London" 22 | 23 | abstract class Shape 24 | 25 | class Square extends Shape { 26 | fixed corners: Int = 4 27 | } 28 | 29 | fruit1: Fruit = new Banana { 30 | isRipe = true 31 | } 32 | 33 | fruit2: Fruit = new Grape { 34 | isUsedForWine = true 35 | } 36 | 37 | fruit3: Fruit = new Apple { 38 | isRed = false 39 | } 40 | 41 | city1: City = "San Francisco" 42 | 43 | city2: City = "Tokyo" 44 | 45 | city3: City = "Zurich" 46 | 47 | city4: City = "London" 48 | 49 | class Zebra extends Animal { 50 | name = "Zebra" 51 | } 52 | 53 | class Donkey extends Animal { 54 | name = "Donkey" 55 | } 56 | 57 | typealias ZebraOrDonkey = Zebra | Donkey 58 | 59 | animal1: ZebraOrDonkey = new Zebra {} 60 | 61 | animal2: ZebraOrDonkey = new Donkey {} 62 | 63 | typealias AnimalOrString = Animal | String 64 | 65 | animalOrString1: AnimalOrString = new Zebra {} 66 | 67 | animalOrString2: AnimalOrString = "Zebra" 68 | 69 | typealias IntOrFloat = Int | Float 70 | 71 | intOrFloat1: IntOrFloat = 5.0 72 | 73 | intOrFloat2: IntOrFloat = 5.5 74 | 75 | intOrFloat3: IntOrFloat = 128 76 | 77 | typealias Environment = "dev" | "prod" | "qa" 78 | 79 | config: Mapping = new { 80 | ["dev"] = "Imaginary Service Company (ISC) configuration" 81 | } 82 | 83 | typealias AnimalOrShape = Animal | Shape 84 | 85 | animalOrShape1: AnimalOrShape = new Donkey {} 86 | 87 | animalOrShape2: AnimalOrShape = new Square {} 88 | 89 | typealias Numbers = Int8 | Int16 | Int32 | Int 90 | 91 | numbers1: Numbers = 5 92 | 93 | numbers2: Numbers = 128 94 | 95 | numbers3: Numbers = 32768 96 | 97 | numbers4: Numbers = math.maxInt 98 | -------------------------------------------------------------------------------- /codegen/src/tests/utils.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | amends "pkl:test" 18 | 19 | import "../internal/utils.pkl" 20 | 21 | facts { 22 | ["normalizeName"] { 23 | utils.normalizeName("foo") == "foo" 24 | utils.normalizeName("foo foo") == "fooFoo" 25 | utils.normalizeName("1 foo") == "1Foo" 26 | utils.normalizeName("bar ` $$ 你好 baz") == "bar你好Baz" 27 | utils.normalizeName("Swift111") == "Swift111" 28 | utils.normalizeName("snake_case") == "snake_case" 29 | } 30 | ["toSwiftString"] { 31 | utils.toSwiftString("foo") == #""foo""# 32 | utils.toSwiftString("你好") == #""你好""# 33 | utils.toSwiftString(#"pkl:"name""#) == ##"#"pkl:"name""#"## 34 | utils.toSwiftString( 35 | """ 36 | my multiline 37 | 38 | string 39 | """ 40 | ) 41 | == #""" 42 | """ 43 | my multiline 44 | 45 | string 46 | """ 47 | """# 48 | utils.toSwiftString( 49 | """ 50 | multiline string 51 | 52 | with `backticks` 53 | """ 54 | ) 55 | == #""" 56 | """ 57 | multiline string 58 | 59 | with `backticks` 60 | """ 61 | """# 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/test-external-reader/TestExternalReader.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | import PklSwift 19 | 20 | @main 21 | struct TestExternalReader { 22 | static func main() async throws { 23 | let client = ExternalReaderClient( 24 | options: ExternalReaderClientOptions( 25 | resourceReaders: [FibReader()] 26 | )) 27 | try await client.run() 28 | } 29 | } 30 | 31 | struct FibReader: ResourceReader { 32 | var scheme: String { "fib" } 33 | var isGlobbable: Bool { false } 34 | var hasHierarchicalUris: Bool { false } 35 | func listElements(uri: URL) async throws -> [PathElement] { throw PklError("not implemented") } 36 | func read(url: URL) async throws -> [UInt8] { 37 | let key = url.absoluteString.dropFirst(self.scheme.count + 1) 38 | guard let n = Int(key), n > 0 else { 39 | throw PklError("input uri must be in format fib:") 40 | } 41 | return Array(String(fibonacci(n: n)).utf8) 42 | } 43 | } 44 | 45 | func fibonacci(n: Int) -> Int { 46 | var (a, b) = (0, 1) 47 | for _ in 0..#/swift.pkl" 34 | /// ``` 35 | class Module extends Annotation { 36 | /// The full name of the Swift module 37 | name: SwiftName 38 | } 39 | 40 | /// The name for the annotated member when generated to Swift. 41 | /// 42 | /// This may be used to annotate modules, classes, typealiases, and properties. 43 | /// 44 | /// If the annotation is applied to a module, it determines the name of the namespace (the `enum` that holds all the 45 | /// module's members). 46 | /// 47 | /// Examples: 48 | /// 49 | /// ``` 50 | /// @swift.Name { value = "ThePerson" } 51 | /// class Person 52 | /// ``` 53 | class Name extends Annotation { 54 | value: SwiftName 55 | } 56 | -------------------------------------------------------------------------------- /Sources/PklSwift/API/Class.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import MessagePack 18 | 19 | /// Class is the Swift representation of Pkl's `pkl.base#Class`. 20 | public struct Class: Hashable { 21 | /// The URI of the module containing this class. 22 | /// Will be an empty string for values encoded by Pkl versions older than 0.30. 23 | public let moduleUri: String 24 | 25 | /// The name of this class. 26 | /// Will be an empty string for values encoded by Pkl versions older than 0.30. 27 | public let name: String 28 | } 29 | 30 | extension Class: PklSerializableType, Sendable { 31 | public static let messageTag: PklValueType = .class 32 | 33 | public static func decode(_ fields: [MessagePackValue], codingPath: [any CodingKey]) throws -> Class { 34 | if fields.count > 1 { // pkl 0.30+ includes the name and module uri 35 | try checkFieldCount(fields, codingPath: codingPath, min: 3) 36 | return try Class( 37 | moduleUri: fields[2].decode(String.self), 38 | name: fields[1].decode(String.self) 39 | ) 40 | } 41 | 42 | try checkFieldCount(fields, codingPath: codingPath, min: 1) 43 | return Class(moduleUri: "", name: "") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/ExplicitlyCoolName.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `ExplicitName`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum ExplicitlyCoolName {} 5 | 6 | extension ExplicitlyCoolName { 7 | public enum ConfigType: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable, Sendable { 8 | case one = "one" 9 | case two = "two" 10 | } 11 | 12 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 13 | public static let registeredIdentifier: String = "ExplicitName" 14 | 15 | public var MyCoolProp: SomethingVeryFunny 16 | 17 | public init(MyCoolProp: SomethingVeryFunny) { 18 | self.MyCoolProp = MyCoolProp 19 | } 20 | 21 | enum CodingKeys: String, CodingKey { 22 | case MyCoolProp = "myProp" 23 | } 24 | } 25 | 26 | public struct SomethingVeryFunny: PklRegisteredType, Decodable, Hashable, Sendable { 27 | public static let registeredIdentifier: String = "ExplicitName#SomethingFunny" 28 | 29 | public init() {} 30 | } 31 | 32 | /// Load the Pkl module at the given source and evaluate it into `ExplicitlyCoolName.Module`. 33 | /// 34 | /// - Parameter source: The source of the Pkl module. 35 | public static func loadFrom(source: ModuleSource) async throws -> ExplicitlyCoolName.Module { 36 | try await PklSwift.withEvaluator { evaluator in 37 | try await loadFrom(evaluator: evaluator, source: source) 38 | } 39 | } 40 | 41 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 42 | /// `ExplicitlyCoolName.Module`. 43 | /// 44 | /// - Parameter evaluator: The evaluator to use for evaluation. 45 | /// - Parameter source: The module to evaluate. 46 | public static func loadFrom( 47 | evaluator: PklSwift.Evaluator, 48 | source: PklSwift.ModuleSource 49 | ) async throws -> ExplicitlyCoolName.Module { 50 | try await evaluator.evaluateModule(source: source, as: Module.self) 51 | } 52 | } -------------------------------------------------------------------------------- /Sources/PklSwift/API/TypeAlias.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import MessagePack 18 | 19 | /// TypeAlias is the Swift representation of Pkl's `pkl.base#TypeAlias`. 20 | public struct TypeAlias: Hashable { 21 | /// The URI of the module containing this typealias. 22 | /// Will be an empty string for values encoded by Pkl versions older than 0.30. 23 | public let moduleUri: String 24 | 25 | /// The name of this typealias. 26 | /// Will be an empty string for values encoded by Pkl versions older than 0.30. 27 | public let name: String 28 | } 29 | 30 | extension TypeAlias: PklSerializableType, Sendable { 31 | public static let messageTag: PklValueType = .typealias 32 | 33 | public static func decode(_ fields: [MessagePackValue], codingPath: [any CodingKey]) throws -> TypeAlias { 34 | if fields.count > 1 { // pkl 0.30+ includes the name and module uri 35 | try checkFieldCount(fields, codingPath: codingPath, min: 3) 36 | return try TypeAlias( 37 | moduleUri: fields[2].decode(String.self), 38 | name: fields[1].decode(String.self) 39 | ) 40 | } 41 | 42 | try checkFieldCount(fields, codingPath: codingPath, min: 1) 43 | return TypeAlias(moduleUri: "", name: "") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/com_example_ExtendedSimple.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `com.example.ExtendedSimple`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum com_example_ExtendedSimple {} 5 | 6 | extension com_example_ExtendedSimple { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "com.example.ExtendedSimple" 9 | 10 | public init() {} 11 | } 12 | 13 | public struct ExtendedSimple: com_example_Simple.Person { 14 | public static let registeredIdentifier: String = "com.example.ExtendedSimple#ExtendedSimple" 15 | 16 | public var eyeColor: String 17 | 18 | /// The name of the person 19 | public var theName: String 20 | 21 | /// Some name that matches a keyword 22 | public var `enum`: String 23 | 24 | public init(eyeColor: String, theName: String, `enum`: String) { 25 | self.eyeColor = eyeColor 26 | self.theName = theName 27 | self.`enum` = `enum` 28 | } 29 | 30 | enum CodingKeys: String, CodingKey { 31 | case eyeColor = "eyeColor" 32 | case theName = "the name" 33 | case `enum` = "enum" 34 | } 35 | } 36 | 37 | /// Load the Pkl module at the given source and evaluate it into `com_example_ExtendedSimple.Module`. 38 | /// 39 | /// - Parameter source: The source of the Pkl module. 40 | public static func loadFrom(source: ModuleSource) async throws -> com_example_ExtendedSimple.Module { 41 | try await PklSwift.withEvaluator { evaluator in 42 | try await loadFrom(evaluator: evaluator, source: source) 43 | } 44 | } 45 | 46 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 47 | /// `com_example_ExtendedSimple.Module`. 48 | /// 49 | /// - Parameter evaluator: The evaluator to use for evaluation. 50 | /// - Parameter source: The module to evaluate. 51 | public static func loadFrom( 52 | evaluator: PklSwift.Evaluator, 53 | source: PklSwift.ModuleSource 54 | ) async throws -> com_example_ExtendedSimple.Module { 55 | try await evaluator.evaluateModule(source: source, as: Module.self) 56 | } 57 | } -------------------------------------------------------------------------------- /codegen/src/internal/Gen.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | @Unlisted 18 | abstract module pkl.swift.internal.Gen 19 | 20 | import "SwiftMapping.pkl" 21 | import "Type.pkl" 22 | 23 | /// The generated contents for this particular mapping. 24 | mapping: SwiftMapping 25 | 26 | /// All mappings 27 | mappings: List 28 | 29 | /// The Swift source code 30 | contents: String 31 | 32 | /// Swift source code to be put outside the extension, at the top level 33 | topLevelContents: String? 34 | 35 | /// The name of the namespace 36 | namespaceName: String 37 | 38 | /// The indentation applied to generated Swift code. 39 | indent: String 40 | 41 | function renderInnerHash(type: Type, rpt: Int, name: String, isDictKey: Boolean): String = 42 | if (type is Type.Array) 43 | """ 44 | \(module.indent.repeat(rpt))for x in \(name) { 45 | \(renderInnerHash(type.elem, rpt + 1, "x", false)) 46 | \(module.indent.repeat(rpt))} 47 | """ 48 | else if (type is Type.Dictionary) 49 | """ 50 | \(module.indent.repeat(rpt))for (k, v) in \(name) { 51 | \(renderInnerHash(type.key, rpt + 1, "k", type.key is Type.Optional)) 52 | \(renderInnerHash(type.elem, rpt + 1, "v", false)) 53 | \(module.indent.repeat(rpt))} 54 | """ 55 | else if (type is Type.Optional && !isDictKey) 56 | """ 57 | \(module.indent.repeat(rpt))if let \(name)\(if (isDictKey) " = \(name).key" else "") { 58 | \(renderInnerHash(type.elem, rpt + 1, name, false)) 59 | \(module.indent.repeat(rpt))} 60 | """ 61 | else 62 | """ 63 | \(module.indent.repeat(rpt))hasher.combine(\(name)) 64 | """ 65 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/override.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `override`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum override {} 5 | 6 | public protocol override_Foo: PklRegisteredType, DynamicallyEquatable, Hashable, Sendable { 7 | var myProp: String { get } 8 | } 9 | 10 | extension override { 11 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 12 | public static let registeredIdentifier: String = "override" 13 | 14 | public var foo: any Foo 15 | 16 | public init(foo: any Foo) { 17 | self.foo = foo 18 | } 19 | 20 | public static func ==(lhs: Module, rhs: Module) -> Bool { 21 | lhs.foo.isDynamicallyEqual(to: rhs.foo) 22 | } 23 | 24 | public func hash(into hasher: inout Hasher) { 25 | hasher.combine(foo) 26 | } 27 | 28 | public init(from decoder: any Decoder) throws { 29 | let dec = try decoder.container(keyedBy: PklCodingKey.self) 30 | let foo = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "foo")) 31 | .value as! any Foo 32 | self = Module(foo: foo) 33 | } 34 | } 35 | 36 | public typealias Foo = override_Foo 37 | 38 | public struct Bar: Foo { 39 | public static let registeredIdentifier: String = "override#Bar" 40 | 41 | public var myProp: String 42 | 43 | public init(myProp: String) { 44 | self.myProp = myProp 45 | } 46 | } 47 | 48 | /// Load the Pkl module at the given source and evaluate it into `override.Module`. 49 | /// 50 | /// - Parameter source: The source of the Pkl module. 51 | public static func loadFrom(source: ModuleSource) async throws -> override.Module { 52 | try await PklSwift.withEvaluator { evaluator in 53 | try await loadFrom(evaluator: evaluator, source: source) 54 | } 55 | } 56 | 57 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 58 | /// `override.Module`. 59 | /// 60 | /// - Parameter evaluator: The evaluator to use for evaluation. 61 | /// - Parameter source: The module to evaluate. 62 | public static func loadFrom( 63 | evaluator: PklSwift.Evaluator, 64 | source: PklSwift.ModuleSource 65 | ) async throws -> override.Module { 66 | try await evaluator.evaluateModule(source: source, as: Module.self) 67 | } 68 | } -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Generated/ApiTypes.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `ApiTypes`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum ApiTypes {} 5 | 6 | extension ApiTypes { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "ApiTypes" 9 | 10 | public var res1: Duration 11 | 12 | public var res2: DataSize 13 | 14 | public var stringClass: Class 15 | 16 | public var baseModuleClass: Class 17 | 18 | public var uint8TypeAlias: TypeAlias 19 | 20 | public var fooClass: Class 21 | 22 | public var barTypeAlias: TypeAlias 23 | 24 | public init( 25 | res1: Duration, 26 | res2: DataSize, 27 | stringClass: Class, 28 | baseModuleClass: Class, 29 | uint8TypeAlias: TypeAlias, 30 | fooClass: Class, 31 | barTypeAlias: TypeAlias 32 | ) { 33 | self.res1 = res1 34 | self.res2 = res2 35 | self.stringClass = stringClass 36 | self.baseModuleClass = baseModuleClass 37 | self.uint8TypeAlias = uint8TypeAlias 38 | self.fooClass = fooClass 39 | self.barTypeAlias = barTypeAlias 40 | } 41 | } 42 | 43 | public struct Foo: PklRegisteredType, Decodable, Hashable, Sendable { 44 | public static let registeredIdentifier: String = "ApiTypes#Foo" 45 | 46 | public init() {} 47 | } 48 | 49 | public typealias Bar = Foo 50 | 51 | /// Load the Pkl module at the given source and evaluate it into `ApiTypes.Module`. 52 | /// 53 | /// - Parameter source: The source of the Pkl module. 54 | public static func loadFrom(source: ModuleSource) async throws -> ApiTypes.Module { 55 | try await PklSwift.withEvaluator { evaluator in 56 | try await loadFrom(evaluator: evaluator, source: source) 57 | } 58 | } 59 | 60 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 61 | /// `ApiTypes.Module`. 62 | /// 63 | /// - Parameter evaluator: The evaluator to use for evaluation. 64 | /// - Parameter source: The module to evaluate. 65 | public static func loadFrom( 66 | evaluator: PklSwift.Evaluator, 67 | source: PklSwift.ModuleSource 68 | ) async throws -> ApiTypes.Module { 69 | try await evaluator.evaluateModule(source: source, as: Module.self) 70 | } 71 | } -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Generated/Collections.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `Collections`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum Collections {} 5 | 6 | extension Collections { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 8 | public static let registeredIdentifier: String = "Collections" 9 | 10 | public var res1: [Int] 11 | 12 | public var res2: [Int] 13 | 14 | public var res3: [[Int]] 15 | 16 | public var res4: [[Int]] 17 | 18 | public var res5: [Int: Bool] 19 | 20 | public var res6: [Int: [Int: Bool]] 21 | 22 | public var res7: [Int: Bool] 23 | 24 | public var res8: [Int: [Int: Bool]] 25 | 26 | public var res9: Set 27 | 28 | public var res10: Set 29 | 30 | public init( 31 | res1: [Int], 32 | res2: [Int], 33 | res3: [[Int]], 34 | res4: [[Int]], 35 | res5: [Int: Bool], 36 | res6: [Int: [Int: Bool]], 37 | res7: [Int: Bool], 38 | res8: [Int: [Int: Bool]], 39 | res9: Set, 40 | res10: Set 41 | ) { 42 | self.res1 = res1 43 | self.res2 = res2 44 | self.res3 = res3 45 | self.res4 = res4 46 | self.res5 = res5 47 | self.res6 = res6 48 | self.res7 = res7 49 | self.res8 = res8 50 | self.res9 = res9 51 | self.res10 = res10 52 | } 53 | } 54 | 55 | /// Load the Pkl module at the given source and evaluate it into `Collections.Module`. 56 | /// 57 | /// - Parameter source: The source of the Pkl module. 58 | public static func loadFrom(source: ModuleSource) async throws -> Collections.Module { 59 | try await PklSwift.withEvaluator { evaluator in 60 | try await loadFrom(evaluator: evaluator, source: source) 61 | } 62 | } 63 | 64 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 65 | /// `Collections.Module`. 66 | /// 67 | /// - Parameter evaluator: The evaluator to use for evaluation. 68 | /// - Parameter source: The module to evaluate. 69 | public static func loadFrom( 70 | evaluator: PklSwift.Evaluator, 71 | source: PklSwift.ModuleSource 72 | ) async throws -> Collections.Module { 73 | try await evaluator.evaluateModule(source: source, as: Module.self) 74 | } 75 | } -------------------------------------------------------------------------------- /codegen/snippet-tests/output/ExtendsAbstractClass.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `ExtendsAbstractClass`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum ExtendsAbstractClass {} 5 | 6 | public protocol ExtendsAbstractClass_A: PklRegisteredType, DynamicallyEquatable, Hashable, Sendable { 7 | var b: String { get } 8 | } 9 | 10 | extension ExtendsAbstractClass { 11 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 12 | public static let registeredIdentifier: String = "ExtendsAbstractClass" 13 | 14 | public var a: any A 15 | 16 | public init(a: any A) { 17 | self.a = a 18 | } 19 | 20 | public static func ==(lhs: Module, rhs: Module) -> Bool { 21 | lhs.a.isDynamicallyEqual(to: rhs.a) 22 | } 23 | 24 | public func hash(into hasher: inout Hasher) { 25 | hasher.combine(a) 26 | } 27 | 28 | public init(from decoder: any Decoder) throws { 29 | let dec = try decoder.container(keyedBy: PklCodingKey.self) 30 | let a = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "a")) 31 | .value as! any A 32 | self = Module(a: a) 33 | } 34 | } 35 | 36 | public typealias A = ExtendsAbstractClass_A 37 | 38 | public struct B: A { 39 | public static let registeredIdentifier: String = "ExtendsAbstractClass#B" 40 | 41 | public var c: String 42 | 43 | public var b: String 44 | 45 | public init(c: String, b: String) { 46 | self.c = c 47 | self.b = b 48 | } 49 | } 50 | 51 | /// Load the Pkl module at the given source and evaluate it into `ExtendsAbstractClass.Module`. 52 | /// 53 | /// - Parameter source: The source of the Pkl module. 54 | public static func loadFrom(source: ModuleSource) async throws -> ExtendsAbstractClass.Module { 55 | try await PklSwift.withEvaluator { evaluator in 56 | try await loadFrom(evaluator: evaluator, source: source) 57 | } 58 | } 59 | 60 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 61 | /// `ExtendsAbstractClass.Module`. 62 | /// 63 | /// - Parameter evaluator: The evaluator to use for evaluation. 64 | /// - Parameter source: The module to evaluate. 65 | public static func loadFrom( 66 | evaluator: PklSwift.Evaluator, 67 | source: PklSwift.ModuleSource 68 | ) async throws -> ExtendsAbstractClass.Module { 69 | try await evaluator.evaluateModule(source: source, as: Module.self) 70 | } 71 | } -------------------------------------------------------------------------------- /Sources/PklSwiftInternals/include/Discovery.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #if !defined(PKLS_DISCOVERY_H) 18 | #define PKLS_DISCOVERY_H 19 | 20 | #include "Defines.h" 21 | 22 | #include 23 | 24 | PKLS_ASSUME_NONNULL_BEGIN 25 | 26 | /// The type of callback that is called by `pkls_enumerateTypes()`. 27 | /// 28 | /// - Parameters: 29 | /// - typeMetadata: A type metadata pointer that can be bitcast to `Any.Type`. 30 | /// - context: An arbitrary pointer passed by the caller to 31 | /// `pkls_enumerateTypes()`. 32 | typedef void (* PKLSTypeEnumerator)(void *typeMetadata, void *_Null_unspecified context); 33 | 34 | /// The type name filter that is called by `pkls_enumerateTypes()`. 35 | /// 36 | /// - Parameters: 37 | /// - typeName: The name of the type being considered, as a C string. 38 | /// - context: An arbitrary pointer passed by the caller to 39 | /// `pkls_enumerateTypes()`. 40 | /// 41 | /// - Returns: Whether or not the type named by `typeName` should be passed to 42 | /// the corresponding enumerator function. 43 | typedef bool (* PKLSTypeNameFilter)(const char *typeName, void *_Null_unspecified context); 44 | 45 | /// Enumerate all types known to Swift found in the current process. 46 | /// 47 | /// - Parameters: 48 | /// - nameFilter: If not `nullptr`, a filtering function that checks if a type 49 | /// name is valid before realizing the type. 50 | /// - body: A function to invoke. `context` is passed to it along with a 51 | /// type metadata pointer (which can be bitcast to `Any.Type`.) 52 | /// - context: An arbitrary pointer to pass to `body`. 53 | /// 54 | /// This function may enumerate the same type more than once (for instance, if 55 | /// it is present in an image's metadata table multiple times, or if it is an 56 | /// Objective-C class implemented in Swift.) Callers are responsible for 57 | /// deduping type metadata pointers passed to `body`. 58 | PKLS_EXTERN void pkls_enumerateTypes(PKLSTypeNameFilter _Nullable nameFilter, PKLSTypeEnumerator body, void *_Null_unspecified context); 59 | 60 | PKLS_ASSUME_NONNULL_END 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /Sources/PklSwift/ModuleSource.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | /// A representation of a source for a Pkl module to be evaluated. 20 | public struct ModuleSource: Hashable, Sendable { 21 | /// The URI of the module. 22 | let uri: URL 23 | 24 | /// The text contents of the module, if available. 25 | /// 26 | // If `nil`, gets resolved by Pkl during evaluation time. 27 | // If the scheme of the uri matches a ``ModuleReader``, it will be used to resolve the module. 28 | let text: String? 29 | } 30 | 31 | extension ModuleSource { 32 | /// Creates a ``ModuleSource`` from the given file path component. 33 | /// 34 | /// Relative paths are resolved against the current working directory. 35 | /// 36 | /// - Parameter path: The file path component. 37 | public static func path(_ path: String) -> ModuleSource { 38 | let path = resolvePaths(path) 39 | return ModuleSource(uri: URL(fileURLWithPath: path), text: nil) 40 | } 41 | 42 | /// Creates a synthetic ``ModuleSource`` with the given text. 43 | /// The module is assigned `"repl:text"` as its URI. 44 | /// 45 | /// - Parameter text: The text contents of the module. 46 | public static func text(_ text: String) -> ModuleSource { 47 | ModuleSource(uri: URL(string: "repl:text")!, text: text) 48 | } 49 | 50 | /// Creates a ``ModuleSource`` with the given URL. 51 | /// 52 | /// - Parameter url: The URL that represents this module source. 53 | public static func url(_ url: URL) -> ModuleSource { 54 | ModuleSource(uri: url, text: nil) 55 | } 56 | 57 | /// Creates a ``ModuleSource`` with the given URI string, or returns `nil` if the URI string is malformed. 58 | /// 59 | /// - Parameter string: The URI string, for example, `"https://example.com/foo.pkl"` 60 | public static func uri(_ string: String) -> ModuleSource? { 61 | guard let url = URL(string: string) else { return nil } 62 | return ModuleSource(uri: url, text: nil) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/union.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `union`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum union {} 5 | 6 | extension union { 7 | /// City; e.g. where people live 8 | public enum City: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable, Sendable { 9 | case sanFrancisco = "San Francisco" 10 | case london = "London" 11 | case 上海 = "上海" 12 | } 13 | 14 | /// Locale that contains cities and towns 15 | public enum County: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable, Sendable { 16 | case sanFrancisco = "San Francisco" 17 | case sanMateo = "San Mateo" 18 | case yolo = "Yolo" 19 | } 20 | 21 | /// Noodles 22 | public enum Noodles: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable, Sendable { 23 | case 拉面 = "拉面" 24 | case 刀切面 = "刀切面" 25 | case 面线 = "面线" 26 | case 意大利面 = "意大利面" 27 | } 28 | 29 | public enum AccountDisposition: String, CaseIterable, CodingKeyRepresentable, Decodable, Hashable, Sendable { 30 | case empty = "" 31 | case icloud3 = "icloud3" 32 | case prod = "prod" 33 | case shared = "shared" 34 | } 35 | 36 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 37 | public static let registeredIdentifier: String = "union" 38 | 39 | /// A city 40 | public var city: City 41 | 42 | /// County 43 | public var county: County 44 | 45 | /// Noodles 46 | public var noodle: Noodles 47 | 48 | /// Account disposition 49 | public var disposition: AccountDisposition 50 | 51 | public init(city: City, county: County, noodle: Noodles, disposition: AccountDisposition) { 52 | self.city = city 53 | self.county = county 54 | self.noodle = noodle 55 | self.disposition = disposition 56 | } 57 | } 58 | 59 | /// Load the Pkl module at the given source and evaluate it into `union.Module`. 60 | /// 61 | /// - Parameter source: The source of the Pkl module. 62 | public static func loadFrom(source: ModuleSource) async throws -> union.Module { 63 | try await PklSwift.withEvaluator { evaluator in 64 | try await loadFrom(evaluator: evaluator, source: source) 65 | } 66 | } 67 | 68 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 69 | /// `union.Module`. 70 | /// 71 | /// - Parameter evaluator: The evaluator to use for evaluation. 72 | /// - Parameter source: The module to evaluate. 73 | public static func loadFrom( 74 | evaluator: PklSwift.Evaluator, 75 | source: PklSwift.ModuleSource 76 | ) async throws -> union.Module { 77 | try await evaluator.evaluateModule(source: source, as: Module.self) 78 | } 79 | } -------------------------------------------------------------------------------- /codegen/snippet-tests/output/ExtendingOpenClass.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `ExtendingOpenClass`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum ExtendingOpenClass {} 5 | 6 | public protocol ExtendingOpenClass_MyOpenClass: PklRegisteredType, DynamicallyEquatable, Hashable, Sendable { 7 | var myStr: String { get } 8 | } 9 | 10 | extension ExtendingOpenClass { 11 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 12 | public static let registeredIdentifier: String = "ExtendingOpenClass" 13 | 14 | public var res1: MyClass 15 | 16 | public var res2: MyClass2 17 | 18 | public init(res1: MyClass, res2: MyClass2) { 19 | self.res1 = res1 20 | self.res2 = res2 21 | } 22 | } 23 | 24 | public struct MyClass: MyOpenClass { 25 | public static let registeredIdentifier: String = "ExtendingOpenClass#MyClass" 26 | 27 | public var myBoolean: Bool 28 | 29 | public var myStr: String 30 | 31 | public init(myBoolean: Bool, myStr: String) { 32 | self.myBoolean = myBoolean 33 | self.myStr = myStr 34 | } 35 | } 36 | 37 | public typealias MyOpenClass = ExtendingOpenClass_MyOpenClass 38 | 39 | public struct MyOpenClassImpl: MyOpenClass { 40 | public static let registeredIdentifier: String = "ExtendingOpenClass#MyOpenClass" 41 | 42 | public var myStr: String 43 | 44 | public init(myStr: String) { 45 | self.myStr = myStr 46 | } 47 | } 48 | 49 | public struct MyClass2: lib3.GoGoGo { 50 | public static let registeredIdentifier: String = "ExtendingOpenClass#MyClass2" 51 | 52 | public var myBoolean: Bool 53 | 54 | public var duck: String 55 | 56 | public init(myBoolean: Bool, duck: String) { 57 | self.myBoolean = myBoolean 58 | self.duck = duck 59 | } 60 | } 61 | 62 | /// Load the Pkl module at the given source and evaluate it into `ExtendingOpenClass.Module`. 63 | /// 64 | /// - Parameter source: The source of the Pkl module. 65 | public static func loadFrom(source: ModuleSource) async throws -> ExtendingOpenClass.Module { 66 | try await PklSwift.withEvaluator { evaluator in 67 | try await loadFrom(evaluator: evaluator, source: source) 68 | } 69 | } 70 | 71 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 72 | /// `ExtendingOpenClass.Module`. 73 | /// 74 | /// - Parameter evaluator: The evaluator to use for evaluation. 75 | /// - Parameter source: The module to evaluate. 76 | public static func loadFrom( 77 | evaluator: PklSwift.Evaluator, 78 | source: PklSwift.ModuleSource 79 | ) async throws -> ExtendingOpenClass.Module { 80 | try await evaluator.evaluateModule(source: source, as: Module.self) 81 | } 82 | } -------------------------------------------------------------------------------- /Sources/MessagePack/Box.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | struct Box { 20 | let value: Value 21 | init(_ value: Value) { 22 | self.value = value 23 | } 24 | } 25 | 26 | extension Box: Encodable where Value: Encodable { 27 | func encode(to encoder: Encoder) throws { 28 | try self.value.encode(to: encoder) 29 | } 30 | } 31 | 32 | extension Box: Decodable where Value: Decodable { 33 | init(from decoder: Decoder) throws { 34 | try self.init(Value(from: decoder)) 35 | } 36 | } 37 | 38 | extension Box where Value == Data { 39 | init(from decoder: Decoder) throws { 40 | let container = try decoder.singleValueContainer() 41 | try self.init(container.decode(Value.self)) 42 | } 43 | 44 | func encode(to encoder: Encoder) throws { 45 | var container = encoder.singleValueContainer() 46 | try container.encode(self.value) 47 | } 48 | } 49 | 50 | extension Box where Value == Date { 51 | init(from decoder: Decoder) throws { 52 | let container = try decoder.singleValueContainer() 53 | try self.init(container.decode(Value.self)) 54 | } 55 | 56 | func encode(to encoder: Encoder) throws { 57 | var container = encoder.singleValueContainer() 58 | try container.encode(self.value) 59 | } 60 | } 61 | 62 | extension Box where Value == [UInt8] { 63 | init(from decoder: Decoder) throws { 64 | let container = try decoder.singleValueContainer() 65 | try self.init(container.decode(Value.self)) 66 | } 67 | 68 | func encode(to encoder: Encoder) throws { 69 | var container = encoder.singleValueContainer() 70 | try container.encode(self.value) 71 | } 72 | } 73 | 74 | extension Box where Value == URL { 75 | init(from decoder: Decoder) throws { 76 | let container = try decoder.singleValueContainer() 77 | try self.init(container.decode(Value.self)) 78 | } 79 | 80 | func encode(to encoder: Encoder) throws { 81 | var container = encoder.singleValueContainer() 82 | try container.encode(self.value) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /codegen/src/GeneratorSettings.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | /// Settings used to configure code generation. 18 | @swift.Module { name = "pkl_gen_swift" } 19 | @swift.Name { value = "GeneratorSettings" } 20 | module pkl.swift.GeneratorSettings 21 | 22 | import "pkl:reflect" 23 | 24 | import "swift.pkl" 25 | 26 | /// The set of modules to turn into Swift code. 27 | /// 28 | /// A module's dependencies are also included in code generation. 29 | /// Therefore, in most cases, it is only necessary to provide the entrypoint for code generation. 30 | inputs: Listing? 31 | 32 | /// The output path to write generated files into. 33 | /// 34 | /// Defaults to `.out`. Relative paths are resolved against the enclosing directory. 35 | outputPath: String? 36 | 37 | /// If [true], prints the filenames that would be created, but skips writing any files. 38 | dryRun: Boolean? 39 | 40 | /// The Generator.pkl script to use for code generation. 41 | /// 42 | /// This is an internal setting that's meant for development purposes. 43 | generateScript: String? 44 | 45 | /// The project directory to control dependency and evaluator settings during codegen. 46 | /// 47 | /// This corresponds to the `--project-dir` flag in the Pkl CLI. 48 | /// Relative paths are resolved against the enclosing directory. 49 | /// 50 | /// Paths must use `/` as the path separator. 51 | projectDir: String? 52 | 53 | output { 54 | local myModuleDirectory = 55 | let (myModuleUri = reflect.Module(module).uri) 56 | myModuleUri.replaceFirst("file://", "").split("/").dropLast(1).join("/") 57 | 58 | local function resolvePath(path: String): String = 59 | if (path.startsWith(Regex("\\w+:")) || path.startsWith("/")) 60 | path 61 | else 62 | myModuleDirectory + "/" + path 63 | 64 | value = (module) { 65 | when (module.inputs != null) { 66 | inputs = new { 67 | for (input in module.inputs!!) { 68 | resolvePath(input) 69 | } 70 | } 71 | } 72 | when (module.outputPath != null) { 73 | outputPath = resolvePath(module.outputPath!!) 74 | } 75 | when (module.projectDir != null) { 76 | projectDir = resolvePath(module.projectDir!!) 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/ExternalReaderClientTest.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import SemanticVersion 18 | import XCTest 19 | 20 | @testable import PklSwift 21 | 22 | #if os(macOS) || os(Linux) || os(Windows) 23 | class ExternalReaderClientTest: XCTestCase { 24 | func testE2E() async throws { 25 | let version = try await SemanticVersion(EvaluatorManager().getVersion())! 26 | guard version >= pklVersion0_27 else { 27 | throw XCTSkip("External readers require Pkl 0.27 or later.") 28 | } 29 | 30 | let tempDir = try tempDir() 31 | let testFile = tempDir.appendingPathComponent("test.pkl") 32 | try #""" 33 | import "pkl:test" 34 | 35 | fib5 = read("fib:5").text.toInt() 36 | fib10 = read("fib:10").text.toInt() 37 | fib20 = read("fib:20").text.toInt() 38 | 39 | fibErrA = test.catch(() -> read("fib:%20")) 40 | fibErrB = test.catch(() -> read("fib:abc")) 41 | fibErrC = test.catch(() -> read("fib:-10")) 42 | """#.write(to: testFile, atomically: true, encoding: .utf8) 43 | 44 | let expectedResult = """ 45 | fib5 = 5 46 | fib10 = 55 47 | fib20 = 6765 48 | fibErrA = "I/O error reading resource `fib:%20`. IOException: PklError(message: \\"input uri must be in format fib:\\")" 49 | fibErrB = "I/O error reading resource `fib:abc`. IOException: PklError(message: \\"input uri must be in format fib:\\")" 50 | fibErrC = "I/O error reading resource `fib:-10`. IOException: PklError(message: \\"input uri must be in format fib:\\")" 51 | """ 52 | 53 | let opts = EvaluatorOptions( 54 | allowedModules: ["file:", "repl:text"], 55 | allowedResources: ["fib:", "prop:"], 56 | externalResourceReaders: [ 57 | "fib": ExternalReader(executable: "./.build/debug/test-external-reader"), 58 | ] 59 | ) 60 | 61 | try await withEvaluator(options: opts) { evaluator in 62 | let result = try await evaluator.evaluateOutputText(source: .url(testFile)) 63 | XCTAssertEqual(result.trimmingCharacters(in: .whitespacesAndNewlines), expectedResult.trimmingCharacters(in: .whitespacesAndNewlines)) 64 | } 65 | } 66 | } 67 | #endif 68 | -------------------------------------------------------------------------------- /codegen/snippet-tests/input/Classes.pkl: -------------------------------------------------------------------------------- 1 | // ===----------------------------------------------------------------------===// 2 | // Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // ===----------------------------------------------------------------------===// 16 | module Classes 17 | 18 | bug: Bug 19 | 20 | 蚊子: Bug 21 | 22 | thisPerson: ThisPerson 23 | 24 | d: D 25 | 26 | hidden myHiddenProp: String 27 | 28 | class Wheel { 29 | hasSpokes: Boolean 30 | } 31 | 32 | class Bike { 33 | isFixie: Boolean 34 | 35 | /// Wheels are the front and back wheels. 36 | /// 37 | /// There are typically two of them. 38 | wheels: List 39 | } 40 | 41 | abstract class Being { 42 | isAlive: Boolean 43 | } 44 | 45 | /// A Person! 46 | open class Person extends Being { 47 | isAlive = true 48 | 49 | bike: Bike 50 | 51 | /// The person's first name 52 | firstName: UInt16? 53 | 54 | /// The person's last name 55 | lastName: Mapping 56 | 57 | things: Set 58 | } 59 | 60 | class ThisPerson extends Person { 61 | myself: ThisPerson 62 | someoneElse: Person 63 | } 64 | 65 | typealias BugKind = "butterfly" | "beetle\"" | "beetle one" 66 | 67 | // `nothing` is allowed as a string literal 68 | typealias BugKindTwo = nothing | "butterfly" | "beetle\"" | "beetle one" 69 | 70 | // should turn into just a string type because of a conflict; "bettle one" and "bettle_one" would 71 | // turn into the same Swift identifier. 72 | typealias BugKindThree = "butterfly" | "beetle\"" | "beetle one" | "beetle_one" 73 | 74 | // should also turn into just a string type because there no way to generate valid swift symols; "*" and "!!" would 75 | // turn into invalid Swift symbols. 76 | typealias BugKindFour = "*" | "!!" 77 | 78 | class Bug { 79 | /// The owner of this bug. 80 | owner: Person? 81 | 82 | /// The age of this bug 83 | age: Int? 84 | 85 | /// How long the bug holds its breath for 86 | holdsBreathFor: Duration 87 | 88 | size: DataSize 89 | 90 | kind: BugKind 91 | 92 | kind2: BugKindTwo 93 | 94 | kind3: BugKindThree 95 | 96 | kind4: BugKindFour 97 | 98 | bagOfStuff: Dynamic 99 | 100 | bugClass: Class 101 | 102 | bugTypeAlias: TypeAlias 103 | } 104 | 105 | abstract class A { 106 | a: "a" 107 | } 108 | 109 | open class B extends A { 110 | b: "b" 111 | } 112 | 113 | open class C extends B { 114 | c: "c" 115 | } 116 | 117 | class D extends C { 118 | d: "d" 119 | } 120 | -------------------------------------------------------------------------------- /Sources/pkl-gen-swift/Generated/GeneratorSettings.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `pkl.swift.GeneratorSettings`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum GeneratorSettings {} 5 | 6 | extension GeneratorSettings { 7 | /// Settings used to configure code generation. 8 | public struct Module: PklRegisteredType, Decodable, Hashable { 9 | public static let registeredIdentifier: String = "pkl.swift.GeneratorSettings" 10 | 11 | /// The set of modules to turn into Swift code. 12 | /// 13 | /// A module's dependencies are also included in code generation. 14 | /// Therefore, in most cases, it is only necessary to provide the entrypoint for code generation. 15 | public var inputs: [String]? 16 | 17 | /// The output path to write generated files into. 18 | /// 19 | /// Defaults to `.out`. Relative paths are resolved against the enclosing directory. 20 | public var outputPath: String? 21 | 22 | /// If [true], prints the filenames that would be created, but skips writing any files. 23 | public var dryRun: Bool? 24 | 25 | /// The Generator.pkl script to use for code generation. 26 | /// 27 | /// This is an internal setting that's meant for development purposes. 28 | public var generateScript: String? 29 | 30 | /// The project directory to control dependency and evaluator settings during codegen. 31 | /// 32 | /// This corresponds to the `--project-dir` flag in the Pkl CLI. 33 | /// Relative paths are resolved against the enclosing directory. 34 | /// 35 | /// Paths must use `/` as the path separator. 36 | public var projectDir: String? 37 | 38 | public init( 39 | inputs: [String]?, 40 | outputPath: String?, 41 | dryRun: Bool?, 42 | generateScript: String?, 43 | projectDir: String? 44 | ) { 45 | self.inputs = inputs 46 | self.outputPath = outputPath 47 | self.dryRun = dryRun 48 | self.generateScript = generateScript 49 | self.projectDir = projectDir 50 | } 51 | } 52 | 53 | /// Load the Pkl module at the given source and evaluate it into `GeneratorSettings.Module`. 54 | /// 55 | /// - Parameter source: The source of the Pkl module. 56 | public static func loadFrom(source: ModuleSource) async throws -> GeneratorSettings.Module { 57 | try await PklSwift.withEvaluator { evaluator in 58 | try await loadFrom(evaluator: evaluator, source: source) 59 | } 60 | } 61 | 62 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 63 | /// `GeneratorSettings.Module`. 64 | /// 65 | /// - Parameter evaluator: The evaluator to use for evaluation. 66 | /// - Parameter source: The module to evaluate. 67 | public static func loadFrom( 68 | evaluator: PklSwift.Evaluator, 69 | source: PklSwift.ModuleSource 70 | ) async throws -> GeneratorSettings.Module { 71 | try await evaluator.evaluateModule(source: source, as: Module.self) 72 | } 73 | } -------------------------------------------------------------------------------- /Sources/PklSwift/Logger.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | import MessagePack 19 | 20 | /// Handler to control logging messages emitted by the Pkl evaluator. 21 | /// 22 | /// To set a logger, register it on EvaluatorOptions.Logger when building an Evaluator. 23 | public protocol Logger: Sendable { 24 | /// Log the message using level TRACE. 25 | /// 26 | /// - Parameters: 27 | /// - message: The message to log 28 | /// - frameUri: A string representing the location where the log message was emitted. 29 | func trace(message: String, frameUri: String) 30 | 31 | /// Log the message using level WARN. 32 | /// 33 | /// - Parameters: 34 | /// - message: The message to log 35 | /// - frameUri: A string representing the location where the log message was emitted. 36 | func warn(message: String, frameUri: String) 37 | } 38 | 39 | public struct NoopLogger: Logger, Sendable { 40 | public func trace(message: String, frameUri: String) { 41 | // no-op 42 | } 43 | 44 | public func warn(message: String, frameUri: String) { 45 | // no-op 46 | } 47 | } 48 | 49 | extension Logger { 50 | /// The default format for log messages. 51 | public func formatLogMessage(level: String, message: String, frameUri: String) -> String { 52 | "pkl: \(level): \(message) (\(frameUri))\n" 53 | } 54 | } 55 | 56 | /// A logger that writes messages to the provided ``FileHandle``. 57 | public struct FileHandleLogger: Logger, Sendable { 58 | var fileHandle: FileHandle 59 | 60 | public init(_ fileHandle: FileHandle) { 61 | self.fileHandle = fileHandle 62 | } 63 | 64 | public func trace(message: String, frameUri: String) { 65 | let message = formatLogMessage(level: "TRACE", message: message, frameUri: frameUri) 66 | self.fileHandle.write(Data(message.utf8)) 67 | } 68 | 69 | public func warn(message: String, frameUri: String) { 70 | let message = formatLogMessage(level: "WARN", message: message, frameUri: frameUri) 71 | self.fileHandle.write(Data(message.utf8)) 72 | } 73 | } 74 | 75 | public enum Loggers { 76 | /// A logger that writes everything to stdout. 77 | public static var standardOutput: Logger { FileHandleLogger(.standardOutput) } 78 | 79 | /// A logger that writes everything to stderr. 80 | public static var standardError: Logger { FileHandleLogger(.standardError) } 81 | 82 | /// A logger that discards log messages. 83 | public static var noop: Logger { NoopLogger() } 84 | } 85 | -------------------------------------------------------------------------------- /Sources/MessagePack/Decoder/KeyedSingleDecodingContainer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | extension _MessagePackDecoder { 20 | final class KeyedSingleContainer where Key: CodingKey { 21 | var value: MessagePackValue 22 | var index: Int 23 | var codingPath: [CodingKey] 24 | var userInfo: [CodingUserInfoKey: Any] 25 | 26 | func nestedCodingPath(forKey key: CodingKey) -> [CodingKey] { 27 | self.codingPath + [key] 28 | } 29 | 30 | init( 31 | value: MessagePackValue, 32 | codingPath: [CodingKey], 33 | userInfo: [CodingUserInfoKey: Any] 34 | ) { 35 | self.codingPath = codingPath 36 | self.userInfo = userInfo 37 | self.value = value 38 | self.index = 0 39 | } 40 | 41 | func checkCanDecodeValue(forKey key: Key) throws { 42 | guard contains(key) else { 43 | let context = DecodingError.Context( 44 | codingPath: self.codingPath, debugDescription: "key not found: \(key)" 45 | ) 46 | throw DecodingError.keyNotFound(key, context) 47 | } 48 | } 49 | } 50 | } 51 | 52 | extension _MessagePackDecoder.KeyedSingleContainer: KeyedDecodingContainerProtocol { 53 | var allKeys: [Key] { 54 | [Key(intValue: 0)!] 55 | } 56 | 57 | func contains(_ key: Key) -> Bool { 58 | key.intValue == 0 59 | } 60 | 61 | func decodeNil(forKey key: Key) throws -> Bool { 62 | self.value == MessagePackValue.nil 63 | } 64 | 65 | func decode(_ typ: T.Type, forKey key: Key) throws -> T where T: Decodable { 66 | _ = self.value.getAs(T.self) // FIXME: make optional 67 | 68 | let context = DecodingError.Context( 69 | codingPath: codingPath, debugDescription: "key not found: \(key)" 70 | ) 71 | throw DecodingError.keyNotFound(key, context) 72 | } 73 | 74 | func nestedUnkeyedContainer(forKey key: Key) throws -> UnkeyedDecodingContainer { 75 | fatalError("\(#function)") 76 | } 77 | 78 | func nestedContainer(keyedBy nestedKeyType: NestedKey.Type, forKey key: Key) throws 79 | -> KeyedDecodingContainer where NestedKey: CodingKey { 80 | fatalError("\(#function)") 81 | } 82 | 83 | func superDecoder() throws -> Decoder { 84 | fatalError("\(#function)") 85 | } 86 | 87 | func superDecoder(forKey key: Key) throws -> Decoder { 88 | fatalError("\(#function)") 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Sources/pkl-gen-swift/PklSwiftGenerator.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | import PklSwift 19 | 20 | public struct PklSwiftGenerator { 21 | private let settings: GeneratorSettings.Module 22 | private let workingDirectory: String 23 | private var outputStream: TextOutputStream 24 | 25 | public init(settings: GeneratorSettings.Module, workingDirectory: String, outputStream: TextOutputStream) { 26 | self.settings = settings 27 | self.workingDirectory = workingDirectory 28 | self.outputStream = outputStream 29 | } 30 | 31 | public mutating func run() async throws { 32 | var options = EvaluatorOptions.preconfigured 33 | options.logger = Loggers.standardError 34 | let doEval: (Evaluator) async throws -> Void = { evaluator in 35 | for pklInputModule in self.settings.inputs ?? [] { 36 | try await self.runModule( 37 | evaluator: evaluator, 38 | pklInputModule: pklInputModule 39 | ) 40 | } 41 | } 42 | if let projectDir = settings.projectDir { 43 | return try await withProjectEvaluator(projectBaseURI: URL(fileURLWithPath: projectDir), options: options, doEval) 44 | } else { 45 | return try await withEvaluator(options: options, doEval) 46 | } 47 | } 48 | 49 | private mutating func runModule(evaluator: Evaluator, pklInputModule: String) async throws { 50 | let out = resolvePaths(self.settings.outputPath ?? ".out") 51 | let moduleToEvaluate = """ 52 | amends "\(self.settings.generateScript!)" 53 | 54 | import "\(pklInputModule)" as theModule 55 | 56 | moduleToGenerate = theModule 57 | """ 58 | let tempFile = try tempFile(suffix: ".pkl") 59 | try moduleToEvaluate.write(to: tempFile, atomically: true, encoding: .utf8) 60 | let files = try await evaluator.evaluateOutputFiles(source: .url(tempFile)) 61 | for (filename, contents) in files { 62 | let path = resolvePaths(out, filename) 63 | if self.settings.dryRun == true { 64 | self.outputStream.write(path + "\n") 65 | continue 66 | } 67 | let dir = path.components(separatedBy: "/").dropLast().joined(separator: "/") 68 | try? FileManager.default.createDirectory(atPath: dir, withIntermediateDirectories: true) 69 | try contents.write(toFile: path, atomically: true, encoding: .utf8) 70 | self.outputStream.write(path + "\n") 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.adoc: -------------------------------------------------------------------------------- 1 | == Code of Conduct 2 | 3 | === Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our 7 | project and our community a harassment-free experience for everyone, 8 | regardless of age, body size, disability, ethnicity, sex 9 | characteristics, gender identity and expression, level of experience, 10 | education, socio-economic status, nationality, personal appearance, 11 | race, religion, or sexual identity and orientation. 12 | 13 | === Our Standards 14 | 15 | Examples of behavior that contributes to creating a positive environment 16 | include: 17 | 18 | * Using welcoming and inclusive language 19 | * Being respectful of differing viewpoints and experiences 20 | * Gracefully accepting constructive criticism 21 | * Focusing on what is best for the community 22 | * Showing empathy towards other community members 23 | 24 | Examples of unacceptable behavior by participants include: 25 | 26 | * The use of sexualized language or imagery and unwelcome sexual 27 | attention or advances 28 | * Trolling, insulting/derogatory comments, and personal or political 29 | attacks 30 | * Public or private harassment 31 | * Publishing others’ private information, such as a physical or 32 | electronic address, without explicit permission 33 | * Other conduct which could reasonably be considered inappropriate in a 34 | professional setting 35 | 36 | === Our Responsibilities 37 | 38 | Project maintainers are responsible for clarifying the standards of 39 | acceptable behavior and are expected to take appropriate and fair 40 | corrective action in response to any instances of unacceptable behavior. 41 | 42 | Project maintainers have the right and responsibility to remove, edit, 43 | or reject comments, commits, code, wiki edits, issues, and other 44 | contributions that are not aligned to this Code of Conduct, or to ban 45 | temporarily or permanently any contributor for other behaviors that they 46 | deem inappropriate, threatening, offensive, or harmful. 47 | 48 | === Scope 49 | 50 | This Code of Conduct applies within all project spaces, and it also 51 | applies when an individual is representing the project or its community 52 | in public spaces. Examples of representing a project or community 53 | include using an official project e-mail address, posting via an 54 | official social media account, or acting as an appointed representative 55 | at an online or offline event. Representation of a project may be 56 | further defined and clarified by project maintainers. 57 | 58 | === Enforcement 59 | 60 | Instances of abusive, harassing, or otherwise unacceptable behavior may 61 | be reported by contacting the open source team at 62 | opensource-conduct@group.apple.com. All complaints will be reviewed and 63 | investigated and will result in a response that is deemed necessary and 64 | appropriate to the circumstances. The project team is obligated to 65 | maintain confidentiality with regard to the reporter of an incident. 66 | Further details of specific enforcement policies may be posted 67 | separately. 68 | 69 | Project maintainers who do not follow or enforce the Code of Conduct in 70 | good faith may face temporary or permanent repercussions as determined 71 | by other members of the project’s leadership. 72 | 73 | === Attribution 74 | 75 | This Code of Conduct is adapted from the 76 | https://www.contributor-covenant.org[Contributor Covenant], version 1.4, 77 | available at 78 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 79 | -------------------------------------------------------------------------------- /codegen/snippet-tests/output/Foo.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `Foo`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum Foo {} 5 | 6 | public protocol Foo_Animal: Foo_Being { 7 | var name: String { get } 8 | } 9 | 10 | public protocol Foo_Being: PklRegisteredType, DynamicallyEquatable, Hashable, Sendable { 11 | var exists: Bool { get } 12 | } 13 | 14 | extension Foo { 15 | public struct Module: PklRegisteredType, Decodable, Hashable, Sendable { 16 | public static let registeredIdentifier: String = "Foo" 17 | 18 | public var animals: [any Animal] 19 | 20 | public init(animals: [any Animal]) { 21 | self.animals = animals 22 | } 23 | 24 | public static func ==(lhs: Module, rhs: Module) -> Bool { 25 | arrayEquals(arr1: lhs.animals, arr2: rhs.animals) 26 | } 27 | 28 | public func hash(into hasher: inout Hasher) { 29 | for x in animals { 30 | hasher.combine(x) 31 | } 32 | } 33 | 34 | public init(from decoder: any Decoder) throws { 35 | let dec = try decoder.container(keyedBy: PklCodingKey.self) 36 | let animals = try dec.decode([PklSwift.PklAny].self, forKey: PklCodingKey(string: "animals")) 37 | .map { 38 | $0.value as! any Animal 39 | } 40 | self = Module(animals: animals) 41 | } 42 | } 43 | 44 | public typealias Animal = Foo_Animal 45 | 46 | public struct AnimalImpl: Animal { 47 | public static let registeredIdentifier: String = "Foo#Animal" 48 | 49 | public var name: String 50 | 51 | public var exists: Bool 52 | 53 | public init(name: String, exists: Bool) { 54 | self.name = name 55 | self.exists = exists 56 | } 57 | } 58 | 59 | public typealias Being = Foo_Being 60 | 61 | public struct Bird: Animal { 62 | public static let registeredIdentifier: String = "Foo#Bird" 63 | 64 | public var flies: Bool 65 | 66 | public var name: String 67 | 68 | public var exists: Bool 69 | 70 | public init(flies: Bool, name: String, exists: Bool) { 71 | self.flies = flies 72 | self.name = name 73 | self.exists = exists 74 | } 75 | } 76 | 77 | public struct Dog: Animal { 78 | public static let registeredIdentifier: String = "Foo#Dog" 79 | 80 | public var barks: Bool 81 | 82 | public var name: String 83 | 84 | public var exists: Bool 85 | 86 | public init(barks: Bool, name: String, exists: Bool) { 87 | self.barks = barks 88 | self.name = name 89 | self.exists = exists 90 | } 91 | } 92 | 93 | /// Load the Pkl module at the given source and evaluate it into `Foo.Module`. 94 | /// 95 | /// - Parameter source: The source of the Pkl module. 96 | public static func loadFrom(source: ModuleSource) async throws -> Foo.Module { 97 | try await PklSwift.withEvaluator { evaluator in 98 | try await loadFrom(evaluator: evaluator, source: source) 99 | } 100 | } 101 | 102 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 103 | /// `Foo.Module`. 104 | /// 105 | /// - Parameter evaluator: The evaluator to use for evaluation. 106 | /// - Parameter source: The module to evaluate. 107 | public static func loadFrom( 108 | evaluator: PklSwift.Evaluator, 109 | source: PklSwift.ModuleSource 110 | ) async throws -> Foo.Module { 111 | try await evaluator.evaluateModule(source: source, as: Module.self) 112 | } 113 | } -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/quickstart.adoc: -------------------------------------------------------------------------------- 1 | include::ROOT:partial$component-attributes.adoc[] 2 | :uri-jpkl: https://pkl-lang.org/main/current/pkl-cli/index.html#java-executable 3 | :uri-pkl-gen-swift-macos: https://github.com/apple/pkl-swift/releases/download/{pkl-swift-version}/pkl-gen-swift-macos.bin 4 | :uri-pkl-gen-swift-linux-aarch64: https://github.com/apple/pkl-swift/releases/download/{pkl-swift-version}/pkl-gen-swift-linux-aarch64.bin 5 | :uri-pkl-gen-swift-linux-amd64: https://github.com/apple/pkl-swift/releases/download/{pkl-swift-version}/pkl-gen-swift-linux-amd64.bin 6 | 7 | = Quickstart 8 | 9 | These steps will get you up and going with using Pkl and Swift. 10 | 11 | == 1. Install dependencies 12 | 13 | Add the pkl-swift library as a dependency to your project's `Package.swift`: 14 | 15 | .Package.swift 16 | [source,swift] 17 | [subs="+attributes"] 18 | ---- 19 | let package = Package( 20 | dependencies: [ 21 | .package(url: "https://github.com/apple/pkl-swift", from: "{pkl-swift-version}") 22 | ], 23 | targets: [ 24 | .executableTarget( 25 | name: "my-application", 26 | dependencies: [.product(name: "PklSwift", package: "pkl-swift")] 27 | ), 28 | ] 29 | ) 30 | ---- 31 | 32 | Also, ensure that Pkl is xref:main:pkl-cli:index.adoc#installation[installed on your machine]. 33 | 34 | == 2. Define a Pkl schema 35 | 36 | Create a new Pkl file in the directory of your choice, to describe the schema of your configuration. 37 | In this example, the file is placed into `pkl/AppConfig.pkl`. 38 | 39 | .pkl/AppConfig.pkl 40 | [source,pkl] 41 | ---- 42 | module AppConfig 43 | 44 | /// The hostname of this application. 45 | host: String 46 | 47 | /// The port to listen on. 48 | port: UInt16 49 | ---- 50 | 51 | [#codegen] 52 | == 3. Generate Swift source code 53 | 54 | With a schema defined, generate Swift source code for it. 55 | 56 | Code generation is done via the `pkl-gen-swift` CLI. To install, download it from GitHub. 57 | 58 | On macOS: 59 | 60 | [source,bash] 61 | [subs="+attributes"] 62 | ---- 63 | curl -L {uri-pkl-gen-swift-macos} -o pkl-gen-swift 64 | 65 | chmod +x pkl-gen-swift 66 | ---- 67 | 68 | The binary is published for macOS, and for Linux for aarch64 and amd64 architectures. 69 | 70 | * macOS Universal Binary: {uri-pkl-gen-swift-macos} 71 | * linux/aarch64: {uri-pkl-gen-swift-linux-aarch64} 72 | * linux/amd64: {uri-pkl-gen-swift-linux-amd64} 73 | 74 | In our example, Swift sources can be generated using the following command: 75 | 76 | [source,bash] 77 | ---- 78 | pkl-gen-swift pkl/AppConfig.pkl -o Sources/MyApplication/Generated/ 79 | ---- 80 | 81 | This will create a file called `AppConfig.pkl.swift` inside the `Sources/MyApplication/Generated` directory. 82 | 83 | For more details on how to control code generation, consult `pkl-gen-swift --help`. 84 | 85 | == 4. Evaluate Pkl configuration data in Swift 86 | 87 | With the above scaffolding set up, evaluate Pkl configuration data in Swift. 88 | 89 | First, define some configuration that uses the Pkl schema. 90 | 91 | In our example, we create a file at path `pkl/local/appConfig.pkl`. We imagine that we are defining configuration for a server running in a local environment, and would likewise place other environments in sibling directorys; e.g. `pkl/int/appConfig.pkl` and `pkl/prod/appConfig.pkl`. 92 | 93 | .pkl/local/appConfig.pkl 94 | [source,pkl] 95 | ---- 96 | amends "../AppConfig.pkl" 97 | 98 | host = "localhost" 99 | 100 | port = 5939 101 | ---- 102 | 103 | Once defined, evaluate the Pkl module into Swift data. 104 | 105 | .Sources/MyApplication/Main.swift 106 | [source,swift] 107 | ---- 108 | func main() async throws { 109 | let config = try await AppConfig.loadFrom(source: .path("pkl/local/appConfig.pkl")) 110 | print("I'm running on host \(config.host)\n") 111 | } 112 | ---- 113 | -------------------------------------------------------------------------------- /Sources/MessagePack/Encoder/UnkeyedEncodingContainer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | extension _MessagePackEncoder { 20 | final class UnkeyedContainer { 21 | private var storage: [_MessagePackEncodingContainer] = [] 22 | 23 | var count: Int { 24 | self.storage.count 25 | } 26 | 27 | var codingPath: [CodingKey] 28 | 29 | var nestedCodingPath: [CodingKey] { 30 | self.codingPath + [AnyCodingKey(intValue: self.count)!] 31 | } 32 | 33 | var userInfo: [CodingUserInfoKey: Any] 34 | 35 | init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey: Any]) { 36 | self.codingPath = codingPath 37 | self.userInfo = userInfo 38 | } 39 | } 40 | } 41 | 42 | extension _MessagePackEncoder.UnkeyedContainer: UnkeyedEncodingContainer { 43 | func encodeNil() throws { 44 | var container = self.nestedSingleValueContainer() 45 | try container.encodeNil() 46 | } 47 | 48 | func encode(_ value: some Encodable) throws { 49 | var container = self.nestedSingleValueContainer() 50 | try container.encode(value) 51 | } 52 | 53 | private func nestedSingleValueContainer() -> SingleValueEncodingContainer { 54 | let container = _MessagePackEncoder.SingleValueContainer( 55 | codingPath: self.nestedCodingPath, userInfo: self.userInfo 56 | ) 57 | self.storage.append(container) 58 | 59 | return container 60 | } 61 | 62 | func nestedContainer(keyedBy keyType: NestedKey.Type) -> KeyedEncodingContainer< 63 | NestedKey 64 | > where NestedKey: CodingKey { 65 | let container = _MessagePackEncoder.KeyedContainer( 66 | codingPath: self.nestedCodingPath, userInfo: self.userInfo 67 | ) 68 | self.storage.append(container) 69 | return KeyedEncodingContainer(container) 70 | } 71 | 72 | func nestedUnkeyedContainer() -> UnkeyedEncodingContainer { 73 | let container = _MessagePackEncoder.UnkeyedContainer( 74 | codingPath: self.nestedCodingPath, userInfo: self.userInfo 75 | ) 76 | self.storage.append(container) 77 | 78 | return container 79 | } 80 | 81 | func superEncoder() -> Encoder { 82 | fatalError("Unimplemented") // FIXME: 83 | } 84 | } 85 | 86 | extension _MessagePackEncoder.UnkeyedContainer: _MessagePackEncodingContainer { 87 | func write(into: Writer) throws { 88 | let length = self.storage.count 89 | if let uint16 = UInt16(exactly: length) { 90 | if uint16 <= 15 { 91 | _ = try into.write(UInt8(0x90 + uint16)) 92 | } else { 93 | _ = try into.write(0xDC) 94 | _ = try into.write(uint16.bytes) 95 | } 96 | } else if let uint32 = UInt32(exactly: length) { 97 | _ = try into.write(0xDD) 98 | _ = try into.write(uint32.bytes) 99 | } else { 100 | fatalError() 101 | } 102 | 103 | for container in storage { 104 | try container.write(into: into) 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Tests/PklSwiftTests/Fixtures/Generated/AnyType.pkl.swift: -------------------------------------------------------------------------------- 1 | // Code generated from Pkl module `AnyType`. DO NOT EDIT. 2 | import PklSwift 3 | 4 | public enum AnyType {} 5 | 6 | extension AnyType { 7 | public struct Module: PklRegisteredType, Decodable, Hashable, @unchecked Sendable { 8 | public static let registeredIdentifier: String = "AnyType" 9 | 10 | public var bird: AnyHashable? 11 | 12 | public var primitive: AnyHashable? 13 | 14 | public var primitive2: AnyHashable? 15 | 16 | public var array: AnyHashable? 17 | 18 | public var set: AnyHashable? 19 | 20 | public var mapping: AnyHashable? 21 | 22 | public var nullable: AnyHashable? 23 | 24 | public var duration: AnyHashable? 25 | 26 | public var dataSize: AnyHashable? 27 | 28 | public init( 29 | bird: AnyHashable?, 30 | primitive: AnyHashable?, 31 | primitive2: AnyHashable?, 32 | array: AnyHashable?, 33 | set: AnyHashable?, 34 | mapping: AnyHashable?, 35 | nullable: AnyHashable?, 36 | duration: AnyHashable?, 37 | dataSize: AnyHashable? 38 | ) { 39 | self.bird = bird 40 | self.primitive = primitive 41 | self.primitive2 = primitive2 42 | self.array = array 43 | self.set = set 44 | self.mapping = mapping 45 | self.nullable = nullable 46 | self.duration = duration 47 | self.dataSize = dataSize 48 | } 49 | 50 | public init(from decoder: any Decoder) throws { 51 | let dec = try decoder.container(keyedBy: PklCodingKey.self) 52 | let bird = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "bird")) 53 | .value 54 | let primitive = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "primitive")) 55 | .value 56 | let primitive2 = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "primitive2")) 57 | .value 58 | let array = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "array")) 59 | .value 60 | let set = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "set")) 61 | .value 62 | let mapping = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "mapping")) 63 | .value 64 | let nullable = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "nullable")) 65 | .value 66 | let duration = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "duration")) 67 | .value 68 | let dataSize = try dec.decode(PklSwift.PklAny.self, forKey: PklCodingKey(string: "dataSize")) 69 | .value 70 | self = Module(bird: bird, primitive: primitive, primitive2: primitive2, array: array, set: set, mapping: mapping, nullable: nullable, duration: duration, dataSize: dataSize) 71 | } 72 | } 73 | 74 | public struct Bird: PklRegisteredType, Decodable, Hashable, Sendable { 75 | public static let registeredIdentifier: String = "AnyType#Bird" 76 | 77 | public var species: String 78 | 79 | public init(species: String) { 80 | self.species = species 81 | } 82 | } 83 | 84 | /// Load the Pkl module at the given source and evaluate it into `AnyType.Module`. 85 | /// 86 | /// - Parameter source: The source of the Pkl module. 87 | public static func loadFrom(source: ModuleSource) async throws -> AnyType.Module { 88 | try await PklSwift.withEvaluator { evaluator in 89 | try await loadFrom(evaluator: evaluator, source: source) 90 | } 91 | } 92 | 93 | /// Load the Pkl module at the given source and evaluate it with the given evaluator into 94 | /// `AnyType.Module`. 95 | /// 96 | /// - Parameter evaluator: The evaluator to use for evaluation. 97 | /// - Parameter source: The module to evaluate. 98 | public static func loadFrom( 99 | evaluator: PklSwift.Evaluator, 100 | source: PklSwift.ModuleSource 101 | ) async throws -> AnyType.Module { 102 | try await evaluator.evaluateModule(source: source, as: Module.self) 103 | } 104 | } -------------------------------------------------------------------------------- /Sources/PklSwift/Project.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | /// The Swift representation of `pkl.Project` 18 | public struct Project: PklRegisteredType, Hashable, DependencyDeclaredInProjectFile { 19 | public static let registeredIdentifier: String = "pkl.Project" 20 | 21 | let package: Package? 22 | 23 | let evaluatorSettings: PklEvaluatorSettings 24 | 25 | let projectFileUri: String 26 | 27 | let tests: [String] 28 | 29 | let dependencies: [String: any DependencyDeclaredInProjectFile] 30 | 31 | public static func ==(lhs: Project, rhs: Project) -> Bool { 32 | lhs.hashValue == rhs.hashValue 33 | } 34 | 35 | public func hash(into hasher: inout Hasher) { 36 | hasher.combine(self.package) 37 | hasher.combine(self.evaluatorSettings) 38 | hasher.combine(self.projectFileUri) 39 | hasher.combine(self.tests) 40 | for (k, v) in self.dependencies { 41 | hasher.combine(k) 42 | hasher.combine(v) 43 | } 44 | } 45 | } 46 | 47 | public protocol DependencyDeclaredInProjectFile: Hashable, Decodable, Equatable, Sendable {} 48 | 49 | extension Project: Decodable { 50 | public init(from decoder: Decoder) throws { 51 | let dec = try decoder.container(keyedBy: PklCodingKey.self) 52 | let package = try dec.decode(Package?.self, forKey: PklCodingKey(stringValue: "package")!) 53 | let evaluatorSettings = try dec.decode(PklEvaluatorSettings.self, forKey: PklCodingKey(stringValue: "evaluatorSettings")!) 54 | let projectFileUri = try dec.decode(String.self, forKey: PklCodingKey(stringValue: "projectFileUri")!) 55 | let tests = try dec.decode([String].self, forKey: PklCodingKey(stringValue: "tests")!) 56 | let dependencies = try dec.decode([String: PklAny].self, forKey: PklCodingKey(stringValue: "dependencies")!) 57 | .mapValues { $0.value as! any DependencyDeclaredInProjectFile } 58 | self = .init( 59 | package: package, 60 | evaluatorSettings: evaluatorSettings, 61 | projectFileUri: projectFileUri, 62 | tests: tests, 63 | dependencies: dependencies 64 | ) 65 | } 66 | } 67 | 68 | extension Project { 69 | /// The Swift representation of `pkl.Project#RemoteDependency` 70 | public struct RemoteDependency: PklRegisteredType, DependencyDeclaredInProjectFile, Decodable, Hashable { 71 | public static let registeredIdentifier: String = "pkl.Project#RemoteDependency" 72 | 73 | let uri: String 74 | let checksums: Checksums? 75 | } 76 | 77 | /// The Swift representation of `pkl.Project#Package` 78 | public struct Package: PklRegisteredType, Hashable { 79 | public static let registeredIdentifier: String = "pkl.Project#Package" 80 | 81 | let name: String 82 | let baseUri: String 83 | let version: String 84 | let packageZipUrl: String 85 | let description: String? 86 | let authors: [String] 87 | let website: String? 88 | let documentation: String? 89 | let sourceCode: String? 90 | let sourceCodeUrlScheme: String? 91 | let license: String? 92 | let licenseText: String? 93 | let issueTracker: String? 94 | let apiTests: [String] 95 | let exclude: [String] 96 | let uri: String 97 | } 98 | 99 | @available( 100 | *, 101 | deprecated, 102 | message: "Replaced by PklEvaluatorSettings, independent of Project", 103 | renamed: "PklEvaluatorSettings" 104 | ) 105 | public typealias EvaluatorSettings = PklEvaluatorSettings 106 | } 107 | -------------------------------------------------------------------------------- /docs/modules/ROOT/pages/codegen.adoc: -------------------------------------------------------------------------------- 1 | include::ROOT:partial$component-attributes.adoc[] 2 | 3 | = Code generation 4 | 5 | Pkl code may turn into Swift code by way of code generation. 6 | 7 | == Running code generation 8 | 9 | Code generation is done through the `pkl-gen-swift` binary. For installation instructions, consult the xref:ROOT:quickstart.adoc#codegen[codegen] section of the quickstart. 10 | 11 | Once installed, Swift may be generated from Pkl: 12 | 13 | [source,bash] 14 | ---- 15 | pkl-gen-swift pkl/AppConfig.pkl -o Sources/MyProject/Generated/ 16 | ---- 17 | 18 | == How Pkl is turned into Swift 19 | 20 | === Basic types 21 | 22 | The below table describes how Pkl types are mapped into Swift types. 23 | 24 | [cols="1,1"] 25 | |=== 26 | | Pkl type | Swift type 27 | |`Boolean` 28 | |`Bool` 29 | 30 | |`String` 31 | |`String` 32 | 33 | |`Int` 34 | |`Int` 35 | 36 | |`Int8` 37 | |`Int8` 38 | 39 | |`Int16` 40 | |`Int16` 41 | 42 | |`Int32` 43 | |`Int32` 44 | 45 | |`UInt` 46 | |`UInt` 47 | 48 | |`UInt8` 49 | |`UInt8` 50 | 51 | |`UInt16` 52 | |`UInt16` 53 | 54 | |`UInt32` 55 | |`UInt32` 56 | 57 | |`Float` 58 | |`Float64` 59 | 60 | |`Number` 61 | |`Float64` 62 | 63 | |`List` 64 | |`[T]` 65 | 66 | |`Listing` 67 | |`[T]` 68 | 69 | |`Map` 70 | |`[K: V]` 71 | 72 | |`Mapping` 73 | |`[K: V]` 74 | 75 | |`Set` 76 | |`Set` 77 | 78 | |`Pair` 79 | |Not supported 80 | 81 | |`Dynamic` 82 | |Not supported 83 | 84 | |`DataSize` 85 | |`PklSwift.DataSize` 86 | 87 | |`Duration` 88 | |`PklSwift.Duration` 89 | 90 | |`IntSeq` 91 | |Not supported 92 | 93 | |`Class` 94 | |`PklSwift.PklClass` 95 | 96 | |`TypeAlias` 97 | |`PklSwift.PklTypeAlias` 98 | 99 | |`Any` 100 | |`AnyHashable?` 101 | 102 | |`unknown` 103 | |`AnyHashable?` 104 | 105 | |Unions (`A\|B\|C`) 106 | |`AnyHashable?` footnote:[To represent unions as enums, they must be assigned to a typealias. For reference, see xref:enums[enums].] 107 | |=== 108 | 109 | === Classes 110 | 111 | Classes turn into a variation of structs and protocols, depending on inheritance. Protocols get generated because Swift cannot model polymorphism with structs alone (e.g. a value that is a `Dog` struct is not assignable when an `Animal` struct is expected). 112 | 113 | The below table describes how classes get generated. 114 | 115 | |=== 116 | | Pkl class | Swift protocol | Swift struct 117 | | `class Person` 118 | | 119 | | `struct Person` 120 | 121 | | `open class Person` 122 | | `protocol Person` 123 | | `struct PersonImpl: Person` 124 | 125 | | `abstract class Person` 126 | | `protocol Person` 127 | | 128 | 129 | | `class Person extends Being` 130 | | 131 | | `struct Person: Being` footnote:[all properties from `class Being` are added to this struct] 132 | |=== 133 | 134 | NOTE: Classes with recursive property types will generate an invalid struct. This is a known limitation of Swift. 135 | 136 | [[enums]] 137 | === Enums 138 | 139 | If a typealias is defined as a union of types, it is turned into a Swift enum. Each alternative in the union becomes an enum member. 140 | 141 | For example, the following Pkl code: 142 | 143 | [source,pkl] 144 | ---- 145 | typealias City = "San Francisco"|"Cupertino"|"London" 146 | ---- 147 | 148 | Turns into something like this: 149 | 150 | [source,swift] 151 | ---- 152 | enum City: String, CaseIterable, Decodable, Hashable { 153 | case sanFrancisco = "San Francisco" 154 | case cupertino = "Cupertino" 155 | case london = "London" 156 | } 157 | ---- 158 | 159 | All other typealiases that aren't unions turn into Swift typealiases. 160 | 161 | [[name-conflicts]] 162 | == Resolving name conflicts 163 | 164 | When turning Pkl names into Swift names, the code generator follows these rules: 165 | 166 | 1. Any non-letter and non-digit characters get stripped 167 | 2. Each preceding letter gets capitalized. 168 | 169 | As a result, it is possible that two names collide and turn into the same Swift name. 170 | To resolve these conflicts, the https://pkl-lang.org/package-docs/pkg.pkl-lang.org/pkl-swift/pkl.swift/current/swift/Name.html[`@swift.Name`] 171 | annotation must be used on at least one of these declarations so the resulting names are distinct. 172 | 173 | For example: 174 | 175 | [source,pkl] 176 | [subs="+attributes"] 177 | ---- 178 | import "package://pkg.pkl-lang.org/pkl-swift/pkl.swift@{pkl-swift-version}#/swift.pkl" 179 | 180 | @swift.Name { value = "MyCoolApplication" } 181 | class My_Application 182 | 183 | class MyApplication 184 | ---- 185 | -------------------------------------------------------------------------------- /Sources/MessagePack/Decoder/UnkeyedMapDecodingContainer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | extension MessagePackValue { 20 | func flattened() -> [MessagePackValue] { 21 | switch self { 22 | case .nil, .bool, .int, .float, .string, .bin, .ext, .timestamp: 23 | return [self] 24 | case .array(let values): 25 | var flattened: [MessagePackValue] = [] 26 | flattened.reserveCapacity(values.count) 27 | for v in values { 28 | flattened.append(contentsOf: v.flattened()) 29 | } 30 | return flattened 31 | case .map(let values): 32 | var flattened: [MessagePackValue] = [] 33 | flattened.reserveCapacity(values.count * 2) 34 | for (k, _) in values { 35 | flattened.append(contentsOf: k.flattened()) 36 | flattened.append(contentsOf: k.flattened()) 37 | } 38 | return flattened 39 | } 40 | } 41 | } 42 | 43 | extension Array { 44 | func flattened() -> [MessagePackValue] where Array.Element == (MessagePackValue, MessagePackValue) { 45 | var flattened: [MessagePackValue] = [] 46 | flattened.reserveCapacity(self.count * 2) 47 | for el in self { 48 | flattened.append(el.0) 49 | flattened.append(el.1) 50 | } 51 | return flattened 52 | } 53 | } 54 | 55 | extension _MessagePackDecoder { 56 | final class MapUnkeyedContainer { 57 | var codingPath: [CodingKey] 58 | 59 | var nestedCodingPath: [CodingKey] { 60 | self.codingPath + [AnyCodingKey(intValue: self.count ?? 0)!] 61 | } 62 | 63 | var userInfo: [CodingUserInfoKey: Any] 64 | 65 | var count: Int? { 66 | self.value.count 67 | } 68 | 69 | // var value: [(MessagePackValue, MessagePackValue)] 70 | var value: [MessagePackValue] // flattened 71 | 72 | var currentIndex: Int = 0 73 | 74 | init(value: [(MessagePackValue, MessagePackValue)], codingPath: [CodingKey], userInfo: [CodingUserInfoKey: Any]) { 75 | self.value = value.flattened() 76 | self.codingPath = codingPath 77 | self.userInfo = userInfo 78 | } 79 | 80 | var isAtEnd: Bool { 81 | guard let count else { 82 | return true 83 | } 84 | return self.currentIndex >= count 85 | } 86 | 87 | func checkCanDecodeValue() throws { 88 | guard !self.isAtEnd else { 89 | throw DecodingError.dataCorruptedError(in: self, debugDescription: "Unexpected end of data") 90 | } 91 | } 92 | } 93 | } 94 | 95 | extension _MessagePackDecoder.MapUnkeyedContainer: UnkeyedDecodingContainer { 96 | func decodeNil() throws -> Bool { 97 | fatalError("Not implemented: \(#function)") 98 | } 99 | 100 | func decode(_ typ: T.Type) throws -> T where T: Decodable { 101 | defer { 102 | currentIndex += 1 103 | } 104 | let msgPackValue = value[currentIndex] 105 | 106 | return try msgPackValue.decodeInto(typ) 107 | } 108 | 109 | func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { 110 | fatalError("Not implemented: \(#function)") 111 | } 112 | 113 | func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer where NestedKey: CodingKey { 114 | fatalError("Not implemented: \(#function)") 115 | } 116 | 117 | func superDecoder() throws -> Decoder { 118 | fatalError("Not used.") 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Sources/MessagePack/Encoder/KeyedEncodingContainer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | extension _MessagePackEncoder { 20 | final class KeyedContainer where Key: CodingKey { 21 | private var storage: [AnyCodingKey: _MessagePackEncodingContainer] = [:] 22 | 23 | var codingPath: [CodingKey] 24 | var userInfo: [CodingUserInfoKey: Any] 25 | 26 | func nestedCodingPath(forKey key: CodingKey) -> [CodingKey] { 27 | self.codingPath + [key] 28 | } 29 | 30 | init(codingPath: [CodingKey], userInfo: [CodingUserInfoKey: Any]) { 31 | self.codingPath = codingPath 32 | self.userInfo = userInfo 33 | } 34 | } 35 | } 36 | 37 | extension _MessagePackEncoder.KeyedContainer: KeyedEncodingContainerProtocol { 38 | func encodeNil(forKey key: Key) throws { 39 | var container = self.nestedSingleValueContainer(forKey: key) 40 | try container.encodeNil() 41 | } 42 | 43 | func encode(_ value: some Encodable, forKey key: Key) throws { 44 | var container = self.nestedSingleValueContainer(forKey: key) 45 | try container.encode(value) 46 | } 47 | 48 | private func nestedSingleValueContainer(forKey key: Key) -> SingleValueEncodingContainer { 49 | let container = _MessagePackEncoder.SingleValueContainer( 50 | codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo 51 | ) 52 | self.storage[AnyCodingKey(key)] = container 53 | return container 54 | } 55 | 56 | func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer { 57 | let container = _MessagePackEncoder.UnkeyedContainer( 58 | codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo 59 | ) 60 | self.storage[AnyCodingKey(key)] = container 61 | 62 | return container 63 | } 64 | 65 | func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) 66 | -> KeyedEncodingContainer where NestedKey: CodingKey { 67 | let container = _MessagePackEncoder.KeyedContainer( 68 | codingPath: self.nestedCodingPath(forKey: key), userInfo: self.userInfo 69 | ) 70 | self.storage[AnyCodingKey(key)] = container 71 | 72 | return KeyedEncodingContainer(container) 73 | } 74 | 75 | func superEncoder() -> Encoder { 76 | fatalError("Unimplemented") // FIXME: 77 | } 78 | 79 | func superEncoder(forKey key: Key) -> Encoder { 80 | fatalError("Unimplemented") // FIXME: 81 | } 82 | } 83 | 84 | extension _MessagePackEncoder.KeyedContainer: _MessagePackEncodingContainer { 85 | func write(into: Writer) throws { 86 | let length = storage.count 87 | if let uint16 = UInt16(exactly: length) { 88 | if length <= 15 { 89 | _ = try into.write(0x80 + UInt8(length)) 90 | } else { 91 | _ = try into.write(0xDE) 92 | _ = try into.write(uint16.bytes) 93 | } 94 | } else if let uint32 = UInt32(exactly: length) { 95 | _ = try into.write(0xDF) 96 | _ = try into.write(uint32.bytes) 97 | } else { 98 | fatalError() 99 | } 100 | 101 | for (key, container) in self.storage { 102 | let keyContainer = _MessagePackEncoder.SingleValueContainer( 103 | codingPath: self.codingPath, userInfo: self.userInfo 104 | ) 105 | try keyContainer.encode(key.stringValue) 106 | try keyContainer.write(into: into) 107 | try container.write(into: into) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /codegen/src/Generator.pkl: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | /// Generates Swift sources from Pkl 18 | @swift.Module { name = "pkl_gen_swift" } 19 | @ModuleInfo { minPklVersion = "0.29.0" } 20 | module pkl.swift.Generator 21 | 22 | import "pkl:reflect" 23 | 24 | import "internal/gatherer.pkl" 25 | import "internal/SwiftMapping.pkl" 26 | import "internal/SwiftNamespace.pkl" 27 | import "internal/utils.pkl" 28 | import "swift.pkl" 29 | 30 | /// The module that should be generated. 31 | moduleToGenerate: Module 32 | 33 | /// The indentation applied to rendered Swift code. 34 | indent: String = " " 35 | 36 | typealias EnumMember = 37 | reflect.DeclaredType | reflect.StringLiteralType | reflect.NullableType | reflect.NothingType 38 | 39 | function hasDistinctEnumNames(members: List) = 40 | let (names = members.map((it) -> utils.normalizeEnumName(it.value))) 41 | names.isDistinct 42 | 43 | // noinspection TypeMismatch 44 | function isEnumLike(decl: reflect.TypeDeclaration) = 45 | decl is reflect.TypeAlias 46 | && let (referent = decl.referent) 47 | referent is reflect.UnionType 48 | && referent.members.every((t) -> t is EnumMember) 49 | && if (referent.members is List) 50 | referent.members.every((it) -> utils.canBeNormalizedToEnumCaseName(it.value)) 51 | && hasDistinctEnumNames(referent.members) 52 | else 53 | true 54 | 55 | // noinspection UnresolvedElement 56 | function getSwiftModuleName(decl: reflect.TypeDeclaration): String? = 57 | decl.enclosingDeclaration.annotations 58 | .findOrNull((it) -> it.getClass().toString() == "pkl.swift.swift#Module") 59 | ?.name 60 | 61 | function gatherEnums(decl: List): List = 62 | decl 63 | .filter((it) -> isEnumLike(it)) 64 | .fold(List(), (acc, it) -> 65 | acc.add(new SwiftMapping.Enum { 66 | swiftModuleName = getSwiftModuleName(it) 67 | source = it 68 | seenMappings = acc 69 | }) 70 | ) 71 | 72 | function gatherTypeAliases(decl: List): Mixin> = (acc) -> 73 | decl 74 | .filter((it) -> it is reflect.TypeAlias && !isEnumLike(it)) 75 | .fold(acc, (accum, it) -> 76 | accum.add(new SwiftMapping.TypeAlias { 77 | swiftModuleName = getSwiftModuleName(it) 78 | source = it 79 | seenMappings = accum 80 | }) 81 | ) 82 | 83 | function gatherClasses(decl: List): Mixin> = (acc) -> 84 | decl 85 | .filter((it) -> it is reflect.Class) 86 | .fold(acc, (accum, it) -> 87 | accum.add(new SwiftMapping.Class { 88 | swiftModuleName = getSwiftModuleName(it) 89 | source = it 90 | seenMappings = accum 91 | }) 92 | ) 93 | 94 | local allMappings: List = 95 | let (clazz = reflect.Module(moduleToGenerate).moduleClass) 96 | let (declarations = gatherer.gatherTypeDeclarations(clazz, List())) 97 | gatherEnums(declarations) |> gatherClasses(declarations) |> gatherTypeAliases(declarations) 98 | 99 | local packages = 100 | allMappings 101 | .groupBy((it) -> it.namespaceName) 102 | .mapValues((namespace: String, _mappings: List) -> new SwiftNamespace { 103 | namespaceName = namespace 104 | `module` = _mappings.first.source.enclosingDeclaration 105 | mappings = allMappings 106 | moduleMappings = _mappings 107 | indent = module.indent 108 | }) 109 | 110 | output { 111 | files { 112 | for (_, package in packages) { 113 | ...package.output.files!! 114 | } 115 | } 116 | text = 117 | throw("Generator.pkl only produces multiple-file output. Try running again with the -m flag.") 118 | } 119 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SNIPPET_INPUTS := $(shell find codegen/snippet-tests/input/*.pkl) 2 | FIXTURES_INPUTS := $(shell find Tests/PklSwiftTests/Fixtures/*.pkl) 3 | SWIFT_INPUTS := $(shell find Sources/*/**.swift) 4 | PKL_CODEGEN_INPUTS := $(shell find codegen/src/**/*.pkl) 5 | PKL_TESTS := $(shell find codegen/src/tests/*.pkl) 6 | PKL_TEST_OUTPUT := $(PKL_TESTS:.pkl=.xml) 7 | MAKEFLAGS += -j$(NPROCS) 8 | UNAME := $(shell uname -s) 9 | PKL_GEN_SWIFT := $(shell swift build --show-bin-path)/pkl-gen-swift 10 | PKL_GEN_SWIFT_RELEASE := 11 | ifeq ($(UNAME), Darwin) 12 | PKL_GEN_SWIFT_RELEASE += ".build/apple/release/pkl-gen-swift" 13 | else 14 | PKL_GEN_SWIFT_RELEASE := $(shell swift build --configuration release --product pkl-gen-swift --show-bin-path)/pkl-gen-swift 15 | endif 16 | PKL_EXEC ?= pkl 17 | 18 | .PHONY: help 19 | help: 20 | @echo "Snippet tests:" 21 | @echo $(SNIPPET_INPUTS) | tr ' ' '\n' | sort 22 | 23 | @echo "Fixtures:" 24 | @echo $(FIXTURES_INPUTS) | tr ' ' '\n' | sort 25 | 26 | .PHONY: test 27 | test: test-snippets test-pkl test-swift-lib 28 | 29 | .PHONY: generate-snippets 30 | generate-snippets: clean-snippets pkl-gen-swift 31 | $(PKL_GEN_SWIFT) $(SNIPPET_INPUTS) -o codegen/snippet-tests/output --generate-script codegen/src/Generator.pkl 32 | 33 | .PHONY: generate-fixtures 34 | generate-fixtures: clean-fixtures pkl-gen-swift 35 | $(PKL_GEN_SWIFT) $(FIXTURES_INPUTS) -o Tests/PklSwiftTests/Fixtures/Generated --generate-script codegen/src/Generator.pkl 36 | 37 | # TODO how do we inline the logic of test-snippets into here? 38 | .PHONY: test-snippets 39 | test-snippets: 40 | ./scripts/test-snippets.sh 41 | 42 | .PHONY: test-swift-lib 43 | test-swift-lib: generate-fixtures 44 | @swift test 45 | 46 | .PHONY: test-pkl 47 | test-pkl: $(PKL_CODEGEN_INPUTS) 48 | $(PKL_EXEC) test codegen/src/tests/*.pkl --junit-reports .out/test-results/ 49 | 50 | .PHONY: clean 51 | clean: clean-snippets clean-fixtures clean-build 52 | 53 | .PHONY: clean-build 54 | clean-build: 55 | @rm -rf .build/ 56 | 57 | .PHONY: clean-snippets 58 | clean-snippets: 59 | @rm -rf codegen/snippet-tests/output/ 60 | 61 | .PHONY: clean-fixtures 62 | clean-fixtures: 63 | @rm -rf Tests/PklSwiftTests/Fixtures/Generated 64 | 65 | .PHONY: pkl-gen-swift 66 | pkl-gen-swift: $(PKL_GEN_SWIFT) 67 | 68 | $(PKL_GEN_SWIFT): $(SWIFT_INPUTS) 69 | @swift build 70 | 71 | .build/x86_64-apple-macosx/release/pkl-gen-swift: $(SWIFT_INPUTS) 72 | @swift build \ 73 | --product pkl-gen-swift \ 74 | --configuration release \ 75 | --arch x86_64 76 | 77 | .build/arm64-apple-macosx/release/pkl-gen-swift: $(SWIFT_INPUTS) 78 | @swift build \ 79 | --product pkl-gen-swift \ 80 | --configuration release \ 81 | --arch arm64 82 | 83 | ifeq ($(UNAME), Darwin) 84 | # If macOS, build a universal binary 85 | # We should be able to do multi-arch builds, but need to workaround for now 86 | # (see https://github.com/apple/swift-package-manager/issues/6969) 87 | $(PKL_GEN_SWIFT_RELEASE): 88 | $(MAKE) .build/x86_64-apple-macosx/release/pkl-gen-swift 89 | $(MAKE) .build/arm64-apple-macosx/release/pkl-gen-swift 90 | @mkdir -p .build/apple/release/ 91 | @lipo -create \ 92 | -output .build/apple/release/pkl-gen-swift \ 93 | .build/x86_64-apple-macosx/release/pkl-gen-swift \ 94 | .build/arm64-apple-macosx/release/pkl-gen-swift 95 | else 96 | # otherwise, build a binary for the machine's arch. 97 | $(PKL_GEN_SWIFT_RELEASE): $(SWIFT_INPUTS) 98 | @swift build \ 99 | --product pkl-gen-swift \ 100 | --configuration release 101 | endif 102 | 103 | .PHONY: pkl-package 104 | pkl-package: 105 | $(PKL_EXEC) project package codegen/src/ 106 | 107 | .PHONY: pkl-gen-swift-release 108 | pkl-gen-swift-release: $(PKL_GEN_SWIFT_RELEASE) 109 | 110 | .PHONY: pkl-gen-swift-release-output 111 | pkl-gen-swift-release-output: 112 | @echo "$(PKL_GEN_SWIFT_RELEASE)" | xargs 113 | 114 | .PHONY: gha-config 115 | gha-config: 116 | $(PKL_EXEC) eval --project-dir .github -m .github .github/index.pkl 117 | 118 | .PHONY: swiftformat 119 | swiftformat: 120 | swift package plugin --allow-writing-to-package-directory swiftformat . 121 | 122 | .PHONY: swiftformat-lint 123 | swiftformat-lint: 124 | swift package plugin --allow-writing-to-package-directory swiftformat --lint . 125 | 126 | .PHONY: license-format 127 | license-format: .build/tools/hawkeye 128 | .build/tools/hawkeye format 129 | 130 | .PHONY: pkl-format 131 | pkl-format: 132 | $(PKL_EXEC) format --grammar-version 1 --write . 133 | 134 | .PHONY: pkl-format-lint 135 | pkl-format-lint: 136 | $(PKL_EXEC) format --grammar-version 1 --diff-name-only . 137 | 138 | .PHONY: format 139 | format: swiftformat license-format pkl-format 140 | -------------------------------------------------------------------------------- /Sources/MessagePack/Decoder/UnkeyedDecodingContainer.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | extension _MessagePackDecoder { 20 | final class UnkeyedContainer { 21 | var codingPath: [CodingKey] 22 | 23 | var nestedCodingPath: [CodingKey] { 24 | self.codingPath + [AnyCodingKey(intValue: self.count ?? 0)!] 25 | } 26 | 27 | var userInfo: [CodingUserInfoKey: Any] 28 | 29 | lazy var count: Int? = self.value.count 30 | 31 | var value: [MessagePackValue] 32 | 33 | var currentIndex: Int = 0 34 | 35 | init(value: [MessagePackValue], codingPath: [CodingKey], userInfo: [CodingUserInfoKey: Any]) { 36 | self.value = value 37 | self.codingPath = codingPath 38 | self.userInfo = userInfo 39 | } 40 | 41 | var isAtEnd: Bool { 42 | guard let count else { 43 | return true 44 | } 45 | return self.currentIndex >= count 46 | } 47 | 48 | func checkCanDecodeValue() throws { 49 | guard !self.isAtEnd else { 50 | throw DecodingError.dataCorruptedError(in: self, debugDescription: "Unexpected end of data") 51 | } 52 | } 53 | } 54 | } 55 | 56 | extension _MessagePackDecoder.UnkeyedContainer: UnkeyedDecodingContainer { 57 | func decodeNil() throws -> Bool { 58 | try checkCanDecodeValue() 59 | defer { currentIndex += 1 } 60 | switch value[currentIndex] { 61 | case .nil: return true 62 | default: return false 63 | } 64 | } 65 | 66 | func decode(_ typ: T.Type) throws -> T where T: Decodable { 67 | try checkCanDecodeValue() 68 | defer { 69 | currentIndex += 1 70 | } 71 | let msgPackValue = value[currentIndex] 72 | return try msgPackValue.decodeInto(typ) 73 | } 74 | 75 | func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { 76 | try checkCanDecodeValue() 77 | defer { currentIndex += 1 } 78 | switch value[currentIndex] { 79 | case .array(let value): 80 | return _MessagePackDecoder.UnkeyedContainer( 81 | value: value, codingPath: nestedCodingPath, userInfo: userInfo 82 | ) 83 | default: 84 | let context = DecodingError.Context( 85 | codingPath: codingPath, 86 | debugDescription: 87 | "Expected array type, but got \(value[currentIndex].debugDataTypeDescription)" 88 | ) 89 | throw DecodingError.typeMismatch([MessagePackValue].self, context) 90 | } 91 | } 92 | 93 | func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer where NestedKey: CodingKey { 94 | try checkCanDecodeValue() 95 | defer { currentIndex += 1 } 96 | 97 | switch value[currentIndex] { 98 | case .map(let value): 99 | let container = _MessagePackDecoder.KeyedContainer( 100 | value: value, codingPath: nestedCodingPath, userInfo: userInfo 101 | ) 102 | return KeyedDecodingContainer(container) 103 | default: 104 | let context = DecodingError.Context( 105 | codingPath: codingPath, 106 | debugDescription: 107 | "Expected \([(MessagePackValue, MessagePackValue)].self), but got \(value[currentIndex].debugDataTypeDescription)" 108 | ) 109 | throw DecodingError.typeMismatch([(MessagePackValue, MessagePackValue)].self, context) 110 | } 111 | } 112 | 113 | func superDecoder() throws -> Decoder { 114 | fatalError("TODO") 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Sources/MessagePack/IO.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | public protocol Writer { 18 | /// Write the given bytes into an output somewhere. 19 | func write(_ buffer: UnsafeRawBufferPointer) throws 20 | 21 | /// Close the writer interface. 22 | func close() throws 23 | } 24 | 25 | extension Writer { 26 | func write(_ bytes: [UInt8]) throws { 27 | try bytes.withUnsafeBytes { try self.write($0) } 28 | } 29 | 30 | func write(_ byte: UInt8) throws { 31 | try [byte].withUnsafeBytes { try self.write($0) } 32 | } 33 | } 34 | 35 | public protocol Reader { 36 | /// Reads bytes from somewhere, writing them into the given bytearray. 37 | func read(into: UnsafeMutableRawBufferPointer) throws -> Int 38 | 39 | /// Close the reader interface. 40 | func close() throws 41 | } 42 | 43 | /// Writes bytes into an internal buffer. 44 | public class BufferWriter: Writer, @unchecked Sendable { 45 | var bytes: [UInt8] = [] 46 | 47 | public func write(_ buffer: UnsafeRawBufferPointer) throws { 48 | self.bytes.append(contentsOf: buffer) 49 | } 50 | 51 | public func close() throws { 52 | // no-op 53 | } 54 | } 55 | 56 | /// Reads bytes from the provided buffer. 57 | public class BufferReader: Reader, @unchecked Sendable { 58 | let bytes: [UInt8] 59 | var index: Int 60 | 61 | public init(_ bytes: [UInt8]) { 62 | self.bytes = bytes 63 | self.index = 0 64 | } 65 | 66 | public func read(into: UnsafeMutableRawBufferPointer) throws -> Int { 67 | if self.index == self.bytes.count { 68 | return 0 69 | } 70 | let nextIndex = min(index + into.count, self.bytes.count) 71 | let slice = self.bytes[self.index.. Int { 94 | if into.count == 0 { 95 | return 0 96 | } 97 | var bytesRead = 0 98 | if self.peekedByte != nil { 99 | into.copyBytes(from: [self.peekedByte!]) 100 | self.peekedByte = nil 101 | bytesRead += 1 102 | } 103 | if into.count > bytesRead { 104 | let neededBytes = into.count - bytesRead 105 | let ptr = UnsafeMutableRawBufferPointer.allocate(byteCount: neededBytes, alignment: 1) 106 | defer { ptr.deallocate() } 107 | bytesRead += try self.reader.read(into: ptr) 108 | into.copyBytes(from: ptr) 109 | } 110 | return bytesRead 111 | } 112 | 113 | func peek() throws -> UInt8 { 114 | if self.peekedByte != nil { 115 | return self.peekedByte! 116 | } 117 | let ptr = UnsafeMutableRawBufferPointer.allocate(byteCount: 1, alignment: 1) 118 | defer { ptr.deallocate() } 119 | let bytesRead = try reader.read(into: ptr) 120 | if bytesRead == 0 { 121 | let context = DecodingError.Context( 122 | codingPath: [], debugDescription: "Unexpected end of input" 123 | ) 124 | throw DecodingError.dataCorrupted(context) 125 | } 126 | self.peekedByte = Array(ptr)[0] 127 | return self.peekedByte! 128 | } 129 | 130 | func close() throws { 131 | try self.reader.close() 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Sources/MessagePack/Decoder/_MessagePackDecoder.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | //===----------------------------------------------------------------------===// 16 | 17 | import Foundation 18 | 19 | final class _MessagePackDecoder: Decoder { 20 | var codingPath: [CodingKey] = [] 21 | 22 | var userInfo: [CodingUserInfoKey: Any] = [:] 23 | 24 | var value: MessagePackValue 25 | 26 | init(value: MessagePackValue) { 27 | self.value = value 28 | } 29 | 30 | func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer 31 | where Key: CodingKey { 32 | switch self.value { 33 | case .map(let map): 34 | let container = _MessagePackDecoder.KeyedContainer( 35 | value: map, codingPath: self.codingPath, userInfo: self.userInfo 36 | ) 37 | return KeyedDecodingContainer(container) 38 | case .int: 39 | let container = _MessagePackDecoder.KeyedSingleContainer( 40 | value: self.value, codingPath: self.codingPath, userInfo: self.userInfo 41 | ) 42 | return KeyedDecodingContainer(container) 43 | default: 44 | throw DecodingError.typeMismatch( 45 | [(MessagePackValue, MessagePackValue)].self, 46 | DecodingError.Context( 47 | codingPath: self.codingPath, 48 | debugDescription: 49 | "Expected to decode \([(MessagePackValue, MessagePackValue)].self), but found \(self.value.debugDataTypeDescription) instead" 50 | ) 51 | ) 52 | } 53 | } 54 | 55 | func unkeyedContainer() throws -> UnkeyedDecodingContainer { 56 | switch self.value { 57 | case .array(let values): 58 | return UnkeyedContainer( 59 | value: values, codingPath: self.codingPath, userInfo: self.userInfo 60 | ) 61 | // case .map(let values): 62 | // return UnkeyedContainerForMaps( 63 | // value: values, codingPath: codingPath, userInfo: userInfo 64 | // ) 65 | case .map(let values): 66 | return MapUnkeyedContainer( 67 | value: values, codingPath: self.codingPath, userInfo: self.userInfo 68 | ) 69 | default: 70 | let context = DecodingError.Context( 71 | codingPath: self.codingPath, 72 | debugDescription: 73 | "Expected to decode \([(MessagePackValue, MessagePackValue)].self), but got \(self.value.debugDataTypeDescription)" 74 | ) 75 | throw DecodingError.typeMismatch([(MessagePackValue, MessagePackValue)].self, context) 76 | } 77 | } 78 | 79 | func singleValueContainer() -> SingleValueDecodingContainer { 80 | SingleValueContainer(value: self.value, codingPath: self.codingPath, userInfo: self.userInfo) 81 | } 82 | } 83 | 84 | protocol MessagePackDecodingContainer: AnyObject { 85 | var codingPath: [CodingKey] { get set } 86 | 87 | var userInfo: [CodingUserInfoKey: Any] { get } 88 | } 89 | 90 | extension MessagePackValue { 91 | func decodeInto(_ typ: T.Type) throws -> T where T: Decodable { 92 | let decoder = _MessagePackDecoder(value: self) 93 | switch typ { 94 | case is Data.Type: 95 | let box = try Box(from: decoder) 96 | return box.value as! T 97 | case is Date.Type: 98 | let box = try Box(from: decoder) 99 | return box.value as! T 100 | case is [UInt8].Type: 101 | let box = try Box<[UInt8]>(from: decoder) 102 | return box.value as! T 103 | case is URL.Type: 104 | let box = try Box(from: decoder) 105 | return box.value as! T 106 | default: 107 | return try T(from: decoder) 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version: 6.0 2 | //===----------------------------------------------------------------------===// 3 | // Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // https://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | //===----------------------------------------------------------------------===// 17 | 18 | import PackageDescription 19 | 20 | let package = Package( 21 | name: "pkl-swift", 22 | platforms: [ 23 | .macOS(.v13), 24 | .iOS(.v16), 25 | .tvOS(.v16), 26 | .watchOS(.v9), 27 | .visionOS(.v1), 28 | ], 29 | products: [ 30 | .library( 31 | name: "MessagePack", 32 | targets: ["MessagePack"] 33 | ), 34 | .library( 35 | name: "PklSwift", 36 | targets: ["PklSwift"] 37 | ), 38 | .executable( 39 | name: "pkl-gen-swift", 40 | targets: ["pkl-gen-swift"] 41 | ), 42 | ], 43 | dependencies: [ 44 | .package(url: "https://github.com/apple/swift-system", from: "1.2.1"), 45 | .package(url: "https://github.com/apple/swift-argument-parser", from: "1.2.3"), 46 | .package(url: "https://github.com/SwiftPackageIndex/SemanticVersion", from: "0.4.0"), 47 | // to enable `swift package generate-documentation --target PklSwift` 48 | .package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.1.0"), 49 | // to enable `swift package plugin --allow-writing-to-package-directory swiftformat` 50 | .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.55.0"), 51 | ], 52 | targets: [ 53 | .target( 54 | name: "PklSwift", 55 | dependencies: ["MessagePack", "PklSwiftInternals", "SemanticVersion"], 56 | swiftSettings: [.enableUpcomingFeature("StrictConcurrency")], 57 | ), 58 | .target( 59 | name: "PklSwiftInternals", 60 | swiftSettings: [.enableUpcomingFeature("StrictConcurrency")] 61 | ), 62 | .target( 63 | name: "MessagePack", 64 | dependencies: [ 65 | .product(name: "SystemPackage", package: "swift-system"), 66 | ], 67 | swiftSettings: [.enableUpcomingFeature("StrictConcurrency")] 68 | ), 69 | .executableTarget( 70 | name: "pkl-gen-swift", 71 | dependencies: [ 72 | .product(name: "ArgumentParser", package: "swift-argument-parser"), 73 | "PklSwift", 74 | ], 75 | resources: [.embedInCode("Resources/VERSION.txt")], 76 | swiftSettings: [.enableUpcomingFeature("StrictConcurrency")] 77 | ), 78 | .executableTarget( 79 | name: "test-external-reader", 80 | dependencies: ["PklSwift"], 81 | swiftSettings: [.enableUpcomingFeature("StrictConcurrency")] 82 | ), 83 | .testTarget( 84 | name: "PklSwiftTests", 85 | dependencies: [ 86 | "PklSwift", 87 | ], 88 | exclude: [ 89 | "Fixtures/Classes.pkl", 90 | "Fixtures/UnionTypes.pkl", 91 | "Fixtures/AnyType.pkl", 92 | "Fixtures/lib1.pkl", 93 | "Fixtures/ExtendedModule.pkl", 94 | "Fixtures/OpenModule.pkl", 95 | "Fixtures/Collections.pkl", 96 | "Fixtures/Collections2.pkl", 97 | "Fixtures/Poly.pkl", 98 | "Fixtures/ApiTypes.pkl", 99 | "Fixtures/Collections2.pkl", 100 | "Fixtures/UnusedClass.pkl", 101 | "Fixtures/Imports/UnusedClassDefs.pkl", 102 | ], 103 | swiftSettings: [.enableUpcomingFeature("StrictConcurrency")] 104 | ), 105 | .testTarget( 106 | name: "MessagePackTests", 107 | dependencies: [ 108 | "MessagePack", 109 | ], 110 | swiftSettings: [.enableUpcomingFeature("StrictConcurrency")] 111 | ), 112 | ], 113 | swiftLanguageModes: [.v5, .v6], 114 | cxxLanguageStandard: .cxx20 115 | ) 116 | --------------------------------------------------------------------------------