Skip to content

Commit

Permalink
add ability to pass arguments (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
EstevanBR authored Aug 9, 2024
1 parent 1e13426 commit df05716
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 48 deletions.
5 changes: 5 additions & 0 deletions Sources/CreateProject/Color.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import class Foundation.ProcessInfo

enum Color: String {
case reset = "\u{001B}[0;0m"
case black = "\u{001B}[0;30m"
Expand All @@ -19,6 +21,9 @@ enum Color: String {
case bgWhite = "\u{001B}[0;47m"

static func +(color: Color, text: String) -> String {
guard UserChoice.shouldColorizeOutput else {
return text
}
return color.rawValue + text + Color.reset.rawValue
}
}
67 changes: 59 additions & 8 deletions Sources/CreateProject/CreateProject.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import Foundation

let version = "2.2.0"

@main
private struct CreateProject {
static let fileManager = FileManager()

static func main() throws {
checkHelp()
checkShowVersion()

do {
let projectPath: String = try UserChoice.get(message: "Please enter where you would like the project directory to be created: ")
let projectPath = try getProjectPath()

let (projectPathExists, projectPathIsDirectory) = fileManager.directoryExists(atPath: projectPath)

Expand All @@ -23,14 +26,15 @@ private struct CreateProject {
throw ChangeDirectoryError(path: projectPath)
}

let projectName: String = try UserChoice.get(message: "Please enter the name of the project: ")
let projectName = try getProjectName()

let contents = try fileManager.contentsOfDirectory(atPath: fileManager.currentDirectoryPath)
guard contents.isEmpty else {
print(color: .red, "Directory at \(projectPath) must be empty, but found: \(contents.joined(separator: ", "))")
exit(1)
}

let executableName: String = try UserChoice.get(message: "Please enter the name of the executable: ")
let executableName = try getExecutableName()

guard try UserChoice.getBool(message: "Project will be created at: \(fileManager.currentDirectoryPath + "/Package.swift, would you like to proceed?")") else {
do {
Expand All @@ -43,10 +47,15 @@ private struct CreateProject {
exit(0)
}

let godotPath: String = switch ProcessInfo.processInfo.environment["GODOT"] {
case .some(let value) where value.isEmpty == false: value
default: try UserChoice.get(message: Color.yellow + "GODOT not set\n" + "Please enter the full path to the Godot 4.2 executable: ")
let godotPath = try getGodotPath()

#if os(macOS)
guard !godotPath.hasSuffix(".app") else {
print(color: .red, "GODOT path ends in .app, it should be the path to executable itself")
print(color: .yellow, "try \(godotPath)/Contents/MacOS/Godot")
exit(1)
}
#endif

print(color: .green, "Created \(try FileFactory.createPackageFile(projectName: projectName, executableName: executableName))")
print(color: .green, "Created \(try FileFactory.copyReadmeFile())")
Expand Down Expand Up @@ -78,6 +87,48 @@ private struct CreateProject {
cd \(fileManager.currentDirectoryPath) && make all
""")
}

static let fileManager = FileManager()
}

private func checkHelp() {
guard !UserChoice.shouldHelp else {
print(UserChoice.Argument.allCases.map { $0.usage + "\n\t" + $0.description }.joined(separator: "\n"))
exit(0)
}
}

private func checkShowVersion() {
guard !UserChoice.shouldShowVersion else {
print(version)
exit(0)
}
}

private func getProjectPath() throws -> String {
try checkFor(.projectPath, promptIfNeeded: "Please enter where you would like the project directory to be created: ")
}

private func getProjectName() throws -> String {
try checkFor(.projectName, promptIfNeeded: "Please enter the name of the project: ")
}

private func getExecutableName() throws -> String {
try checkFor(.executableName, promptIfNeeded: "Please enter the name of the executable: ")
}

private func getGodotPath() throws -> String {
switch ProcessInfo.processInfo.environment["GODOT"] {
case .some(let value) where value.isEmpty == false: value
default: try UserChoice.get(message: Color.yellow + "GODOT not set\n" + "Please enter the full path to the Godot 4.2 executable: ")
}
}

private func checkFor(_ argument: UserChoice.Argument, promptIfNeeded prompt: String) throws -> String {
switch ProcessInfo.processInfo.string(for: argument) {
case .some(let argument): argument
case .none: try UserChoice.get(message: prompt)
}
}

func print(color: Color, _ message: String) {
Expand Down
65 changes: 25 additions & 40 deletions Sources/CreateProject/DataFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,22 @@ enum DataFactory {
compatibility_minimum = 4.2
[libraries]
macos.debug = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
macos.release = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
windows.debug.x86_32 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
windows.release.x86_32 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
windows.debug.x86_64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
windows.release.x86_64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
linux.debug.x86_64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
linux.release.x86_64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
linux.debug.arm64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
linux.release.arm64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
linux.debug.rv64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
linux.release.rv64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
android.debug.x86_64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
android.release.x86_64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
android.debug.arm64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
android.release.arm64 = "res://bin/lib\(projectName).\(arch.dynamicExtension)"
macos.debug = "res://bin/lib\(projectName).\(dynamicExtension)"
macos.release = "res://bin/lib\(projectName).\(dynamicExtension)"
windows.debug.x86_32 = "res://bin/lib\(projectName).\(dynamicExtension)"
windows.release.x86_32 = "res://bin/lib\(projectName).\(dynamicExtension)"
windows.debug.x86_64 = "res://bin/lib\(projectName).\(dynamicExtension)"
windows.release.x86_64 = "res://bin/lib\(projectName).\(dynamicExtension)"
linux.debug.x86_64 = "res://bin/lib\(projectName).\(dynamicExtension)"
linux.release.x86_64 = "res://bin/lib\(projectName).\(dynamicExtension)"
linux.debug.arm64 = "res://bin/lib\(projectName).\(dynamicExtension)"
linux.release.arm64 = "res://bin/lib\(projectName).\(dynamicExtension)"
linux.debug.rv64 = "res://bin/lib\(projectName).\(dynamicExtension)"
linux.release.rv64 = "res://bin/lib\(projectName).\(dynamicExtension)"
android.debug.x86_64 = "res://bin/lib\(projectName).\(dynamicExtension)"
android.release.x86_64 = "res://bin/lib\(projectName).\(dynamicExtension)"
android.debug.arm64 = "res://bin/lib\(projectName).\(dynamicExtension)"
android.release.arm64 = "res://bin/lib\(projectName).\(dynamicExtension)"
"""
.utf8Data
Expand Down Expand Up @@ -259,8 +259,8 @@ enum DataFactory {
rm -rf $(GODOT_BIN_PATH)
mkdir -p $(GODOT_BIN_PATH)
cp $(BUILD_PATH)/debug/libSwiftGodot.\(arch.dynamicExtension) $(GODOT_BIN_PATH)
cp $(BUILD_PATH)/debug/lib\(projectName).\(arch.dynamicExtension) $(GODOT_BIN_PATH)
cp $(BUILD_PATH)/debug/libSwiftGodot.\(dynamicExtension) $(GODOT_BIN_PATH)
cp $(BUILD_PATH)/debug/lib\(projectName).\(dynamicExtension) $(GODOT_BIN_PATH)
.PHONY: run
run:
Expand All @@ -282,28 +282,13 @@ enum DataFactory {
}
}

private enum Architecture {
case x86_64
case arm64

var dynamicExtension: String {
switch self {
case .x86_64: "so"
case .arm64: "dylib"
}
}
}

private extension DataFactory {
static var arch: Architecture {
#if arch(x86_64)
.x86_64
#elseif arch(arm64)
.arm64
#else
fatalError("Unknown architecture")
#endif
}
// on macOS, dynamic libraries have the .dylib file extension, but on other platforms it's .so
private var dynamicExtension: String {
#if os(macOS)
"dylib"
#else
"so"
#endif
}

private extension String {
Expand Down
74 changes: 74 additions & 0 deletions Sources/CreateProject/UserChoice.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,35 @@
import Foundation

extension ProcessInfo {
func string(for argument: UserChoice.Argument) -> String? {
let arguments = ProcessInfo.processInfo.arguments

guard let firstIndex = arguments.firstIndex(of: argument.rawValue) else {
return nil
}

guard arguments.indices.contains(firstIndex + 1) else {
return nil
}

return arguments[firstIndex + 1]
}

func bool(for argument: UserChoice.Argument) -> Bool? {
let arguments = ProcessInfo.processInfo.arguments

guard let firstIndex = arguments.firstIndex(of: argument.rawValue) else {
return nil
}

guard arguments.indices.contains(firstIndex + 1) else {
return nil
}

return Bool(arguments[firstIndex + 1])
}
}

enum UserChoice {
enum Error: Swift.Error, LocalizedError {
case missingUserChoice
Expand All @@ -12,6 +42,50 @@ enum UserChoice {
}
}
}

enum Argument: String, CaseIterable {
case help = "--help"
case noColor = "--noColor"
case projectName = "--projectName"
case executableName = "--executableName"
case projectPath = "--projectPath"
case godotPath = "--godot"
case version = "--version"

var description: String {
switch self {
case .help: "show help info"
case .noColor: "disable colorized output"
case .version: "show current version"
case .projectName: "project name"
case .executableName: "executable name"
case .godotPath: "path to Godot binary"
case .projectPath: "path where Package.swift and other files will be saved"
}
}

var usage: String {
switch self {
case .help, .noColor, .version:
rawValue

case .projectName, .executableName: rawValue + " <name>"
case .projectPath, .godotPath: rawValue + " <path>"
}
}
}

static var shouldColorizeOutput: Bool = {
!ProcessInfo.processInfo.arguments.contains(Argument.noColor.rawValue)
}()

static var shouldHelp: Bool = {
ProcessInfo.processInfo.arguments.contains(Argument.help.rawValue)
}()

static var shouldShowVersion: Bool = {
ProcessInfo.processInfo.arguments.contains(Argument.version.rawValue)
}()

static func get(message: String) throws -> String {
print(message, terminator: "")
Expand Down

0 comments on commit df05716

Please sign in to comment.