diff --git a/Package.swift b/Package.swift index 549d483..9c49dbf 100644 --- a/Package.swift +++ b/Package.swift @@ -29,7 +29,10 @@ let package = Package( ), .testTarget( name: "ManualDClientTests", - dependencies: ["ManualDClient"] + dependencies: [ + "ManualDClient", + .product(name: "DependenciesTestSupport", package: "swift-dependencies"), + ] ), .testTarget( name: "swift-manual-dTests", diff --git a/Sources/ManualDClient/Helpers.swift b/Sources/ManualDClient/Helpers.swift index 0cacd78..51f1c7d 100644 --- a/Sources/ManualDClient/Helpers.swift +++ b/Sources/ManualDClient/Helpers.swift @@ -4,3 +4,9 @@ import ManualDCore extension ComponentPressureLosses { var totalLosses: Double { values.reduce(0) { $0 + $1 } } } + +extension Array where Element == EffectiveLengthGroup { + var totalEffectiveLength: Int { + reduce(0) { $0 + $1.effectiveLength } + } +} diff --git a/Sources/ManualDClient/Live.swift b/Sources/ManualDClient/Live.swift index c070bd0..81c089a 100644 --- a/Sources/ManualDClient/Live.swift +++ b/Sources/ManualDClient/Live.swift @@ -1,4 +1,5 @@ import Dependencies +import Foundation import ManualDCore extension ManualDClient: DependencyKey { @@ -13,6 +14,31 @@ extension ManualDClient: DependencyKey { let availableStaticPressure = request.externalStaticPressure - totalComponentLosses let frictionRate = availableStaticPressure * 100.0 / Double(request.totalEffectiveLength) return .init(availableStaticPressure: availableStaticPressure, frictionRate: frictionRate) + }, + totalEffectiveLength: { request in + let trunkLengths = request.trunkLengths.reduce(0) { $0 + $1 } + let runoutLengths = request.runoutLengths.reduce(0) { $0 + $1 } + let groupLengths = request.effectiveLengthGroups.totalEffectiveLength + return trunkLengths + runoutLengths + groupLengths + }, + equivalentRectangularDuct: { request in + let width = (Double.pi * (pow(Double(request.roundSize) / 2.0, 2.0))) / Double(request.height) + guard let widthStr = numberFormatter.string(for: width), + let widthInt = Int(widthStr) + else { + throw ManualDError( + message: "Failed to convert to to rectangular duct size, width: \(width)" + ) + } + return .init(height: request.height, width: widthInt) } ) } + +private let numberFormatter: NumberFormatter = { + let formatter = NumberFormatter() + formatter.maximumFractionDigits = 0 + formatter.minimumFractionDigits = 0 + formatter.roundingMode = .ceiling + return formatter +}() diff --git a/Sources/ManualDClient/ManualDClient.swift b/Sources/ManualDClient/ManualDClient.swift index 3c6ddf3..3ec5106 100644 --- a/Sources/ManualDClient/ManualDClient.swift +++ b/Sources/ManualDClient/ManualDClient.swift @@ -5,6 +5,9 @@ import ManualDCore @DependencyClient public struct ManualDClient: Sendable { public var frictionRate: @Sendable (FrictionRateRequest) async throws -> FrictionRateResponse + public var totalEffectiveLength: @Sendable (TotalEffectiveLengthRequest) async throws -> Int + public var equivalentRectangularDuct: + @Sendable (EquivalentRectangularDuctRequest) async throws -> EquivalentRectangularDuctResponse } extension ManualDClient: TestDependencyKey { @@ -48,3 +51,46 @@ extension ManualDClient { } } } + +// MARK: Total Effective Length +extension ManualDClient { + public struct TotalEffectiveLengthRequest: Codable, Equatable, Sendable { + + public let trunkLengths: [Int] + public let runoutLengths: [Int] + public let effectiveLengthGroups: [EffectiveLengthGroup] + + public init( + trunkLengths: [Int], + runoutLengths: [Int], + effectiveLengthGroups: [EffectiveLengthGroup] + ) { + self.trunkLengths = trunkLengths + self.runoutLengths = runoutLengths + self.effectiveLengthGroups = effectiveLengthGroups + } + } +} + +// MARK: Equivalent Rectangular Duct +extension ManualDClient { + public struct EquivalentRectangularDuctRequest: Codable, Equatable, Sendable { + public let roundSize: Int + public let height: Int + + public init(round roundSize: Int, height: Int) { + self.roundSize = roundSize + self.height = height + } + } + + public struct EquivalentRectangularDuctResponse: Codable, Equatable, Sendable { + public let height: Int + public let width: Int + + public init(height: Int, width: Int) { + self.height = height + self.width = width + } + } +} diff --git a/Tests/ManualDClientTests/ManualDClientTests.swift b/Tests/ManualDClientTests/ManualDClientTests.swift index dc9db5a..402281b 100644 --- a/Tests/ManualDClientTests/ManualDClientTests.swift +++ b/Tests/ManualDClientTests/ManualDClientTests.swift @@ -1,12 +1,19 @@ import Dependencies +import DependenciesTestSupport import Foundation import ManualDClient import ManualDCore import Testing -@Suite("ManualDClient Tests") +@Suite( + .dependencies { + $0.manualD = ManualDClient.liveValue + } +) struct ManualDClientTests { + @Dependency(\.manualD) var manualD + var numberFormatter: NumberFormatter { let formatter = NumberFormatter() formatter.minimumFractionDigits = 2 @@ -17,7 +24,6 @@ struct ManualDClientTests { @Test func frictionRate() async throws { - let manualD = ManualDClient.liveValue let response = try await manualD.frictionRate( .init( externalStaticPressure: 0.5, @@ -32,7 +38,6 @@ struct ManualDClientTests { @Test func frictionRateFails() async throws { await #expect(throws: ManualDError.self) { - let manualD = ManualDClient.liveValue _ = try await manualD.frictionRate( .init( externalStaticPressure: 0.5, @@ -42,4 +47,29 @@ struct ManualDClientTests { ) } } + + @Test + func totalEffectiveLength() async throws { + let response = try await manualD.totalEffectiveLength( + .init( + trunkLengths: [25], + runoutLengths: [10], + effectiveLengthGroups: [ + // NOTE: These are made up and may not correspond to actual manual-d group tel's. + EffectiveLengthGroup(group: 1, letter: "a", effectiveLength: 20, category: .supply), + EffectiveLengthGroup(group: 2, letter: "a", effectiveLength: 30, category: .supply), + EffectiveLengthGroup(group: 3, letter: "a", effectiveLength: 10, category: .supply), + EffectiveLengthGroup(group: 12, letter: "a", effectiveLength: 10, category: .supply), + ] + ) + ) + #expect(response == 105) + } + + @Test + func equivalentRectangularDuct() async throws { + let response = try await manualD.equivalentRectangularDuct(.init(round: 7, height: 8)) + #expect(response.height == 8) + #expect(response.width == 5) + } }