import CommandClient import ConfigurationClient import Dependencies import DependenciesMacros import FileClient import Foundation import ShellClient // TODO: Add update checks and pull for the playbook. public extension DependencyValues { var playbookClient: PlaybookClient { get { self[PlaybookClient.self] } set { self[PlaybookClient.self] = newValue } } } @DependencyClient public struct PlaybookClient: Sendable { public var repository: Repository public var run: RunPlaybook } public extension PlaybookClient { @DependencyClient struct Repository: Sendable { public var install: @Sendable (Configuration?) async throws -> Void public var directory: @Sendable (Configuration?) async throws -> String public func install() async throws { try await install(nil) } public func directory() async throws -> String { try await directory(nil) } } } public extension PlaybookClient { @DependencyClient struct RunPlaybook: Sendable { public var buildProject: @Sendable (BuildOptions) async throws -> Void public var createProject: @Sendable (CreateOptions, JSONEncoder?) async throws -> Void public var generateTemplate: @Sendable (GenerateTemplateOptions) async throws -> String public func createProject(_ options: CreateOptions) async throws { try await createProject(options, nil) } public struct SharedRunOptions: Equatable, Sendable { public let extraOptions: [String]? public let inventoryFilePath: String? public let loggingOptions: LoggingOptions public let quiet: Bool public let shell: String? public init( extraOptions: [String]? = nil, inventoryFilePath: String? = nil, loggingOptions: LoggingOptions, quiet: Bool = false, shell: String? = nil ) { self.extraOptions = extraOptions self.inventoryFilePath = inventoryFilePath self.loggingOptions = loggingOptions self.quiet = quiet self.shell = shell } } @dynamicMemberLookup public struct BuildOptions: Equatable, Sendable { public let projectDirectory: String? public let shared: SharedRunOptions public init( projectDirectory: String? = nil, shared: SharedRunOptions ) { self.projectDirectory = projectDirectory self.shared = shared } public subscript(dynamicMember keyPath: KeyPath) -> T { shared[keyPath: keyPath] } } @dynamicMemberLookup public struct CreateOptions: Equatable, Sendable { public let projectDirectory: String public let shared: SharedRunOptions public let template: Configuration.Template? public let useLocalTemplateDirectory: Bool public init( projectDirectory: String, shared: SharedRunOptions, template: Configuration.Template? = nil, useLocalTemplateDirectory: Bool ) { self.projectDirectory = projectDirectory self.shared = shared self.template = template self.useLocalTemplateDirectory = useLocalTemplateDirectory } public subscript(dynamicMember keyPath: KeyPath) -> T { shared[keyPath: keyPath] } } @dynamicMemberLookup public struct GenerateTemplateOptions: Equatable, Sendable { public let shared: SharedRunOptions public let templateDirectory: String public let templateVarsDirectory: String? public let useVault: Bool public init( shared: SharedRunOptions, templateDirectory: String, templateVarsDirectory: String? = nil, useVault: Bool = true ) { self.shared = shared self.templateDirectory = templateDirectory self.templateVarsDirectory = templateVarsDirectory self.useVault = useVault } public subscript(dynamicMember keyPath: KeyPath) -> T { shared[keyPath: keyPath] } } } } extension PlaybookClient.Repository: DependencyKey { public static var liveValue: Self { .init { try await installPlaybook(configuration: $0) } directory: { try await findDirectory(configuration: $0) } } } extension PlaybookClient.RunPlaybook: DependencyKey { public static var liveValue: PlaybookClient.RunPlaybook { .init( buildProject: { try await $0.run() }, createProject: { try await $0.run(encoder: $1) }, generateTemplate: { try await $0.run() } ) } } extension PlaybookClient: DependencyKey { public static let testValue: PlaybookClient = Self( repository: Repository(), run: RunPlaybook() ) public static var liveValue: PlaybookClient { .init( repository: .liveValue, run: .liveValue ) } }