Template support for input parameters (#4)

This PR contains the work done to support input parameters for the `create` command of the executable target, and to render content dynamically for the newly-generated project.

Reviewed-on: #4
Co-authored-by: Javier Cicchelli <javier@rock-n-code.com>
Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
This commit was merged in pull request #4.
This commit is contained in:
2025-02-17 22:11:05 +00:00
committed by Javier Cicchelli
parent 9be8fa4a31
commit 212ca52279
33 changed files with 812 additions and 202 deletions
@@ -64,6 +64,19 @@ extension FileServiceMock: FileServicing {
}
}
func createFile(at location: URL, with data: Data) async throws (FileServiceError) {
guard let nextAction else { return }
switch nextAction {
case .error(let error):
throw error
case let .createFile(location, data):
try await spy?.createFile(at: location, with: data)
default:
break
}
}
func createFolder(at location: URL) async throws (FileServiceError) {
guard let nextAction else { return }
@@ -127,6 +140,7 @@ private extension FileServiceMock {
extension FileServiceMock {
enum Action {
case copyFile(URL, URL)
case createFile(URL, Data)
case createFolder(URL)
case deleteItem(URL)
case error(FileServiceError)
@@ -0,0 +1,75 @@
import ColibriLibrary
import Foundation
final class TemplateServiceMock {
// MARK: Properties
private var actions: [Action] = []
private weak var spy: TemplateServiceSpy?
// MARK: Initialisers
init(
action: Action,
spy: TemplateServiceSpy? = nil
) {
self.actions.append(action)
self.spy = spy
}
}
// MARK: - TemplateServicing
extension TemplateServiceMock: TemplateServicing {
// MARK: Functions
@discardableResult
func render(_ object: Any, on template: String) async throws(TemplateServiceError) -> String {
guard let nextAction else { return .empty }
switch nextAction {
case .error(let error):
throw error
case .render(let content):
try await spy?.render(object, on: template)
return content
}
}
}
// MARK: - Helpers
private extension TemplateServiceMock {
// MARK: Computed
var nextAction: Action? {
guard !actions.isEmpty else {
return nil
}
return actions.removeFirst()
}
}
// MARK: - Actions
extension TemplateServiceMock {
enum Action {
case error(TemplateServiceError)
case render(String)
}
}
// MARK: - String+Constants
private extension String {
static let empty = ""
}
@@ -0,0 +1,73 @@
import ColibriLibrary
import Foundation
final class TerminalServiceMock {
// MARK: Properties
private var actions: [Action] = []
private weak var spy: TerminalServiceSpy?
// MARK: Initialisers
init(
action: Action,
spy: TerminalServiceSpy? = nil
) {
self.actions.append(action)
self.spy = spy
}
}
// MARK: - TerminalServicing
extension TerminalServiceMock: TerminalServicing {
// MARK: Functions
func run(_ executableURL: URL, arguments: [String]) async throws (TerminalServiceError) -> String {
guard let nextAction else { return .empty }
switch nextAction {
case .error(let error):
throw error
case let .run(executableURL, arguments):
try await spy?.run(executableURL, arguments: arguments)
return .empty
}
}
}
// MARK: - Helpers
private extension TerminalServiceMock {
// MARK: Computed
var nextAction: Action? {
guard !actions.isEmpty else {
return nil
}
return actions.removeFirst()
}
}
// MARK: - Actions
extension TerminalServiceMock {
enum Action {
case error(TerminalServiceError)
case run(URL, [String])
}
}
// MARK: - String+Constants
private extension String {
static let empty = ""
}
@@ -1,7 +1,6 @@
import ColibriLibrary
import Foundation
@testable import ColibriLibrary
final class FileServiceSpy {
// MARK: Properties
@@ -22,6 +21,10 @@ extension FileServiceSpy: FileServicing {
actions.append(.fileCopied(source, destination))
}
func createFile(at location: URL, with data: Data) async throws (FileServiceError) {
actions.append(.fileCreated(location, data))
}
func createFolder(at location: URL) async throws (FileServiceError) {
actions.append(.folderCreated(location))
}
@@ -43,6 +46,7 @@ extension FileServiceSpy: FileServicing {
extension FileServiceSpy {
enum Action: Equatable {
case fileCreated(_ location: URL, _ data: Data)
case fileCopied(_ source: URL, _ destination: URL)
case folderCreated(_ location: URL)
case itemDeleted(_ location: URL)
@@ -0,0 +1,38 @@
import ColibriLibrary
final class TemplateServiceSpy {
// MARK: Properties
private(set) var actions: [Action] = []
}
// MARK: - TemplateServicing
extension TemplateServiceSpy: TemplateServicing {
// MARK: Functions
@discardableResult
func render(_ object: Any, on template: String) async throws (TemplateServiceError) -> String {
actions.append(.rendered(object, template))
return .content
}
}
// MARK: - Actions
extension TemplateServiceSpy {
enum Action {
case rendered(_ object: Any, _ template: String)
}
}
// MARK: - String+Constants
private extension String {
static let content = ""
}
@@ -0,0 +1,39 @@
import ColibriLibrary
import Foundation
final class TerminalServiceSpy {
// MARK: Properties
private(set) var actions: [Action] = []
}
// MARK: - TerminalServicing
extension TerminalServiceSpy: TerminalServicing {
// MARK: Functions
@discardableResult
func run(_ executableURL: URL, arguments: [String]) async throws(TerminalServiceError) -> String {
actions.append(.ran(executableURL, arguments))
return .content
}
}
// MARK: - Actions
extension TerminalServiceSpy {
enum Action: Equatable {
case ran(_ executableURL: URL, _ arguments: [String])
}
}
// MARK: - String+Constants
private extension String {
static let content = ""
}