From 54c07886add481d280c66930184ead267a64bef4 Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Tue, 17 Dec 2024 13:32:05 -0500 Subject: [PATCH] feat: Adding documentation comments. --- .../ConfigurationClient.swift | 50 ++++++-- Sources/PandocClient/Constants.swift | 2 + Sources/PandocClient/PandocClient+Run.swift | 30 +++-- Sources/PandocClient/PandocClient.swift | 60 +++++++-- Sources/PlaybookClient/Constants.swift | 4 +- Sources/PlaybookClient/PlaybookClient.swift | 117 +++++++++++++++--- Sources/VaultClient/VaultClient.swift | 33 +++-- .../hpa/Internal/VaultOptions+Globals.swift | 17 --- 8 files changed, 245 insertions(+), 68 deletions(-) delete mode 100644 Sources/hpa/Internal/VaultOptions+Globals.swift diff --git a/Sources/ConfigurationClient/ConfigurationClient.swift b/Sources/ConfigurationClient/ConfigurationClient.swift index 31e2878..d04c157 100644 --- a/Sources/ConfigurationClient/ConfigurationClient.swift +++ b/Sources/ConfigurationClient/ConfigurationClient.swift @@ -38,7 +38,7 @@ public struct ConfigurationClient: Sendable { /// ## NOTE: This uses the `fileClient.write` under the hood, so if you need to control /// what happens during tests, then you can customize the behavior of the fileClient. /// - var write: @Sendable (File, Configuration, Bool) async throws -> Void + public var write: @Sendable (WriteOptions) async throws -> Void /// Find the user's configuration and load it. public func findAndLoad() async throws -> Configuration { @@ -62,13 +62,19 @@ public struct ConfigurationClient: Sendable { to file: File, force: Bool = false ) async throws { - try await write(file, configuration, force) + try await write(.init(configuration, to: file, force: force)) } + /// Represents the options to generate a configuration file for a user. public struct GenerateOptions: Equatable, Sendable { + /// Force generation, overwritting existing file if it exists. public let force: Bool + + /// Generate a `json` file instead of the default `toml` file. public let json: Bool + + /// The path to generate a file in. public let path: Path? public init( @@ -81,11 +87,39 @@ public struct ConfigurationClient: Sendable { self.path = path } + /// Represents the path option for generating a configuration file for a user. + /// + /// This can either be a full file path or a directory. If a directory is supplied + /// then we will use the default file name of 'config' and add the extension dependning + /// on if the caller wants a `json` or `toml` file. public enum Path: Equatable, Sendable { case file(File) case directory(String) } } + + /// Represents the options required to write a configuration file. + public struct WriteOptions: Equatable, Sendable { + + /// The configuration to wrtie to the file path. + public let configuration: Configuration + + /// The file path to write the configuration to. + public let file: File + + /// Force overwritting an existing file, if it exists. + public let force: Bool + + public init( + _ configuration: Configuration, + to file: File, + force: Bool + ) { + self.configuration = configuration + self.file = file + self.force = force + } + } } extension ConfigurationClient: DependencyKey { @@ -97,7 +131,7 @@ extension ConfigurationClient: DependencyKey { find: { try await liveClient.find() }, generate: { try await liveClient.generate($0) }, load: { try await liveClient.load(file: $0) }, - write: { try await liveClient.write($1, to: $0, force: $2) } + write: { try await liveClient.write($0) } ) } @@ -225,7 +259,7 @@ struct LiveConfigurationClient { } else { // Json does not allow comments, so we write the mock configuration // to the file path. - try await write(.mock, to: File(fileUrl)!, force: options.force) + try await write(.init(.mock, to: File(fileUrl)!, force: options.force)) } return fileUrl.cleanFilePath @@ -247,10 +281,12 @@ struct LiveConfigurationClient { } func write( - _ configuration: Configuration, - to file: File, - force: Bool + _ options: ConfigurationClient.WriteOptions ) async throws { + let configuration = options.configuration + let file = options.file + let force = options.force + let exists = fileManager.fileExists(file.url) if !force, exists { diff --git a/Sources/PandocClient/Constants.swift b/Sources/PandocClient/Constants.swift index a55c6bd..4981493 100644 --- a/Sources/PandocClient/Constants.swift +++ b/Sources/PandocClient/Constants.swift @@ -1,4 +1,6 @@ extension PandocClient { + + /// Represents constant string values needed internally. enum Constants { static let pandocCommand = "pandoc" static let defaultOutputFileName = "Report" diff --git a/Sources/PandocClient/PandocClient+Run.swift b/Sources/PandocClient/PandocClient+Run.swift index 987f93d..8836342 100644 --- a/Sources/PandocClient/PandocClient+Run.swift +++ b/Sources/PandocClient/PandocClient+Run.swift @@ -6,7 +6,10 @@ import PlaybookClient extension PandocClient.RunOptions { - func run(_ fileType: PandocClient.FileType) async throws -> String { + func run( + _ fileType: PandocClient.FileType, + _ environment: [String: String] + ) async throws -> String { @Dependency(\.commandClient) var commandClient @Dependency(\.logger) var logger @Dependency(\.playbookClient) var playbookClient @@ -14,13 +17,13 @@ extension PandocClient.RunOptions { return try await commandClient.run(logging: loggingOptions, quiet: quiet, shell: shell) { let ensuredOptions = try await self.ensuredOptions(fileType) - let projectDirectory = self.projectDirectory ?? ProcessInfo.processInfo.environment["PWD"] + let projectDirectory = self.projectDirectory ?? environment["PWD"] guard let projectDirectory else { throw ProjectDirectoryNotSpecified() } - if shouldBuild { + if shouldBuildProject { logger.debug("Building project...") try await playbookClient.run.buildProject(.init( projectDirectory: projectDirectory, @@ -65,6 +68,16 @@ extension PandocClient.RunOptions { } } +extension PandocClient.FileType { + var fileExtension: String { + switch self { + case .html: return "html" + case .latex: return "tex" + case .pdf: return "pdf" + } + } +} + @_spi(Internal) public struct EnsuredPandocOptions: Equatable, Sendable { public let buildDirectory: String @@ -76,16 +89,7 @@ public struct EnsuredPandocOptions: Equatable, Sendable { public let pdfEngine: String? public var ensuredExtensionFileName: String { - let extensionString: String - - switch outputFileType { - case .html: - extensionString = ".html" - case .latex: - extensionString = ".tex" - case .pdf: - extensionString = ".pdf" - } + let extensionString = ".\(outputFileType.fileExtension)" if !outputFileName.hasSuffix(extensionString) { return outputFileName + extensionString diff --git a/Sources/PandocClient/PandocClient.swift b/Sources/PandocClient/PandocClient.swift index 54894ea..c79089d 100644 --- a/Sources/PandocClient/PandocClient.swift +++ b/Sources/PandocClient/PandocClient.swift @@ -2,8 +2,15 @@ import CommandClient import ConfigurationClient import Dependencies import DependenciesMacros +import Foundation public extension DependencyValues { + + /// Represents interactions with the `pandoc` command line application. + /// + /// The `pandoc` command line application is used to generate the final output + /// documents from a home performance assessment project. + /// var pandocClient: PandocClient { get { self[PandocClient.self] } set { self[PandocClient.self] = newValue } @@ -13,14 +20,31 @@ public extension DependencyValues { @DependencyClient public struct PandocClient: Sendable { + /// Run a pandoc command. public var run: Run + /// Represents the pandoc commands that we can run. @DependencyClient public struct Run: Sendable { + + /// Generate a latex file from the given options, returning the path the + /// generated file was written to. public var generateLatex: @Sendable (RunOptions) async throws -> String + + /// Generate an html file from the given options, returning the path the + /// generated file was written to. public var generateHtml: @Sendable (RunOptions) async throws -> String + + /// Generate a pdf file from the given options, returning the path the + /// generated file was written to. public var generatePdf: @Sendable (RunOptions, String?) async throws -> String + /// Generate a pdf file from the given options, returning the path the + /// generated file was written to. + /// + /// - Parameters: + /// - options: The shared run options. + /// - pdfEngine: The pdf-engine to use to generate the pdf file. public func generatePdf( _ options: RunOptions, pdfEngine: String? = nil @@ -30,6 +54,9 @@ public struct PandocClient: Sendable { } + /// Represents the shared options used to run a `pandoc` command. + /// + /// public struct RunOptions: Equatable, Sendable { let buildDirectory: String? @@ -42,8 +69,22 @@ public struct PandocClient: Sendable { let projectDirectory: String? let quiet: Bool let shell: String? - let shouldBuild: Bool + let shouldBuildProject: Bool + /// Create the shared run options. + /// + /// - Parameters: + /// - buildDirectory: Specify the build directory of the project. + /// - extraOptions: Extra arguments / options passed to the `pandoc` command. + /// - file: Files used to build the final output. + /// - loggingOptions: The logging options used when running the command. + /// - includeInHeader: Files to include in the header of the final output document. + /// - outputDirectory: Optional output directory for the output file, defaults to current working directory. + /// - projectDirectory: Optional project directory, defaults to current working directory. + /// - outputFileName: Override the output file name. + /// - quiet: Don't log output from the command. + /// - shell: Optional shell to use when calling the pandoc command. + /// - shouldBuildProject: Build the project prior to generating the output file. public init( buildDirectory: String? = nil, extraOptions: [String]? = nil, @@ -55,7 +96,7 @@ public struct PandocClient: Sendable { outputFileName: String? = nil, quiet: Bool = false, shell: String? = nil, - shouldBuild: Bool = true + shouldBuild shouldBuildProject: Bool = true ) { self.buildDirectory = buildDirectory self.extraOptions = extraOptions @@ -67,11 +108,12 @@ public struct PandocClient: Sendable { self.projectDirectory = projectDirectory self.quiet = quiet self.shell = shell - self.shouldBuild = shouldBuild + self.shouldBuildProject = shouldBuildProject } } + /// Represents the file types that we can generate output files for. @_spi(Internal) public enum FileType: Equatable, Sendable { case html @@ -83,13 +125,17 @@ public struct PandocClient: Sendable { extension PandocClient: DependencyKey { public static let testValue: PandocClient = Self(run: Run()) - public static var liveValue: PandocClient { + public static func live(environment: [String: String]) -> PandocClient { .init( run: Run( - generateLatex: { try await $0.run(.latex) }, - generateHtml: { try await $0.run(.html) }, - generatePdf: { try await $0.run(.pdf(engine: $1)) } + generateLatex: { try await $0.run(.latex, environment) }, + generateHtml: { try await $0.run(.html, environment) }, + generatePdf: { try await $0.run(.pdf(engine: $1), environment) } ) ) } + + public static var liveValue: PandocClient { + .live(environment: ProcessInfo.processInfo.environment) + } } diff --git a/Sources/PlaybookClient/Constants.swift b/Sources/PlaybookClient/Constants.swift index 9f9d56a..9e4b74c 100644 --- a/Sources/PlaybookClient/Constants.swift +++ b/Sources/PlaybookClient/Constants.swift @@ -1,4 +1,6 @@ -// TODO: Use an actuall version tag for playbook repo. +// TODO: Use an actual version tag for playbook repo. +// TODO: Use an externally public url for the playbook repo. + public extension PlaybookClient { @_spi(Internal) enum Constants { diff --git a/Sources/PlaybookClient/PlaybookClient.swift b/Sources/PlaybookClient/PlaybookClient.swift index 9bf84f6..b4dd788 100644 --- a/Sources/PlaybookClient/PlaybookClient.swift +++ b/Sources/PlaybookClient/PlaybookClient.swift @@ -9,6 +9,11 @@ import ShellClient // TODO: Add update checks and pull for the playbook. public extension DependencyValues { + + /// Manages interactions with the playbook repository as well as `ansible-playbook` + /// command line application. + /// + /// var playbookClient: PlaybookClient { get { self[PlaybookClient.self] } set { self[PlaybookClient.self] = newValue } @@ -17,21 +22,36 @@ public extension DependencyValues { @DependencyClient public struct PlaybookClient: Sendable { + + /// Manages interactions with the playbook repository. public var repository: Repository + + /// Run the `ansible-playbook` command line application. public var run: RunPlaybook } public extension PlaybookClient { + /// Manages interactions with the `ansible-hpa-playbook` repository, which is + /// used to build and generate home performance assessment projects. @DependencyClient struct Repository: Sendable { + + /// Install the repository based on the given configuration. If configuration is + /// not supplied then the default location for the playbook repository is + /// `~/.local/share/hpa/playbook` public var install: @Sendable (Configuration?) async throws -> Void + + /// Get the current directory of the playbook repository based on the given + /// configuration. public var directory: @Sendable (Configuration?) async throws -> String + /// Install the playbook in the default location. public func install() async throws { try await install(nil) } + /// Get the current directory path of the playbook repository. public func directory() async throws -> String { try await directory(nil) } @@ -40,23 +60,56 @@ public extension PlaybookClient { public extension PlaybookClient { + /// Runs the `ansible-playbook` command line application with the `ansible-hpa-playbook`. + /// + /// This is used to build and create home performance projects. It can also generate a + /// template for a user to customize to their use case. @DependencyClient struct RunPlaybook: Sendable { + + /// Build a home performance assesment project with the given options. public var buildProject: @Sendable (BuildOptions) async throws -> Void + + /// Create a new home performance assesment project with the given options. public var createProject: @Sendable (CreateOptions, JSONEncoder?) async throws -> Void + + /// Generate a user's template from the default home performance template. public var generateTemplate: @Sendable (GenerateTemplateOptions) async throws -> String + /// Create a new home performance assesment project with the given options. + /// + /// - Parameters: + /// - options: The options used to create the project. public func createProject(_ options: CreateOptions) async throws { try await createProject(options, nil) } + /// Represents options that are shared for all the `ansible-playbook` commands. 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? + /// Extra arguments / options passed to the `ansible-playbook` command. + let extraOptions: [String]? + + /// Specify the inventory file path. + let inventoryFilePath: String? + + /// The logging options used when running the command. + let loggingOptions: LoggingOptions + + /// Disables log output of the command. + let quiet: Bool + + /// Optional shell to use when running the command. + let shell: String? + + /// Create the shared options. + /// + /// - Parameters: + /// - extraOptions: The extra arguments / options to pass to the command. + /// - inventoryFilePath: Specify the inventory file path. + /// - loggingOptions: The logging options used when running the command. + /// - quiet: Disable log output from the command. + /// - shell: Specify a shell used when running the command. public init( extraOptions: [String]? = nil, inventoryFilePath: String? = nil, @@ -72,11 +125,21 @@ public extension PlaybookClient { } } + /// Options require when calling the build project command. @dynamicMemberLookup public struct BuildOptions: Equatable, Sendable { - public let projectDirectory: String? - public let shared: SharedRunOptions + /// An optional project directory, if not supplied then we will use the current working directory. + let projectDirectory: String? + + /// The shared run options. + let shared: SharedRunOptions + + /// Create new build options. + /// + /// - Parameters: + /// - projectDirectory: The optional project directory to build, if not supplied then we'll use the current working directory. + /// - shared: The shared run options. public init( projectDirectory: String? = nil, shared: SharedRunOptions @@ -90,13 +153,25 @@ public extension PlaybookClient { } } + /// Options required when creating a new home performance assessment project. @dynamicMemberLookup public struct CreateOptions: Equatable, Sendable { - public let projectDirectory: String - public let shared: SharedRunOptions - public let template: Configuration.Template? - public let useLocalTemplateDirectory: Bool + /// The directory to generate the new project in. + let projectDirectory: String + /// Shared run options. + let shared: SharedRunOptions + /// Custom template configuration to use. + let template: Configuration.Template? + /// Specify whether we should only use a local template directory to create the project from. + let useLocalTemplateDirectory: Bool + /// Create new create options. + /// + /// - Parameters: + /// - projectDirectory: The directory to generate the project in. + /// - shared: The shared run options. + /// - template: Custom template configuration used when generating the project. + /// - useLocalTemplateDirectory: Whether to use a local template directory, not a template repository. public init( projectDirectory: String, shared: SharedRunOptions, @@ -114,13 +189,25 @@ public extension PlaybookClient { } } + /// Options required when generating a new template repository / directory. @dynamicMemberLookup public struct GenerateTemplateOptions: Equatable, Sendable { - public let shared: SharedRunOptions - public let templateDirectory: String - public let templateVarsDirectory: String? - public let useVault: Bool + /// The shared run options. + let shared: SharedRunOptions + /// The path to generate the template in. + let templateDirectory: String + /// Specify the name of the directory for template variables. + let templateVarsDirectory: String? + /// Specify whether to use `ansible-vault` encrypted variables. + let useVault: Bool + /// Create new generate template options. + /// + /// - Parameters: + /// - shared: The shared run options + /// - templateDirectory: The path to generate the template in. + /// - templateVarsDirectory: Specify the name of the directory for template variables. + /// - useVault: Specify wheter to use `ansible-vault` encrypted variables. public init( shared: SharedRunOptions, templateDirectory: String, diff --git a/Sources/VaultClient/VaultClient.swift b/Sources/VaultClient/VaultClient.swift index 8fcadee..fcca634 100644 --- a/Sources/VaultClient/VaultClient.swift +++ b/Sources/VaultClient/VaultClient.swift @@ -4,9 +4,8 @@ import Dependencies import DependenciesMacros import FileClient -// TODO: Add edit / view routes, possibly create? - public extension DependencyValues { + /// Manages interactions with `ansible-vault` command line application. var vaultClient: VaultClient { get { self[VaultClient.self] } set { self[VaultClient.self] = newValue } @@ -15,23 +14,41 @@ public extension DependencyValues { @DependencyClient public struct VaultClient: Sendable { + /// Run an `ansible-vault` command. public var run: Run @DependencyClient public struct Run: Sendable { + /// Decrypt an `ansible-vault` file. public var decrypt: @Sendable (RunOptions) async throws -> String + /// Encrypt an `ansible-vault` file. public var encrypt: @Sendable (RunOptions) async throws -> String } public struct RunOptions: Equatable, Sendable { - public let extraOptions: [String]? - public let loggingOptions: LoggingOptions - public let outputFilePath: String? - public let quiet: Bool - public let shell: String? - public let vaultFilePath: String? + /// Extra arguments / options passed to the `ansible-vault` command. + let extraOptions: [String]? + /// Logging options to use while running the command. + let loggingOptions: LoggingOptions + /// The optional output file path. + let outputFilePath: String? + /// Disable logging output. + let quiet: Bool + /// Optional shell used to call the command. + let shell: String? + /// The path to the vault file, if not supplied we will search the current directory for it. + let vaultFilePath: String? + /// Create new run options. + /// + /// - Parameters: + /// - extraOptions: Extra arguments / options passed to the command. + /// - loggingOptions: The logging options used while running the command. + /// - outputFilePath: The optional output file path. + /// - quiet: Disable logging output of the command. + /// - shell: The shell used to call the command. + /// - vaultFilePath: The vault file, if not supplied we will search in the current working directory. public init( extraOptions: [String]? = nil, loggingOptions: LoggingOptions, diff --git a/Sources/hpa/Internal/VaultOptions+Globals.swift b/Sources/hpa/Internal/VaultOptions+Globals.swift deleted file mode 100644 index c9fc390..0000000 --- a/Sources/hpa/Internal/VaultOptions+Globals.swift +++ /dev/null @@ -1,17 +0,0 @@ -// import ConfigurationClient -// -// extension VaultOptions { -// -// func vaultOptions( -// arguments: [String], -// configuration: Configuration? -// ) -> CliClient.VaultOptions { -// .init( -// arguments: arguments, -// configuration: configuration, -// quiet: globals.quiet, -// shell: globals.shell, -// vaultFilePath: file -// ) -// } -// }