Skip to content

Instantly share code, notes, and snippets.

@cjnevin
Last active January 31, 2023 00:43
Show Gist options
  • Save cjnevin/c5df3714713fda300a58afe6b4527dcb to your computer and use it in GitHub Desktop.
Save cjnevin/c5df3714713fda300a58afe6b4527dcb to your computer and use it in GitHub Desktop.
#!/usr/bin/env swift
import Foundation
let script = CommandLine.arguments[1]
let fm = FileManager.default
enum Library: Hashable {
case core(String)
case service(String)
case ui(String)
var text: String {
switch self {
case .core(let text), .service(let text), .ui(let text):
return text
}
}
var path: String {
switch self {
case .core: return "/Core"
case .service: return "/Services"
case .ui: return "/UI"
}
}
var kind: String {
switch self {
case .core: return "Core"
case .service: return "Service"
case .ui: return "UI"
}
}
var textWithSuffix: String {
text + kind
}
var isCore: Bool {
switch self {
case .core: return true
default: return false
}
}
var isService: Bool {
switch self {
case .service: return true
default: return false
}
}
}
do {
let files = try fm.contentsOfDirectory(atPath: script)
let directory = URL(filePath: script)
let name = directory.lastPathComponent
let rootDirectory = directory.appending(path: "/Output")
let appDirectory = rootDirectory.appending(path: "/App/\(name)")
let outputDirectory = rootDirectory.appending(path: "/Features/\(name)")
let testDirectory = rootDirectory.appending(path: "/Tests/\(name)")
let coreDirectory = outputDirectory.appending(path: "/Core")
let servicesDirectory = outputDirectory.appending(path: "/Services")
let uiDirectory = outputDirectory.appending(path: "/UI")
try? fm.createDirectory(at: appDirectory, withIntermediateDirectories: true)
try? fm.createDirectory(at: coreDirectory, withIntermediateDirectories: true)
try? fm.createDirectory(at: servicesDirectory, withIntermediateDirectories: true)
try? fm.createDirectory(at: uiDirectory, withIntermediateDirectories: true)
try? fm.createDirectory(at: testDirectory, withIntermediateDirectories: true)
var libraries: [Library] = []
for file in files {
let isUI = file.localizedStandardContains("ViewModel.swift")
|| file.localizedStandardContains("ViewController.swift")
|| file.localizedStandardContains("View.swift")
|| file.localizedStandardContains(".xcassets")
|| file.localizedStandardContains(".plist")
let isService = file.localizedStandardContains("Service.swift")
let isCoordinator = file.localizedStandardContains("Coordinator.swift")
let isSwift = file.localizedStandardContains(".swift")
let source = directory.appending(path: file)
if isUI {
try? fm.copyItem(at: source, to: uiDirectory.appending(path: file))
libraries.append(.ui(name))
}
else if isService {
try? fm.copyItem(at: source, to: servicesDirectory.appending(path: file))
libraries.append(.service(name))
}
else if isCoordinator {
try? fm.copyItem(at: source, to: appDirectory.appending(path: file))
}
else if isSwift {
try? fm.copyItem(at: source, to: coreDirectory.appending(path: file))
libraries.append(.core(name))
}
else {
print("unhandled file \(file)")
}
}
print(libraries)
let filtered = Set(libraries).sorted(by: { $0.textWithSuffix < $1.textWithSuffix })
let packageText = filtered.map {
".library(name: \"\($0.textWithSuffix)\", targets: [\"\($0.textWithSuffix)\"]),"
}.joined(separator: "\n ")
let targetText = filtered.map {
".target(name: \"\($0.textWithSuffix)\", dependencies: [\($0.dependencies(in: libraries))], path: \"Features/\(outputDirectory.lastPathComponent)\($0.path)\"),"
}.joined(separator: "\n ")
let testTarget = filtered.map {
".testTarget(name: \"\($0.textWithSuffix)Tests\", dependencies: [\"\($0.textWithSuffix)\"], path: \"Tests/\(testDirectory.lastPathComponent)/\($0.kind)Tests\"),"
}.joined(separator: "\n ")
let text = """
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: \(name),
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
\(packageText)
],
dependencies: [],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
\(targetText)
\(testTarget)
]
)
"""
fm.createFile(atPath: rootDirectory.appending(path: "/Package.txt").relativePath, contents: text.data(using: .utf8))
}
catch {
print("failed to read directory")
}
extension Library {
func dependencies(in other: [Library]) -> String {
switch self {
case .core: return ""
case .service:
return other.filter(\.isCore).map(\.textWithSuffix).map { "\"\($0)\"" }.joined(separator: ", ")
case .ui:
return other.filter(\.isService).map(\.textWithSuffix).map { "\"\($0)\"" }.joined(separator: ", ")
}
}
}
@cjnevin
Copy link
Author

cjnevin commented Jan 28, 2023

./main.swift ../Example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment