224 lines
6.3 KiB
Swift
224 lines
6.3 KiB
Swift
import ConfigurationClient
|
|
import Dependencies
|
|
import Foundation
|
|
import ShellClient
|
|
|
|
// TODO: Need to parse / ensure that header includes and files are in the build directory, not sure
|
|
// exactly how to handle if they're not, but it seems reasonable to potentially allow files that are
|
|
// outside of the build directory to be included.
|
|
|
|
public extension CliClient {
|
|
|
|
@discardableResult
|
|
func runPandocCommand(
|
|
_ options: PandocOptions,
|
|
logging loggingOptions: LoggingOptions
|
|
) async throws -> String {
|
|
try await withLogger(loggingOptions) {
|
|
@Dependency(\.configurationClient) var configurationClient
|
|
@Dependency(\.logger) var logger
|
|
|
|
let configuration = try await configurationClient.findAndLoad()
|
|
logger.trace("Configuration: \(configuration)")
|
|
|
|
let ensuredOptions = try await ensurePandocOptions(
|
|
configuration: configuration,
|
|
options: options
|
|
)
|
|
|
|
let projectDirectory = options.projectDirectory ?? ProcessInfo.processInfo.environment["PWD"]
|
|
guard let projectDirectory else {
|
|
throw CliClientError.generate(.projectDirectoryNotSpecified)
|
|
}
|
|
|
|
let outputDirectory = options.outputDirectory ?? projectDirectory
|
|
let outputPath = "\(outputDirectory)/\(ensuredOptions.ensuredExtensionFileName)"
|
|
|
|
var arguments = [
|
|
"pandoc"
|
|
]
|
|
arguments += ensuredOptions.includeInHeader.map {
|
|
"--include-in-header=\(projectDirectory)/\(ensuredOptions.buildDirectory)/\($0)"
|
|
}
|
|
|
|
if let pdfEngine = ensuredOptions.pdfEngine {
|
|
arguments.append("--pdf-engine=\(pdfEngine)")
|
|
}
|
|
|
|
arguments.append("--output=\(outputPath)")
|
|
arguments += ensuredOptions.files.map {
|
|
"\(projectDirectory)/\(ensuredOptions.buildDirectory)/\($0)"
|
|
}
|
|
|
|
if options.shouldBuild {
|
|
logger.trace("Building project...")
|
|
try await runPlaybookCommand(
|
|
.init(
|
|
arguments: [
|
|
"--tags", "build-project",
|
|
"--extra-vars", "project_dir=\(projectDirectory)"
|
|
],
|
|
configuration: configuration,
|
|
quiet: options.quiet,
|
|
shell: options.shell
|
|
),
|
|
logging: loggingOptions
|
|
)
|
|
}
|
|
|
|
logger.trace("Running pandoc with arguments: \(arguments)")
|
|
|
|
try await runCommand(
|
|
quiet: options.quiet,
|
|
shell: options.shell.orDefault,
|
|
arguments
|
|
)
|
|
|
|
return outputPath
|
|
}
|
|
}
|
|
}
|
|
|
|
@_spi(Internal)
|
|
public struct EnsuredPandocOptions: Equatable, Sendable {
|
|
public let buildDirectory: String
|
|
public let files: [String]
|
|
public let includeInHeader: [String]
|
|
public let outputFileName: String
|
|
public let outputFileType: CliClient.PandocOptions.FileType
|
|
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"
|
|
}
|
|
|
|
if !outputFileName.hasSuffix(extensionString) {
|
|
return outputFileName + extensionString
|
|
}
|
|
return outputFileName
|
|
}
|
|
}
|
|
|
|
@_spi(Internal)
|
|
public func ensurePandocOptions(
|
|
configuration: Configuration,
|
|
options: CliClient.PandocOptions
|
|
) async throws -> EnsuredPandocOptions {
|
|
let defaults = Configuration.Generate.default
|
|
let pdfEngine = parsePdfEngine(configuration.generate, defaults, options)
|
|
|
|
return .init(
|
|
buildDirectory: parseBuildDirectory(configuration.generate, defaults, options),
|
|
files: parseFiles(configuration.generate, defaults, options),
|
|
includeInHeader: parseIncludeInHeader(configuration.generate, defaults, options),
|
|
outputFileName: parseOutputFileName(configuration.generate, defaults, options),
|
|
outputFileType: options.outputFileType,
|
|
pdfEngine: pdfEngine
|
|
)
|
|
}
|
|
|
|
private func parsePdfEngine(
|
|
_ configuration: Configuration.Generate?,
|
|
_ defaults: Configuration.Generate,
|
|
_ options: CliClient.PandocOptions
|
|
) -> String? {
|
|
switch options.outputFileType {
|
|
case .html, .latex:
|
|
return nil
|
|
case let .pdf(engine: engine):
|
|
if let engine {
|
|
return engine
|
|
} else if let engine = configuration?.pdfEngine {
|
|
return engine
|
|
} else if let engine = defaults.pdfEngine {
|
|
return engine
|
|
} else {
|
|
return "xelatex"
|
|
}
|
|
}
|
|
}
|
|
|
|
private func parseFiles(
|
|
_ configuration: Configuration.Generate?,
|
|
_ defaults: Configuration.Generate,
|
|
_ options: CliClient.PandocOptions
|
|
) -> [String] {
|
|
@Dependency(\.logger) var logger
|
|
|
|
if let files = options.files {
|
|
return files
|
|
} else if let files = configuration?.files {
|
|
return files
|
|
} else if let files = defaults.files {
|
|
return files
|
|
} else {
|
|
logger.warning("Files not specified, this could lead to errors.")
|
|
return []
|
|
}
|
|
}
|
|
|
|
private func parseIncludeInHeader(
|
|
_ configuration: Configuration.Generate?,
|
|
_ defaults: Configuration.Generate,
|
|
_ options: CliClient.PandocOptions
|
|
) -> [String] {
|
|
@Dependency(\.logger) var logger
|
|
|
|
if let files = options.includeInHeader {
|
|
return files
|
|
} else if let files = configuration?.includeInHeader {
|
|
return files
|
|
} else if let files = defaults.includeInHeader {
|
|
return files
|
|
} else {
|
|
logger.warning("Include in header files not specified, this could lead to errors.")
|
|
return []
|
|
}
|
|
}
|
|
|
|
private func parseOutputFileName(
|
|
_ configuration: Configuration.Generate?,
|
|
_ defaults: Configuration.Generate,
|
|
_ options: CliClient.PandocOptions
|
|
) -> String {
|
|
@Dependency(\.logger) var logger
|
|
|
|
if let output = options.outputFileName {
|
|
return output
|
|
} else if let output = configuration?.outputFileName {
|
|
return output
|
|
} else if let output = defaults.outputFileName {
|
|
return output
|
|
} else {
|
|
logger.warning("Output file name not specified, this could lead to errors.")
|
|
return "Report"
|
|
}
|
|
}
|
|
|
|
private func parseBuildDirectory(
|
|
_ configuration: Configuration.Generate?,
|
|
_ defaults: Configuration.Generate,
|
|
_ options: CliClient.PandocOptions
|
|
) -> String {
|
|
@Dependency(\.logger) var logger
|
|
|
|
if let output = options.buildDirectory {
|
|
return output
|
|
} else if let output = configuration?.buildDirectory {
|
|
return output
|
|
} else if let output = defaults.buildDirectory {
|
|
return output
|
|
} else {
|
|
logger.warning("Output file name not specified, this could lead to errors.")
|
|
return ".build"
|
|
}
|
|
}
|