Skip to content

Commit 13dbce9

Browse files
committed
BuildTool Plugin
1 parent 34fda27 commit 13dbce9

File tree

19 files changed

+234
-56
lines changed

19 files changed

+234
-56
lines changed

Package.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ let package = Package(
1717
.library(name: "Otter", targets: ["Otter"]),
1818
.library(name: "Compiler", targets: ["Compiler"]),
1919
.executable(name: "OtterCLI", targets: ["OtterCLI"]),
20+
.plugin(name: "OtterPlugin", targets: ["OtterPlugin"])
2021
],
2122
dependencies: [
2223
.package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.0-latest"),
@@ -48,6 +49,7 @@ let package = Package(
4849
.product(name: "OrderedCollections", package: "swift-collections"),
4950
.product(name: "SwiftSyntax", package: "swift-syntax"),
5051
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
52+
"Yams",
5153
]
5254
),
5355

@@ -56,9 +58,22 @@ let package = Package(
5658
dependencies: [
5759
"Compiler",
5860
.product(name: "ArgumentParser", package: "swift-argument-parser"),
59-
"Yams",
6061
]
6162
),
63+
64+
.plugin(
65+
name: "OtterPlugin",
66+
capability: .buildTool(),
67+
// capability: .command(
68+
// intent: .custom(
69+
// verb: "otter", description: "Generates the database queries"
70+
// ),
71+
// permissions: [
72+
// .writeToPackageDirectory(reason: "Writes the queries out to a swift file."),
73+
// ]
74+
// ),
75+
dependencies: ["OtterCLI"]
76+
),
6277

6378
.testTarget(
6479
name: "OtterTests",

PluginTests/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc

PluginTests/Package.resolved

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PluginTests/Package.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// swift-tools-version: 6.1
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "PluginTests",
7+
platforms: [.macOS(.v10_15)],
8+
dependencies: [
9+
.package(path: "../"),
10+
],
11+
targets: [
12+
.executableTarget(
13+
name: "PluginTests",
14+
dependencies: ["Otter"],
15+
plugins: [.plugin(name: "OtterPlugin", package: "Otter")]
16+
),
17+
]
18+
)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CREATE TABLE foo (bar INTEGER PRIMARY KEY AUTOINCREMENT);
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
selectFoos:
2+
SELECT * FROM foo;
3+
4+
insertFoo:
5+
INSERT INTO foo DEFAULT VALUES;

PluginTests/Sources/main.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import Otter
2+
3+
let db = try DB.inMemory()
4+
5+
try await db.fooQueries.insertFoo.execute()
6+
try await db.fooQueries.insertFoo.execute()
7+
try await db.fooQueries.insertFoo.execute()
8+
9+
let foos = try await db.fooQueries.selectFoos.execute()
10+
11+
for foo in foos {
12+
print(foo.bar)
13+
}

PluginTests/otter.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
databaseName: DB
2+
migrations: Sources/Migrations
3+
queries: Sources/Queries
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//
2+
// OtterPlugin.swift
3+
// Otter
4+
//
5+
// Created by Wes Wickwire on 8/11/25.
6+
//
7+
8+
import PackagePlugin
9+
10+
@main
11+
struct OtterPlugin: BuildToolPlugin {
12+
func createBuildCommands(
13+
context: PluginContext,
14+
target: Target
15+
) throws -> [Command] {
16+
guard target is SourceModuleTarget else { return [] }
17+
18+
let sourceRoot = context.package.directoryURL.absoluteString
19+
20+
let queries = context.pluginWorkDirectoryURL
21+
.appending(component: "Queries.swift")
22+
23+
let inputFiles = target.sourceModule?.sourceFiles
24+
.filter { $0.url.pathExtension == "sql" }
25+
.map(\.url)
26+
27+
return [
28+
.buildCommand(
29+
displayName: "Running otter generate",
30+
executable: try context.tool(named: "OtterCLI").url,
31+
arguments: [
32+
"generate",
33+
"--path",
34+
sourceRoot,
35+
"--override-output",
36+
queries.absoluteString,
37+
"--skip-directory-create"
38+
],
39+
inputFiles: inputFiles ?? [],
40+
outputFiles: [queries]
41+
)
42+
]
43+
}
44+
}

Sources/Compiler/Config.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// Config.swift
3+
// Otter
4+
//
5+
// Created by Wes Wickwire on 8/5/25.
6+
//
7+
8+
import Foundation
9+
import Yams
10+
11+
public struct Config: Codable {
12+
public let queries: String
13+
public let migrations: String
14+
public let output: String?
15+
public let databaseName: String?
16+
public let additionalImports: [String]?
17+
18+
struct NotFoundError: Error, CustomStringConvertible {
19+
var description: String { "Config does not exist" }
20+
}
21+
22+
public init(at path: String) throws {
23+
var url = URL(fileURLWithPath: path)
24+
25+
if url.lastPathComponent != "otter.yaml" {
26+
url.appendPathComponent("otter.yaml")
27+
}
28+
29+
guard FileManager.default.fileExists(atPath: url.path) else {
30+
throw NotFoundError()
31+
}
32+
33+
let data = try Data(contentsOf: url)
34+
35+
let decoder = YAMLDecoder()
36+
self = try decoder.decode(Config.self, from: data)
37+
}
38+
39+
public func project(at path: String) -> Project {
40+
let url = URL(fileURLWithPath: path)
41+
42+
return Project(
43+
generatedOutputFile: url.appendingPathComponent(output ?? "Queries.swift"),
44+
migrationsDirectory: url.appendingPathComponent(migrations),
45+
queriesDirectory: url.appendingPathComponent(queries)
46+
)
47+
}
48+
}

0 commit comments

Comments
 (0)