feat: Adds generate commands that call to pandoc to generate pdf, latex, and html files from a project.

This commit is contained in:
2024-12-13 15:33:20 -05:00
parent d1b3379815
commit 3f56dda568
22 changed files with 606 additions and 57 deletions

View File

@@ -9,9 +9,13 @@ struct Application: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: Constants.appName,
abstract: createAbstract("A utility for working with ansible hpa playbook."),
version: VERSION,
version: VERSION ?? "0.0.0",
subcommands: [
BuildCommand.self, CreateCommand.self, VaultCommand.self, UtilsCommand.self
BuildCommand.self,
CreateCommand.self,
GenerateCommand.self,
VaultCommand.self,
UtilsCommand.self
]
)

View File

@@ -1,6 +1,7 @@
import ArgumentParser
import CliClient
import Dependencies
import Foundation
struct BuildCommand: AsyncParsableCommand {
@@ -9,16 +10,23 @@ struct BuildCommand: AsyncParsableCommand {
static let configuration = CommandConfiguration.playbook(
commandName: commandName,
abstract: "Build a home performance assesment project.",
examples: (label: "Build Project", example: "\(commandName) /path/to/project")
examples: (
label: "Build project when in the project directory.",
example: "\(commandName)"
),
(
label: "Build project from outside the project directory.",
example: "\(commandName) --project-directory /path/to/project"
)
)
@OptionGroup var globals: GlobalOptions
@Argument(
@Option(
help: "Path to the project directory.",
completion: .directory
)
var projectDir: String
var projectDirectory: String?
@Argument(
help: "Extra arguments / options passed to the playbook."
@@ -27,19 +35,21 @@ struct BuildCommand: AsyncParsableCommand {
mutating func run() async throws {
try await _run()
// try await runPlaybook(
// commandName: Self.commandName,
// globals: globals,
// extraArgs: extraArgs,
// "--tags", "build-project",
// "--extra-vars", "project_dir=\(projectDir)"
// )
}
private func _run() async throws {
@Dependency(\.cliClient) var cliClient
let projectDir: String
if projectDirectory == nil {
guard let pwd = ProcessInfo.processInfo.environment["PWD"] else {
throw ProjectDirectoryNotSupplied()
}
projectDir = pwd
} else {
projectDir = projectDirectory!
}
try await cliClient.runPlaybookCommand(
globals.playbookOptions(
arguments: [
@@ -52,3 +62,5 @@ struct BuildCommand: AsyncParsableCommand {
)
}
}
struct ProjectDirectoryNotSupplied: Error {}

View File

@@ -0,0 +1,14 @@
import ArgumentParser
struct GenerateCommand: AsyncParsableCommand {
static let commandName = "generate"
static let configuration = CommandConfiguration(
commandName: commandName,
subcommands: [
GeneratePdfCommand.self,
GenerateLatexCommand.self,
GenerateHtmlCommand.self
]
)
}

View File

@@ -0,0 +1,24 @@
import ArgumentParser
import CliClient
import Dependencies
// TODO: Need to add a step to build prior to generating file.
struct GenerateHtmlCommand: AsyncParsableCommand {
static let commandName = "html"
static let configuration = CommandConfiguration(
commandName: commandName
)
@OptionGroup var globals: GenerateOptions
mutating func run() async throws {
@Dependency(\.cliClient) var cliClient
try await cliClient.runPandocCommand(
globals.pandocOptions(.html),
logging: globals.loggingOptions(commandName: Self.commandName)
)
}
}

View File

@@ -0,0 +1,23 @@
import ArgumentParser
import CliClient
import Dependencies
// TODO: Need to add a step to build prior to generating file.
struct GenerateLatexCommand: AsyncParsableCommand {
static let commandName = "latex"
static let configuration = CommandConfiguration(
commandName: commandName
)
@OptionGroup var globals: GenerateOptions
mutating func run() async throws {
@Dependency(\.cliClient) var cliClient
try await cliClient.runPandocCommand(
globals.pandocOptions(.latex),
logging: globals.loggingOptions(commandName: Self.commandName)
)
}
}

View File

@@ -0,0 +1,87 @@
import ArgumentParser
import CliClient
@dynamicMemberLookup
struct GenerateOptions: ParsableArguments {
@OptionGroup var basic: BasicGlobalOptions
@Option(
name: [.short, .customLong("file")],
help: "Files used to generate the output, can be specified multiple times.",
completion: .file()
)
var files: [String] = []
@Option(
name: [.customShort("H"), .long],
help: "Files to include in the header, can be specified multiple times.",
completion: .file()
)
var includeInHeader: [String] = []
@Flag(
help: "Do not build the project prior to generating the output."
)
var noBuild: Bool = false
@Option(
name: .shortAndLong,
help: "The project directory.",
completion: .directory
)
var projectDirectory: String?
@Option(
name: .shortAndLong,
help: "The output directory",
completion: .directory
)
var outputDirectory: String?
@Option(
name: [.customShort("n"), .customLong("name")],
help: "Name of the output file."
)
var outputFileName: String?
// NOTE: This must be last, both here and in the commands, so if the commands have options of their
// own, they must be declared ahead of using the global options.
@Argument(
help: "Extra arguments / options to pass to the underlying pandoc command."
)
var extraOptions: [String] = []
subscript<T>(dynamicMember keyPath: WritableKeyPath<BasicGlobalOptions, T>) -> T {
get { basic[keyPath: keyPath] }
set { basic[keyPath: keyPath] = newValue }
}
subscript<T>(dynamicMember keyPath: KeyPath<BasicGlobalOptions, T>) -> T {
basic[keyPath: keyPath]
}
}
extension GenerateOptions {
func loggingOptions(commandName: String) -> CliClient.LoggingOptions {
basic.loggingOptions(commandName: commandName)
}
func pandocOptions(
_ fileType: CliClient.PandocOptions.FileType
) -> CliClient.PandocOptions {
.init(
files: files.count > 0 ? files : nil,
includeInHeader: includeInHeader.count > 0 ? includeInHeader : nil,
outputDirectory: outputDirectory,
outputFileName: outputFileName,
outputFileType: fileType,
projectDirectory: projectDirectory,
quiet: basic.quiet,
shell: basic.shell,
shouldBuild: !noBuild
)
}
}

View File

@@ -0,0 +1,32 @@
import ArgumentParser
import CliClient
import Dependencies
// TODO: Need to add a step to build prior to generating file.
struct GeneratePdfCommand: AsyncParsableCommand {
static let commandName = "pdf"
static let configuration = CommandConfiguration(
commandName: commandName
)
@Option(
name: [.customShort("e"), .customLong("engine")],
help: "The pdf engine to use."
)
var pdfEngine: String?
@OptionGroup var globals: GenerateOptions
mutating func run() async throws {
@Dependency(\.cliClient) var cliClient
let output = try await cliClient.runPandocCommand(
globals.pandocOptions(.pdf(engine: pdfEngine)),
logging: globals.loggingOptions(commandName: Self.commandName)
)
print(output)
}
}

View File

@@ -27,10 +27,10 @@ struct BasicGlobalOptions: ParsableArguments {
struct GlobalOptions: ParsableArguments {
@Option(
name: .shortAndLong,
name: .long,
help: "Optional path to the ansible hpa playbook directory."
)
var playbookDir: String?
var playbookDirectory: String?
@Option(
name: .shortAndLong,

View File

@@ -11,7 +11,7 @@ extension GlobalOptions {
arguments: arguments,
configuration: configuration,
inventoryFilePath: inventoryPath,
playbookDirectory: playbookDir,
playbookDirectory: playbookDirectory,
quiet: quietOnlyPlaybook ? true : basic.quiet,
shell: basic.shell
)

View File

@@ -0,0 +1,2 @@
// Do not set this variable, it is set during the build process.
let VERSION: String? = nil