diff --git a/Package.swift b/Package.swift index 8e01c5d..1f083db 100644 --- a/Package.swift +++ b/Package.swift @@ -9,6 +9,7 @@ let package = Package( .library(name: "ApiController", targets: ["ApiController"]), .library(name: "AuthClient", targets: ["AuthClient"]), .library(name: "DatabaseClient", targets: ["DatabaseClient"]), + .library(name: "EnvClient", targets: ["EnvClient"]), .library(name: "FileClient", targets: ["FileClient"]), .library(name: "HTMLSnapshotTesting", targets: ["HTMLSnapshotTesting"]), .library(name: "PdfClient", targets: ["PdfClient"]), @@ -79,6 +80,14 @@ let package = Package( .product(name: "Vapor", package: "vapor"), ] ), + + .target( + name: "EnvClient", + dependencies: [ + .product(name: "Dependencies", package: "swift-dependencies"), + .product(name: "DependenciesMacros", package: "swift-dependencies"), + ] + ), .target( name: "FileClient", dependencies: [ diff --git a/Sources/EnvClient/Interface.swift b/Sources/EnvClient/Interface.swift new file mode 100644 index 0000000..699e090 --- /dev/null +++ b/Sources/EnvClient/Interface.swift @@ -0,0 +1,67 @@ +import Dependencies +import DependenciesMacros +import Foundation + +extension DependencyValues { + + /// Holds values defined in the process environment that are needed. + /// + /// These are generally loaded from a `.env` file, but also have default values, + /// if not found. + public var env: @Sendable () throws -> EnvVars { + get { self[EnvClient.self].env } + set { self[EnvClient.self].env = newValue } + } +} + +@DependencyClient +struct EnvClient: Sendable { + + public var env: @Sendable () throws -> EnvVars +} + +public struct EnvVars: Codable, Equatable, Sendable { + + public let pandocPath: String + public let pdfEngine: String + + public init( + pandocPath: String = "/bin/pandoc", + pdfEngine: String = "weasyprint" + ) { + self.pandocPath = pandocPath + self.pdfEngine = pdfEngine + } + + enum CodingKeys: String, CodingKey { + case pandocPath = "PANDOC_PATH" + case pdfEngine = "PDF_ENGINE" + } + +} + +extension EnvClient: DependencyKey { + static let testValue = Self() + static let liveValue = Self(env: { + // Convert default values into a dictionary. + let defaults = + (try? encoder.encode(EnvVars())) + .flatMap { try? decoder.decode([String: String].self, from: $0) } + ?? [:] + + // Merge the default values with values found in process environment. + let assigned = defaults.merging(ProcessInfo.processInfo.environment, uniquingKeysWith: { $1 }) + + return (try? JSONSerialization.data(withJSONObject: assigned)) + .flatMap { try? decoder.decode(EnvVars.self, from: $0) } + ?? .init() + }) +} + +private let encoder: JSONEncoder = { + JSONEncoder() +}() + +private let decoder: JSONDecoder = { + JSONDecoder() +}() diff --git a/Sources/PdfClient/Interface.swift b/Sources/PdfClient/Interface.swift index 3ca398e..2e95dc6 100644 --- a/Sources/PdfClient/Interface.swift +++ b/Sources/PdfClient/Interface.swift @@ -14,7 +14,6 @@ extension DependencyValues { @DependencyClient public struct PdfClient: Sendable { public var html: @Sendable (Request) async throws -> (any HTML & Sendable) - public var markdown: @Sendable (Request) async throws -> String } extension PdfClient: DependencyKey { @@ -23,9 +22,6 @@ extension PdfClient: DependencyKey { public static let liveValue = Self( html: { request in request.toHTML() - }, - markdown: { request in - request.toMarkdown() } ) } diff --git a/Sources/PdfClient/Request+markdown.swift b/Sources/PdfClient/Request+markdown.swift deleted file mode 100644 index 00f9a11..0000000 --- a/Sources/PdfClient/Request+markdown.swift +++ /dev/null @@ -1,113 +0,0 @@ -import Foundation -import ManualDCore - -extension PdfClient.Request { - - func toMarkdown() -> String { - var retval = """ - # Duct Calc - - **Name:** \(project.name) - **Address:** \(project.streetAddress) - \(project.city), \(project.state) \(project.zipCode) - - ## Equipment - - | | Value | - |:----------------|:--------------------------------| - | Static Pressure | \(equipmentInfo.staticPressure.string()) | - | Heating CFM | \(equipmentInfo.heatingCFM.string()) | - | Cooling CFM | \(equipmentInfo.coolingCFM.string()) | - - ## Friction Rate - - | Component Loss | Value | - |:----------------|:--------------------------------| - - """ - for row in componentLosses { - retval += "\(componentLossRow(row))\n" - } - - retval += """ - - - | Results | Value | - |:-----------------|:---------------------------------| - | Available Static Pressure | \(frictionRate.availableStaticPressure.string()) | - | Total Equivalent Length | \(totalEquivalentLength.string()) | - | Friction Rate Design Value | \(frictionRate.value.string()) | - - ## Duct Sizes - - | Register | Dsn CFM | Round Size | Velocity | Final Size | Flex Size | Height | Width | - |:---------|:--------|:----------------|:---------|:-----------|:----------|:-------|:------| - - """ - for row in ductSizes.rooms { - retval += "\(registerRow(row))\n" - } - - retval += """ - - ## Trunk Sizes - - ### Supply Trunks - - | Name | Associated Supplies | Dsn CFM | Velocity | Final Size | Flex Size | Height | Width | - |:---------|:--------------------|:--------|:---------|:-----------|:----------|:-------|:------| - - """ - for row in ductSizes.trunks.filter({ $0.type == .supply }) { - retval += "\(trunkRow(row))\n" - } - - retval += """ - - ### Return Trunks / Run Outs - - | Name | Associated Supplies | Dsn CFM | Velocity | Final Size | Flex Size | Height | Width | - |:---------|:--------------------|:--------|:---------|:-----------|:----------|:-------|:------| - - """ - for row in ductSizes.trunks.filter({ $0.type == .return }) { - retval += "\(trunkRow(row))\n" - } - - return retval - } - - func registerRow(_ row: DuctSizes.RoomContainer) -> String { - return """ - | \(row.roomName) | \(row.designCFM.value.string(digits: 0)) | \(row.roundSize.string()) | \(row.velocity.string()) | \(row.finalSize.string()) | \(row.flexSize.string()) | \(row.height?.string() ?? "") | \(row.width?.string() ?? "") | - """ - } - - func trunkRow(_ row: DuctSizes.TrunkContainer) -> String { - return """ - | \(row.name ?? "") | \(associatedSupplyString(row)) | \(row.designCFM.value.string(digits: 0)) | \(row.roundSize.string()) | \(row.velocity.string()) | \(row.finalSize.string()) | \(row.flexSize.string()) | \(row.ductSize.height?.string() ?? "") | \(row.width?.string() ?? "") | - """ - } - - func componentLossRow(_ row: ComponentPressureLoss) -> String { - return """ - | \(row.name) | \(row.value.string()) | - """ - } - - var totalEquivalentLength: Double { - maxSupplyTEL.totalEquivalentLength + maxReturnTEL.totalEquivalentLength - } - - func associatedSupplyString(_ row: DuctSizes.TrunkContainer) -> String { - row.associatedSupplyString(rooms: ductSizes.rooms) - } -} - -extension DuctSizes.TrunkContainer { - - func associatedSupplyString(rooms: [DuctSizes.RoomContainer]) -> String { - self.registerIDS(rooms: rooms) - .joined(separator: ", ") - } -}