feat: Fixes generation of html document, it now builds a latex document in the build directory that it then converts to html. Need to update tests.
This commit is contained in:
@@ -4,10 +4,10 @@ import DependenciesMacros
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ShellClient
|
import ShellClient
|
||||||
|
|
||||||
public extension DependencyValues {
|
extension DependencyValues {
|
||||||
|
|
||||||
/// Runs shell commands.
|
/// Runs shell commands.
|
||||||
var commandClient: CommandClient {
|
public var commandClient: CommandClient {
|
||||||
get { self[CommandClient.self] }
|
get { self[CommandClient.self] }
|
||||||
set { self[CommandClient.self] = newValue }
|
set { self[CommandClient.self] = newValue }
|
||||||
}
|
}
|
||||||
@@ -67,12 +67,13 @@ public struct CommandClient: Sendable {
|
|||||||
in workingDirectory: String? = nil,
|
in workingDirectory: String? = nil,
|
||||||
_ arguments: [String]
|
_ arguments: [String]
|
||||||
) async throws {
|
) async throws {
|
||||||
try await runCommand(.init(
|
try await runCommand(
|
||||||
arguments: arguments,
|
.init(
|
||||||
quiet: quiet,
|
arguments: arguments,
|
||||||
shell: shell,
|
quiet: quiet,
|
||||||
workingDirectory: workingDirectory
|
shell: shell,
|
||||||
))
|
workingDirectory: workingDirectory
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs a shell command.
|
/// Runs a shell command.
|
||||||
@@ -161,19 +162,21 @@ extension CommandClient: DependencyKey {
|
|||||||
.init { options in
|
.init { options in
|
||||||
@Dependency(\.asyncShellClient) var shellClient
|
@Dependency(\.asyncShellClient) var shellClient
|
||||||
if !options.quiet {
|
if !options.quiet {
|
||||||
try await shellClient.foreground(.init(
|
try await shellClient.foreground(
|
||||||
shell: .init(options.shell),
|
.init(
|
||||||
environment: environment,
|
shell: .init(options.shell),
|
||||||
in: options.workingDirectory,
|
environment: environment,
|
||||||
options.arguments
|
in: options.workingDirectory,
|
||||||
))
|
options.arguments
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
try await shellClient.background(.init(
|
try await shellClient.background(
|
||||||
shell: .init(options.shell),
|
.init(
|
||||||
environment: environment,
|
shell: .init(options.shell),
|
||||||
in: options.workingDirectory,
|
environment: environment,
|
||||||
options.arguments
|
in: options.workingDirectory,
|
||||||
))
|
options.arguments
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -184,12 +187,12 @@ extension CommandClient: DependencyKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@_spi(Internal)
|
@_spi(Internal)
|
||||||
public extension CommandClient {
|
extension CommandClient {
|
||||||
|
|
||||||
/// Create a command client that can capture the arguments / options.
|
/// Create a command client that can capture the arguments / options.
|
||||||
///
|
///
|
||||||
/// This is used for testing.
|
/// This is used for testing.
|
||||||
static func capturing(_ client: CapturingClient) -> Self {
|
public static func capturing(_ client: CapturingClient) -> Self {
|
||||||
.init { options in
|
.init { options in
|
||||||
await client.set(options)
|
await client.set(options)
|
||||||
}
|
}
|
||||||
@@ -198,7 +201,7 @@ public extension CommandClient {
|
|||||||
/// Captures the arguments / options passed into the command client's run commands.
|
/// Captures the arguments / options passed into the command client's run commands.
|
||||||
///
|
///
|
||||||
@dynamicMemberLookup
|
@dynamicMemberLookup
|
||||||
actor CapturingClient: Sendable {
|
public actor CapturingClient: Sendable {
|
||||||
public private(set) var options: RunCommandOptions?
|
public private(set) var options: RunCommandOptions?
|
||||||
|
|
||||||
public init() {}
|
public init() {}
|
||||||
|
|||||||
@@ -12,20 +12,67 @@ extension PandocClient.RunOptions {
|
|||||||
) async throws -> String {
|
) async throws -> String {
|
||||||
@Dependency(\.commandClient) var commandClient
|
@Dependency(\.commandClient) var commandClient
|
||||||
@Dependency(\.logger) var logger
|
@Dependency(\.logger) var logger
|
||||||
|
|
||||||
|
// return try await commandClient.run(logging: loggingOptions, quiet: quiet, shell: shell) {
|
||||||
|
let ensuredOptions = try await self.ensuredOptions(fileType)
|
||||||
|
|
||||||
|
let projectDirectory = self.projectDirectory ?? environment["PWD"]
|
||||||
|
|
||||||
|
guard let projectDirectory else {
|
||||||
|
throw ProjectDirectoryNotSpecified()
|
||||||
|
}
|
||||||
|
|
||||||
|
try await buildProject(fileType, projectDirectory, ensuredOptions)
|
||||||
|
|
||||||
|
let outputDirectory = self.outputDirectory ?? projectDirectory
|
||||||
|
let outputPath = "\(outputDirectory)/\(ensuredOptions.ensuredExtensionFileName)"
|
||||||
|
|
||||||
|
let arguments = ensuredOptions.makeArguments(
|
||||||
|
fileType: fileType,
|
||||||
|
outputPath: outputPath,
|
||||||
|
projectDirectory: projectDirectory
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug("Pandoc arguments: \(arguments)")
|
||||||
|
return try await runCommand(arguments, outputPath)
|
||||||
|
|
||||||
|
// return (arguments, outputPath)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs a shell command with the given arguments, returning the passed in output path
|
||||||
|
/// so the command can be chained, if needed.
|
||||||
|
///
|
||||||
|
@_spi(Internal)
|
||||||
|
@discardableResult
|
||||||
|
public func runCommand(
|
||||||
|
_ arguments: [String],
|
||||||
|
_ outputPath: String
|
||||||
|
) async throws -> String {
|
||||||
|
@Dependency(\.commandClient) var commandClient
|
||||||
|
@Dependency(\.logger) var logger
|
||||||
|
logger.debug("Running shell command with arguments: \(arguments)")
|
||||||
|
return try await commandClient.run(logging: loggingOptions, quiet: quiet, shell: shell) {
|
||||||
|
(arguments, outputPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the project if necessary, before running the shell command that builds the final
|
||||||
|
/// output file(s).
|
||||||
|
///
|
||||||
|
@_spi(Internal)
|
||||||
|
public func buildProject(
|
||||||
|
_ fileType: PandocClient.FileType,
|
||||||
|
_ projectDirectory: String,
|
||||||
|
_ ensuredOptions: EnsuredPandocOptions
|
||||||
|
) async throws {
|
||||||
|
@Dependency(\.logger) var logger
|
||||||
@Dependency(\.playbookClient) var playbookClient
|
@Dependency(\.playbookClient) var playbookClient
|
||||||
|
|
||||||
return try await commandClient.run(logging: loggingOptions, quiet: quiet, shell: shell) {
|
if shouldBuildProject {
|
||||||
let ensuredOptions = try await self.ensuredOptions(fileType)
|
logger.debug("Building project...")
|
||||||
|
try await playbookClient.run.buildProject(
|
||||||
let projectDirectory = self.projectDirectory ?? environment["PWD"]
|
.init(
|
||||||
|
|
||||||
guard let projectDirectory else {
|
|
||||||
throw ProjectDirectoryNotSpecified()
|
|
||||||
}
|
|
||||||
|
|
||||||
if shouldBuildProject {
|
|
||||||
logger.debug("Building project...")
|
|
||||||
try await playbookClient.run.buildProject(.init(
|
|
||||||
projectDirectory: projectDirectory,
|
projectDirectory: projectDirectory,
|
||||||
shared: .init(
|
shared: .init(
|
||||||
extraOptions: nil,
|
extraOptions: nil,
|
||||||
@@ -34,20 +81,21 @@ extension PandocClient.RunOptions {
|
|||||||
quiet: quiet,
|
quiet: quiet,
|
||||||
shell: shell
|
shell: shell
|
||||||
)
|
)
|
||||||
))
|
)
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let outputDirectory = self.outputDirectory ?? projectDirectory
|
// Build latex file pre-html, so that we can properly convert the latex document
|
||||||
let outputPath = "\(outputDirectory)/\(ensuredOptions.ensuredExtensionFileName)"
|
// into an html document.
|
||||||
|
if fileType == .html {
|
||||||
let arguments = ensuredOptions.makeArguments(
|
logger.debug("Building latex, pre-html conversion...")
|
||||||
|
let outputPath = "\(ensuredOptions.buildDirectory)/Report.tex"
|
||||||
|
let arguments = ensuredOptions.preHtmlLatexOptions.makeArguments(
|
||||||
|
fileType: .latex,
|
||||||
outputPath: outputPath,
|
outputPath: outputPath,
|
||||||
projectDirectory: projectDirectory
|
projectDirectory: projectDirectory
|
||||||
)
|
)
|
||||||
|
try await runCommand(arguments, outputPath)
|
||||||
logger.debug("Pandoc arguments: \(arguments)")
|
|
||||||
|
|
||||||
return (arguments, outputPath)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +128,8 @@ extension PandocClient.FileType {
|
|||||||
|
|
||||||
@_spi(Internal)
|
@_spi(Internal)
|
||||||
public struct EnsuredPandocOptions: Equatable, Sendable {
|
public struct EnsuredPandocOptions: Equatable, Sendable {
|
||||||
|
public static let latexFilename = "Report.tex"
|
||||||
|
|
||||||
public let buildDirectory: String
|
public let buildDirectory: String
|
||||||
public let extraOptions: [String]?
|
public let extraOptions: [String]?
|
||||||
public let files: [String]
|
public let files: [String]
|
||||||
@@ -97,14 +147,29 @@ public struct EnsuredPandocOptions: Equatable, Sendable {
|
|||||||
return outputFileName
|
return outputFileName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var preHtmlLatexOptions: Self {
|
||||||
|
.init(
|
||||||
|
buildDirectory: buildDirectory,
|
||||||
|
extraOptions: extraOptions,
|
||||||
|
files: files,
|
||||||
|
includeInHeader: includeInHeader,
|
||||||
|
outputFileName: Self.latexFilename,
|
||||||
|
outputFileType: .latex,
|
||||||
|
pdfEngine: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func makeArguments(
|
func makeArguments(
|
||||||
|
fileType: PandocClient.FileType,
|
||||||
outputPath: String,
|
outputPath: String,
|
||||||
projectDirectory: String
|
projectDirectory: String
|
||||||
) -> [String] {
|
) -> [String] {
|
||||||
var arguments = [PandocClient.Constants.pandocCommand]
|
var arguments = [PandocClient.Constants.pandocCommand]
|
||||||
|
|
||||||
arguments += includeInHeader.map {
|
if fileType != .html {
|
||||||
"--include-in-header=\(projectDirectory)/\(buildDirectory)/\($0)"
|
arguments += includeInHeader.map {
|
||||||
|
"--include-in-header=\(projectDirectory)/\(buildDirectory)/\($0)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let pdfEngine {
|
if let pdfEngine {
|
||||||
@@ -117,8 +182,12 @@ public struct EnsuredPandocOptions: Equatable, Sendable {
|
|||||||
arguments.append(contentsOf: extraOptions)
|
arguments.append(contentsOf: extraOptions)
|
||||||
}
|
}
|
||||||
|
|
||||||
arguments += files.map {
|
if fileType != .html {
|
||||||
"\(projectDirectory)/\(buildDirectory)/\($0)"
|
arguments += files.map {
|
||||||
|
"\(projectDirectory)/\(buildDirectory)/\($0)"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
arguments.append("\(projectDirectory)/\(buildDirectory)/\(Self.latexFilename)")
|
||||||
}
|
}
|
||||||
|
|
||||||
return arguments
|
return arguments
|
||||||
@@ -145,15 +214,15 @@ public func ensurePandocOptions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@_spi(Internal)
|
@_spi(Internal)
|
||||||
public extension PandocClient.FileType {
|
extension PandocClient.FileType {
|
||||||
func parsePdfEngine(
|
public func parsePdfEngine(
|
||||||
_ configuration: Configuration.Generate?,
|
_ configuration: Configuration.Generate?,
|
||||||
_ defaults: Configuration.Generate
|
_ defaults: Configuration.Generate
|
||||||
) -> String? {
|
) -> String? {
|
||||||
switch self {
|
switch self {
|
||||||
case .html, .latex:
|
case .html, .latex:
|
||||||
return nil
|
return nil
|
||||||
case let .pdf(engine: engine):
|
case .pdf(let engine):
|
||||||
if let engine {
|
if let engine {
|
||||||
return engine
|
return engine
|
||||||
} else if let engine = configuration?.pdfEngine {
|
} else if let engine = configuration?.pdfEngine {
|
||||||
@@ -168,8 +237,8 @@ public extension PandocClient.FileType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@_spi(Internal)
|
@_spi(Internal)
|
||||||
public extension PandocClient.RunOptions {
|
extension PandocClient.RunOptions {
|
||||||
func parseFiles(
|
public func parseFiles(
|
||||||
_ configuration: Configuration.Generate?,
|
_ configuration: Configuration.Generate?,
|
||||||
_ defaults: Configuration.Generate
|
_ defaults: Configuration.Generate
|
||||||
) -> [String] {
|
) -> [String] {
|
||||||
@@ -187,7 +256,7 @@ public extension PandocClient.RunOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseIncludeInHeader(
|
public func parseIncludeInHeader(
|
||||||
_ configuration: Configuration.Generate?,
|
_ configuration: Configuration.Generate?,
|
||||||
_ defaults: Configuration.Generate
|
_ defaults: Configuration.Generate
|
||||||
) -> [String] {
|
) -> [String] {
|
||||||
@@ -205,7 +274,7 @@ public extension PandocClient.RunOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseOutputFileName(
|
public func parseOutputFileName(
|
||||||
_ configuration: Configuration.Generate?,
|
_ configuration: Configuration.Generate?,
|
||||||
_ defaults: Configuration.Generate
|
_ defaults: Configuration.Generate
|
||||||
) -> String {
|
) -> String {
|
||||||
@@ -223,7 +292,7 @@ public extension PandocClient.RunOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseBuildDirectory(
|
public func parseBuildDirectory(
|
||||||
_ configuration: Configuration.Generate?,
|
_ configuration: Configuration.Generate?,
|
||||||
_ defaults: Configuration.Generate
|
_ defaults: Configuration.Generate
|
||||||
) -> String {
|
) -> String {
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import Dependencies
|
|||||||
import DependenciesMacros
|
import DependenciesMacros
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public extension DependencyValues {
|
extension DependencyValues {
|
||||||
|
|
||||||
/// Represents interactions with the `pandoc` command line application.
|
/// Represents interactions with the `pandoc` command line application.
|
||||||
///
|
///
|
||||||
/// The `pandoc` command line application is used to generate the final output
|
/// The `pandoc` command line application is used to generate the final output
|
||||||
/// documents from a home performance assessment project.
|
/// documents from a home performance assessment project.
|
||||||
///
|
///
|
||||||
var pandocClient: PandocClient {
|
public var pandocClient: PandocClient {
|
||||||
get { self[PandocClient.self] }
|
get { self[PandocClient.self] }
|
||||||
set { self[PandocClient.self] = newValue }
|
set { self[PandocClient.self] = newValue }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user