Skip to content

Commit f931b4c

Browse files
committed
Correctly handle Pkl classes and typealiases during codegen and decoding
1 parent 4a6da2d commit f931b4c

File tree

16 files changed

+547
-47
lines changed

16 files changed

+547
-47
lines changed

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ let package = Package(
9292
"Fixtures/Collections.pkl",
9393
"Fixtures/Poly.pkl",
9494
"Fixtures/ApiTypes.pkl",
95+
"Fixtures/Collections2.pkl",
9596
],
9697
swiftSettings: [.enableUpcomingFeature("StrictConcurrency")]
9798
),

Sources/PklSwift/API/PklClass.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
public struct PklClass: Hashable {}
18+
19+
extension PklClass: PklSerializableType, Sendable {
20+
public static let messageTag: PklValueType = .class
21+
22+
public init(from decoder: Decoder) throws {}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
public struct PklTypeAlias: Hashable {}
18+
19+
extension PklTypeAlias: PklSerializableType, Sendable {
20+
public static let messageTag: PklValueType = .typealias
21+
22+
public init(from decoder: Decoder) throws {}
23+
}

Sources/PklSwift/Decoder/PklDecoder.swift

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,21 @@ import Foundation
1818
import MessagePack
1919

2020
public enum PklValueType: UInt8, Decodable, Sendable {
21-
case object = 0x1
22-
case map = 0x2
23-
case mapping = 0x3
24-
case list = 0x4
25-
case listing = 0x5
26-
case set = 0x6
27-
case duration = 0x7
28-
case dataSize = 0x8
29-
case pair = 0x9
30-
case intSeq = 0xA
31-
case regex = 0xB
32-
case `class` = 0xC
33-
case `typealias` = 0xD
34-
case function = 0xE
35-
case bytes = 0xF
21+
case object = 0x01
22+
case map = 0x02
23+
case mapping = 0x03
24+
case list = 0x04
25+
case listing = 0x05
26+
case set = 0x06
27+
case duration = 0x07
28+
case dataSize = 0x08
29+
case pair = 0x09
30+
case intSeq = 0x0A
31+
case regex = 0x0B
32+
case `class` = 0x0C
33+
case `typealias` = 0x0D
34+
case function = 0x0E
35+
case bytes = 0x0F
3636
case objectMemberProperty = 0x10
3737
case objectMemberEntry = 0x11
3838
case objectMemberElement = 0x12
@@ -237,6 +237,12 @@ extension _PklDecoder {
237237
case .dataSize:
238238
let decoder = try _PklDecoder(value: propertyValue)
239239
return try PklAny(value: DataSize(from: decoder))
240+
case .class:
241+
let decoder = try _PklDecoder(value: propertyValue)
242+
return try PklAny(value: PklClass(from: decoder))
243+
case .typealias:
244+
let decoder = try _PklDecoder(value: propertyValue)
245+
return try PklAny(value: PklTypeAlias(from: decoder))
240246
case .bytes:
241247
guard case .bin(let bytes) = value[1] else {
242248
throw DecodingError.dataCorrupted(

Sources/pkl-gen-swift/PklGenSwift.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,8 @@ struct PklGenSwift: AsyncParsableCommand {
182182
)
183183
try await cmd.run()
184184
} catch let error as PklError {
185-
Self.exit(withError: error)
185+
FileHandle.standardError.write(Data(error.message.utf8))
186+
Self.exit()
186187
}
187188
}
188189
}
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
res1: Duration = 10.h
2-
res2: DataSize = 1.2345.gib
2+
res2: DataSize = 1.2345.gib
3+
4+
stringClass: Class = String
5+
moduleClass: Class = module.getClass()
6+
typeAlias: TypeAlias = Foo
7+
8+
typealias Foo = String

Tests/PklSwiftTests/Fixtures/Generated/ApiTypes.pkl.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,29 @@ extension ApiTypes {
1111

1212
public var res2: DataSize
1313

14-
public init(res1: Duration, res2: DataSize) {
14+
public var stringClass: PklClass
15+
16+
public var moduleClass: PklClass
17+
18+
public var typeAlias: PklTypeAlias
19+
20+
public init(
21+
res1: Duration,
22+
res2: DataSize,
23+
stringClass: PklClass,
24+
moduleClass: PklClass,
25+
typeAlias: PklTypeAlias
26+
) {
1527
self.res1 = res1
1628
self.res2 = res2
29+
self.stringClass = stringClass
30+
self.moduleClass = moduleClass
31+
self.typeAlias = typeAlias
1732
}
1833
}
1934

35+
public typealias Foo = String
36+
2037
/// Load the Pkl module at the given source and evaluate it into `ApiTypes.Module`.
2138
///
2239
/// - Parameter source: The source of the Pkl module.

Tests/PklSwiftTests/FixturesTest.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,10 @@ class FixturesTest: XCTestCase {
9494
result,
9595
ApiTypes.Module(
9696
res1: .hours(10),
97-
res2: .gibibytes(1.2345)
97+
res2: .gibibytes(1.2345),
98+
stringClass: PklClass(),
99+
moduleClass: PklClass(),
100+
typeAlias: PklTypeAlias()
98101
)
99102
)
100103
}

codegen/snippet-tests/input/Classes.pkl

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,105 @@
1515
// ===----------------------------------------------------------------------===//
1616
module Classes
1717

18-
animals: Listing<Animal> = new {
19-
new { name = "Uni" }
20-
new { name = "Wally" }
21-
new { name = "Mouse" }
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<Wheel>
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<String, UInt32?>
56+
57+
things: Set<Int>
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"
22115
}
23116

24-
open class Animal {
25-
name: String
117+
class D extends C {
118+
d: "d"
26119
}

0 commit comments

Comments
 (0)