Skip to content

Commit 4dd2167

Browse files
authored
Adding Tests (#5)
1 parent 2b0ce35 commit 4dd2167

26 files changed

+657
-130
lines changed

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ let package = Package(
4747
]
4848
),
4949
.testTarget(
50-
name: "PackageDSLKitTests"
50+
name: "PackageDSLKitTests",
51+
dependencies: ["PackageDSLKit"]
5152
)
5253
]
5354
)

Sources/PackageDSLKit/Component.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@
2727
// OTHER DEALINGS IN THE SOFTWARE.
2828
//
2929

30-
internal struct Component: Sendable, Hashable, Codable {
31-
internal let name: String
32-
internal let inheritedTypes: [String]
33-
internal let properties: [String: Property]
30+
public struct Component: Sendable, Hashable, Codable {
31+
public let name: String
32+
public let inheritedTypes: [String]
33+
public let properties: [String: Property]
3434
}

Sources/PackageDSLKit/ComponentWriter.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,18 @@
2929

3030
import SwiftSyntax
3131

32-
internal struct ComponentWriter: Sendable, Hashable, Codable {
33-
private let propertyWriter = PropertyWriter()
34-
internal func node(from component: Component) -> StructDeclSyntax {
32+
public struct ComponentWriter: Sendable, StructureWriter {
33+
private let propertyWriter: @Sendable (Property) -> VariableDeclSyntax
34+
35+
public init(
36+
propertyWriter: @escaping @Sendable (Property) -> VariableDeclSyntax = PropertyWriter.node
37+
) {
38+
self.propertyWriter = propertyWriter
39+
}
40+
41+
public func node(from component: Component) -> StructDeclSyntax {
3542
let memberBlockList = MemberBlockItemListSyntax(
36-
component.properties.values.map(propertyWriter.node(from:)).map {
43+
component.properties.values.map(propertyWriter).map {
3744
MemberBlockItemSyntax(decl: $0)
3845
}
3946
)

Sources/PackageDSLKit/Dependency.swift

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,45 @@ public struct Dependency: TypeSource {
4040
public init(rawValue: Int) {
4141
self.rawValue = rawValue
4242
}
43-
public init?(strings: [String]) {
43+
internal struct InvalidValueError: Error {
44+
internal init?(invalidCount: Int) {
45+
guard invalidCount != 0 else {
46+
return nil
47+
}
48+
assert(invalidCount > 0)
49+
self.invalidCount = invalidCount
50+
}
51+
52+
internal init?(valuesCount: Int, indiciesCount: Int) {
53+
self.init(invalidCount: indiciesCount - valuesCount)
54+
}
55+
56+
internal let invalidCount: Int
57+
}
58+
internal init?(stringsThrows strings: [String]) throws(InvalidValueError) {
4459
let indicies = strings.map {
4560
Self.strings.firstIndex(of: $0)
4661
}
4762
let rawValues = indicies.compactMap(\.self).map { $0 + 1 }
4863
if rawValues.isEmpty {
4964
return nil
5065
}
51-
assert(rawValues.count == indicies.count)
66+
if let error = InvalidValueError(valuesCount: rawValues.count, indiciesCount: indicies.count)
67+
{
68+
assert(error.invalidCount > 0)
69+
throw error
70+
}
5271
let rawValue = rawValues.reduce(0) { $0 + $1 }
5372
self.init(rawValue: rawValue)
5473
}
74+
public init?(strings: [String]) {
75+
do {
76+
try self.init(stringsThrows: strings)
77+
} catch {
78+
assertionFailure("Invalid Values Passed: \(error.invalidCount)")
79+
return nil
80+
}
81+
}
5582

5683
internal func asInheritedTypes() -> [String] {
5784
rawValue.powerOfTwoExponents().map { Self.strings[$0] }
Lines changed: 95 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,74 @@
2828
//
2929

3030
import Foundation
31-
import PackageDSLKit
3231

33-
@available(*, deprecated, message: "Migrate to separate protocol.")
34-
extension FileManager {
35-
internal func swiftVersion(from directoryURL: URL) -> SwiftVersion? {
32+
extension FileManager: PackageFilesInterface {
33+
public var currentDirectoryURL: URL {
34+
URL(fileURLWithPath: currentDirectoryPath)
35+
}
36+
37+
private func readDirectoryContents(at path: String, fileExtension: String = "swift") throws
38+
-> [String]
39+
{
40+
var contents: [String] = []
41+
let items = try contentsOfDirectory(atPath: path)
42+
43+
// Process subdirectories (post-order)
44+
for item in items {
45+
let itemPath = (path as NSString).appendingPathComponent(item)
46+
var isDirectory: ObjCBool = false
47+
let fileExists = fileExists(atPath: itemPath, isDirectory: &isDirectory)
48+
49+
if fileExists && isDirectory.boolValue {
50+
contents += try readDirectoryContents(at: itemPath, fileExtension: fileExtension)
51+
}
52+
}
53+
54+
// Process files
55+
for item in items where item.hasSuffix(".\(fileExtension)") {
56+
let itemPath = (path as NSString).appendingPathComponent(item)
57+
58+
let fileContents = try String(contentsOfFile: itemPath, encoding: .utf8)
59+
contents.append(fileContents)
60+
}
61+
62+
return contents
63+
}
64+
public func writePackageSwiftFile(
65+
swiftVersion: SwiftVersion,
66+
from dslSourcesURL: URL,
67+
to pathURL: URL
68+
) throws {
69+
let contents = try self.readDirectoryContents(
70+
at: dslSourcesURL.path(),
71+
fileExtension: "swift"
72+
)
73+
74+
let packageFileURL = pathURL.appendingPathComponent("Package.swift")
75+
let strings =
76+
[
77+
"// swift-tools-version: \(swiftVersion)",
78+
SupportCodeBlock.syntaxNode.trimmedDescription,
79+
] + contents
80+
let data = Data(strings.joined(separator: "\n").utf8)
81+
self.createFile(atPath: packageFileURL.path(), contents: data)
82+
// TODO: log error if file creation fails
83+
}
84+
85+
public func createDirectory(at url: URL, withIntermediateDirectories createIntermediates: Bool)
86+
throws
87+
{
88+
try self.createDirectory(
89+
at: url,
90+
withIntermediateDirectories: createIntermediates,
91+
attributes: nil
92+
)
93+
}
94+
95+
public func createFile(at url: URL, text: String) {
96+
self.createFile(atPath: url.path(), contents: Data(text.utf8))
97+
}
98+
public func swiftVersion(from directoryURL: URL) -> SwiftVersion? {
3699
let swiftVersionURL = directoryURL.appending(component: ".swift-version")
37100
let packageSwiftURL = directoryURL.appending(component: "Package.swift")
38101

@@ -53,7 +116,7 @@ extension FileManager {
53116

54117
return .readFrom(packageSwiftFileURL: packageSwiftURL)
55118
}
56-
internal func createTargetSourceAt(
119+
public func createTargetSourceAt(
57120
_ pathURL: URL, productName: String, _ productType: ProductType
58121
) throws {
59122
let sourcesDirURL = pathURL.appendingPathComponent("Sources/\(productName)")
@@ -82,27 +145,24 @@ extension FileManager {
82145
contents: Data(sourceCode.utf8)
83146
)
84147
}
85-
internal func createTargetSourceAt(
86-
_ pathURL: URL, productName: String, _ packageType: PackageType
148+
public func createFileStructure(
149+
forPackageType packageType: PackageType,
150+
forProductName productName: String,
151+
at pathURL: URL
87152
) throws {
88-
let productType: ProductType?
89-
90-
switch packageType {
91-
case .empty:
92-
productType = nil
93-
case .library:
94-
productType = .library
95-
case .executable:
96-
productType = .executable
153+
guard packageType != .empty else {
154+
return
97155
}
98-
assert(productType != nil, "Unknown package type \(packageType)")
99-
guard let productType else {
156+
157+
try self.createTargetSourceAt(pathURL, productName: productName, packageType)
158+
159+
guard packageType == .library else {
100160
return
101161
}
102-
try self.createTargetSourceAt(pathURL, productName: productName, productType)
103-
}
104162

105-
fileprivate func createTestTargetAt(_ pathURL: URL, _ productName: String) throws {
163+
try createTestTargetAt(pathURL, productName)
164+
}
165+
private func createTestTargetAt(_ pathURL: URL, _ productName: String) throws {
106166
let testingDirURL = pathURL.appendingPathComponent("Tests/\(productName)Tests")
107167
try self.createDirectory(at: testingDirURL, withIntermediateDirectories: true)
108168

@@ -117,68 +177,23 @@ extension FileManager {
117177
"""
118178
self.createFile(atPath: testFileURL.path(), contents: Data(testCode.utf8))
119179
}
120-
121-
internal func createFileStructure(
122-
forPackageType packageType: PackageType,
123-
forProductName productName: String,
124-
at pathURL: URL
125-
) throws {
126-
guard packageType != .empty else {
127-
return
128-
}
129-
130-
try self.createTargetSourceAt(pathURL, productName: productName, packageType)
131-
132-
guard packageType == .library else {
133-
return
134-
}
135-
136-
try createTestTargetAt(pathURL, productName)
137-
}
138-
internal func writePackageSwiftFile(
139-
swiftVersion: SwiftVersion,
140-
from dslSourcesURL: URL,
141-
to pathURL: URL
180+
private func createTargetSourceAt(
181+
_ pathURL: URL, productName: String, _ packageType: PackageType
142182
) throws {
143-
let contents = try self.readDirectoryContents(
144-
at: dslSourcesURL.path(),
145-
fileExtension: "swift"
146-
)
147-
148-
let packageFileURL = pathURL.appendingPathComponent("Package.swift")
149-
let strings =
150-
[
151-
"// swift-tools-version: \(swiftVersion)",
152-
SupportCodeBlock.syntaxNode.trimmedDescription,
153-
] + contents
154-
let data = Data(strings.joined(separator: "\n").utf8)
155-
self.createFile(atPath: packageFileURL.path(), contents: data)
156-
}
157-
internal func readDirectoryContents(at path: String, fileExtension: String = "swift") throws
158-
-> [String]
159-
{
160-
var contents: [String] = []
161-
let items = try contentsOfDirectory(atPath: path)
162-
163-
// Process subdirectories (post-order)
164-
for item in items {
165-
let itemPath = (path as NSString).appendingPathComponent(item)
166-
var isDirectory: ObjCBool = false
167-
fileExists(atPath: itemPath, isDirectory: &isDirectory)
183+
let productType: ProductType?
168184

169-
if isDirectory.boolValue {
170-
contents += try readDirectoryContents(at: itemPath, fileExtension: fileExtension)
171-
}
185+
switch packageType {
186+
case .empty:
187+
productType = nil
188+
case .library:
189+
productType = .library
190+
case .executable:
191+
productType = .executable
172192
}
173-
174-
// Process files
175-
for item in items where item.hasSuffix(".\(fileExtension)") {
176-
let itemPath = (path as NSString).appendingPathComponent(item)
177-
178-
let fileContents = try String(contentsOfFile: itemPath, encoding: .utf8)
179-
contents.append(fileContents)
193+
assert(productType != nil, "Unknown package type \(packageType)")
194+
guard let productType else {
195+
return
180196
}
181-
182-
return contents
197+
try self.createTargetSourceAt(pathURL, productName: productName, productType)
183198
}
184199
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// IndexCodeWriter.swift
3+
// PackageDSLKit
4+
//
5+
// Created by Leo Dion.
6+
// Copyright © 2025 BrightDigit.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the “Software”), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or
13+
// sell copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
30+
import SwiftSyntax
31+
32+
public protocol IndexCodeWriter: Sendable {
33+
func writeIndex(_ index: Index) throws(PackageDSLError) -> String
34+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// PackageFiles.swift
3+
// PackageDSLKit
4+
//
5+
// Created by Leo Dion.
6+
// Copyright © 2025 BrightDigit.
7+
//
8+
// Permission is hereby granted, free of charge, to any person
9+
// obtaining a copy of this software and associated documentation
10+
// files (the “Software”), to deal in the Software without
11+
// restriction, including without limitation the rights to use,
12+
// copy, modify, merge, publish, distribute, sublicense, and/or
13+
// sell copies of the Software, and to permit persons to whom the
14+
// Software is furnished to do so, subject to the following
15+
// conditions:
16+
//
17+
// The above copyright notice and this permission notice shall be
18+
// included in all copies or substantial portions of the Software.
19+
//
20+
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
21+
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22+
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23+
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24+
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25+
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26+
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27+
// OTHER DEALINGS IN THE SOFTWARE.
28+
//
29+
30+
import Foundation
31+
32+
public struct PackageFiles: PackageFilesFactory {
33+
public static let `default`: PackageFilesFactory = PackageFiles()
34+
35+
private static let defaultTypes:
36+
[PackageFilesInterfaceType: @Sendable () -> any PackageFilesInterface] = [
37+
.fileManager: { FileManager.default }
38+
]
39+
40+
private let types: [PackageFilesInterfaceType: @Sendable () -> any PackageFilesInterface]
41+
42+
internal init(
43+
types: [PackageFilesInterfaceType: @Sendable () -> any PackageFilesInterface]? = nil
44+
) {
45+
self.types = types ?? Self.defaultTypes
46+
}
47+
48+
public func interface(for type: PackageFilesInterfaceType) -> any PackageFilesInterface {
49+
self.types[type]!()
50+
}
51+
}

0 commit comments

Comments
 (0)