Files
HtmlRW/Plugins/BindingPlugin/source.swift

174 lines
6.1 KiB
Swift

//
// source.swift
// gen_html
//
// Created by Isaac Paul on 9/30/25.
//
import Foundation
import PackagePlugin
final class AppError: LocalizedError, Sendable {
let message: String
init(_ message: String) {
self.message = message
}
init(_ message: String, _ error:Error) {
self.message = "\(message) : \(error.localizedDescription)"
}
static func failure<T>(_ message: String) -> Result<T, AppError> {
return .failure(AppError(message))
}
var errorDescription: String? {
get {
return message
}
}
var failureReason: String? { get { return message } }
}
let toolName = "BindingGenerator"
extension BindingPlugin: BuildToolPlugin {
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
guard let swiftTarget = target as? SwiftSourceModuleTarget else {
print("Unexpected target: \(type(of: target)). Needs SwiftSourceModuleTarget")
throw AppError("Unexpected target: \(type(of: target)). Needs SwiftSourceModuleTarget")
}
let toolUrl = try context.tool(named: toolName).url
return try createBuildCommands(
outputDir: context.pluginWorkDirectoryURL,
inputDir: swiftTarget.directoryURL,
inputFiles: swiftTarget.sourceFiles,
toolExe: toolUrl
)
}
}
#if canImport(XcodeProjectPlugin)
import XcodeProjectPlugin
import System
// The entry point for Xcode project builds.
extension BindingPlugin: XcodeBuildToolPlugin {
func createBuildCommands(context: XcodePluginContext, target: XcodeTarget) throws -> [Command] {
let toolUrl = try context.tool(named: toolName).url
return try createBuildCommands(
outputDir: context.pluginWorkDirectoryURL,
inputDir: context.xcodeProject.directoryURL,
inputFiles: target.inputFiles,
toolExe: toolUrl
)
}
}
#endif
struct Config: Codable {
var root_path: String?
}
extension URL {
func isHtml() -> Bool {
return self.pathExtension == "html" || self.pathExtension == "htm"
}
}
extension String {
func uppercaseFirstLetter() -> String {
guard let firstLetter = self.first else { return self }
return firstLetter.uppercased() + self.dropFirst()
}
}
@main
struct BindingPlugin {
private static let configFile = "binding_generator_plugin.json"
private func createBuildCommands(
outputDir: URL,
inputDir: URL,
inputFiles: FileList,
toolExe: URL
) throws -> [Command] {
let configFilePath = inputFiles.first(where: { $0.url.lastPathComponent == BindingPlugin.configFile })
let configFileURL = configFilePath?.url
//let listSearchDir: [String]
let htmlFiles = inputFiles.filter { $0.url.isHtml() }.map { $0.url }
let rootPath:URL
if let configFileURL = configFileURL {
let configData = try Data(contentsOf: configFileURL)
let config = try JSONDecoder().decode(Config.self, from: configData)
let baseDirectory:URL = configFileURL.deletingLastPathComponent()
if let configRootPath = config.root_path {
rootPath = baseDirectory.appending(path: configRootPath)
} else {
rootPath = inputDir
}
/*
for source in config.sources ?? [] {
let sourceFileOrDirectory = baseDirectory.appendingPathComponent(source)
if sourceFileOrDirectory.isHtml() {
htmlFiles.append(sourceFileOrDirectory)
} else {
let files = try FileManager.default.contentsOfDirectory(
at: sourceFileOrDirectory,
includingPropertiesForKeys: nil
)
let filteredFiles = files.filter { $0.isHtml() }
htmlFiles.append(contentsOf: filteredFiles)
}
}*/
} else {
rootPath = inputDir
}
/*
/Users/isaacpaul/Projects/swift-projects/HRW/Plugins/BindingPlugin/source.swift:146:13: error: 'let' cannot appear nested inside another 'var' or 'let' pattern
<unknown>:0: error: error opening input file '/Users/isaacpaul/Library/Developer/Xcode/DerivedData/HRW-bicwrilrmihgqogjvxyqtcosfaro/Build/Intermediates.noindex/BuildToolPluginIntermediates/hrw.output/HRWTests/BindingPlugin/Example.swift' (No such file or directory)
//
Showing All Messages
Error opening input file '/Users/isaacpaul/Library/Developer/Xcode/DerivedData/HRW-bicwrilrmihgqogjvxyqtcosfaro/Build/Intermediates.noindex/BuildToolPluginIntermediates/hrw.output/HRWTests/BindingPlugin/ExampleBinding.swift' (No such file or directory)
*/
return htmlFiles.map { eachFile in
let fileName = eachFile.lastPathComponent
let ext = eachFile.pathExtension
let extIndex = fileName.index(fileName.startIndex, offsetBy: fileName.count - (ext.count + 1))
let withoutExt = String(fileName[fileName.startIndex..<extIndex])
let targetFileName = "\(withoutExt.uppercaseFirstLetter()).swift"
let rootFP = FilePath(rootPath.path)
var inputFP = FilePath(eachFile.path)
let outputDirFP = FilePath(outputDir.path)
let _ = inputFP.removePrefix(rootFP)
inputFP.removeLastComponent()
//let idk = outputDirFP.pushing(inputFP)
let outputFileA = outputDir.appendingPathComponent(inputFP.string)
let outputFile = outputFileA.appendingPathComponent(targetFileName)
print("Expected path: \(outputFile.path)")
return .buildCommand(
displayName: "Generating binding to \(outputFile.path)",
executable: toolExe,
arguments: ["-o", outputDir.path, "-i", eachFile.path, "-b", rootPath.path],
inputFiles: [eachFile],
outputFiles: [URL(fileURLWithPath: outputFile.path)]
)
}
}
}