feat: Begin using Tagged types
All checks were successful
CI / Linux Tests (push) Successful in 5m23s
All checks were successful
CI / Linux Tests (push) Successful in 5m23s
This commit is contained in:
@@ -31,7 +31,7 @@ struct DependenciesMiddleware: AsyncMiddleware {
|
||||
try await values.yield {
|
||||
try await withDependencies {
|
||||
$0.apiController = apiController
|
||||
$0.authClient = .live(on: request)
|
||||
$0.auth = .live(on: request)
|
||||
$0.database = database
|
||||
// $0.dateFormatter = .liveValue
|
||||
$0.viewController = viewController
|
||||
|
||||
@@ -5,17 +5,23 @@ import ManualDCore
|
||||
import Vapor
|
||||
|
||||
extension DependencyValues {
|
||||
public var authClient: AuthClient {
|
||||
/// Authentication dependency, for handling authentication tasks.
|
||||
public var auth: AuthClient {
|
||||
get { self[AuthClient.self] }
|
||||
set { self[AuthClient.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents authentication tasks that are used in the application.
|
||||
@DependencyClient
|
||||
public struct AuthClient: Sendable {
|
||||
/// Create a new user and log them in.
|
||||
public var createAndLogin: @Sendable (User.Create) async throws -> User
|
||||
/// Get the current user.
|
||||
public var currentUser: @Sendable () throws -> User
|
||||
/// Login a user.
|
||||
public var login: @Sendable (User.Login) async throws -> User
|
||||
/// Logout a user.
|
||||
public var logout: @Sendable () throws -> Void
|
||||
}
|
||||
|
||||
|
||||
@@ -4,22 +4,33 @@ import FluentKit
|
||||
import ManualDCore
|
||||
|
||||
extension DependencyValues {
|
||||
/// The database dependency.
|
||||
public var database: DatabaseClient {
|
||||
get { self[DatabaseClient.self] }
|
||||
set { self[DatabaseClient.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the database interactions used by the application.
|
||||
@DependencyClient
|
||||
public struct DatabaseClient: Sendable {
|
||||
/// Database migrations.
|
||||
public var migrations: Migrations
|
||||
/// Interactions with the projects table.
|
||||
public var projects: Projects
|
||||
/// Interactions with the rooms table.
|
||||
public var rooms: Rooms
|
||||
/// Interactions with the equipment table.
|
||||
public var equipment: Equipment
|
||||
/// Interactions with the component losses table.
|
||||
public var componentLosses: ComponentLosses
|
||||
/// Interactions with the equivalent lengths table.
|
||||
public var equivalentLengths: EquivalentLengths
|
||||
/// Interactions with the users table.
|
||||
public var users: Users
|
||||
/// Interactions with the user profiles table.
|
||||
public var userProfiles: UserProfiles
|
||||
/// Interactions with the trunk sizes table.
|
||||
public var trunkSizes: TrunkSizes
|
||||
|
||||
@DependencyClient
|
||||
@@ -147,23 +158,3 @@ extension DatabaseClient: TestDependencyKey {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension DatabaseClient.Migrations: DependencyKey {
|
||||
public static let testValue = Self()
|
||||
|
||||
public static let liveValue = Self(
|
||||
all: {
|
||||
[
|
||||
Project.Migrate(),
|
||||
User.Migrate(),
|
||||
User.Token.Migrate(),
|
||||
User.Profile.Migrate(),
|
||||
ComponentPressureLoss.Migrate(),
|
||||
EquipmentInfo.Migrate(),
|
||||
Room.Migrate(),
|
||||
EquivalentLength.Migrate(),
|
||||
TrunkSize.Migrate(),
|
||||
]
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
22
Sources/DatabaseClient/Internal/Migrations.swift
Normal file
22
Sources/DatabaseClient/Internal/Migrations.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
import Dependencies
|
||||
import ManualDCore
|
||||
|
||||
extension DatabaseClient.Migrations: DependencyKey {
|
||||
public static let testValue = Self()
|
||||
|
||||
public static let liveValue = Self(
|
||||
all: {
|
||||
[
|
||||
Project.Migrate(),
|
||||
User.Migrate(),
|
||||
User.Token.Migrate(),
|
||||
User.Profile.Migrate(),
|
||||
ComponentPressureLoss.Migrate(),
|
||||
EquipmentInfo.Migrate(),
|
||||
Room.Migrate(),
|
||||
EquivalentLength.Migrate(),
|
||||
TrunkSize.Migrate(),
|
||||
]
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import Foundation
|
||||
import Vapor
|
||||
|
||||
extension DependencyValues {
|
||||
/// Dependency used for file operations.
|
||||
public var fileClient: FileClient {
|
||||
get { self[FileClient.self] }
|
||||
set { self[FileClient.self] = newValue }
|
||||
@@ -14,9 +15,27 @@ extension DependencyValues {
|
||||
public struct FileClient: Sendable {
|
||||
public typealias OnCompleteHandler = @Sendable () async throws -> Void
|
||||
|
||||
public var writeFile: @Sendable (String, String) async throws -> Void
|
||||
public var removeFile: @Sendable (String) async throws -> Void
|
||||
public var streamFile: @Sendable (String, @escaping OnCompleteHandler) async throws -> Response
|
||||
/// Write contents to a file.
|
||||
///
|
||||
/// > Warning: This will overwrite a file if it exists.
|
||||
public var writeFile: @Sendable (_ contents: String, _ path: String) async throws -> Void
|
||||
/// Remove a file.
|
||||
public var removeFile: @Sendable (_ path: String) async throws -> Void
|
||||
/// Stream a file.
|
||||
public var streamFile:
|
||||
@Sendable (_ path: String, @escaping OnCompleteHandler) async throws -> Response
|
||||
|
||||
/// Stream a file at the given path.
|
||||
///
|
||||
/// - Paramters:
|
||||
/// - path: The path to the file to stream.
|
||||
/// - onComplete: Completion handler to run when done streaming the file.
|
||||
public func streamFile(
|
||||
at path: String,
|
||||
onComplete: @escaping OnCompleteHandler = {}
|
||||
) async throws -> Response {
|
||||
try await streamFile(path, onComplete)
|
||||
}
|
||||
}
|
||||
|
||||
extension FileClient: TestDependencyKey {
|
||||
|
||||
@@ -97,16 +97,16 @@ func roundSize(_ size: Double) throws -> Int {
|
||||
}
|
||||
}
|
||||
|
||||
func velocity(cfm: Int, roundSize: Int) -> Int {
|
||||
let cfm = Double(cfm)
|
||||
func velocity(cfm: CFM, roundSize: Int) -> Int {
|
||||
let cfm = Double(cfm.rawValue)
|
||||
let roundSize = Double(roundSize)
|
||||
let velocity = cfm / (pow(roundSize / 24, 2) * 3.14)
|
||||
return Int(round(velocity))
|
||||
}
|
||||
|
||||
func flexSize(_ request: ManualDClient.DuctSizeRequest) throws -> Int {
|
||||
let cfm = pow(Double(request.designCFM), 0.4)
|
||||
let fr = pow(request.frictionRate / 1.76, 0.2)
|
||||
func flexSize(_ cfm: CFM, _ frictionRate: DesignFrictionRate) throws -> Int {
|
||||
let cfm = pow(Double(cfm.rawValue), 0.4)
|
||||
let fr = pow(frictionRate.rawValue / 1.76, 0.2)
|
||||
let size = 0.55 * (cfm / fr)
|
||||
return try roundSize(size)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import Logging
|
||||
import ManualDCore
|
||||
|
||||
extension DependencyValues {
|
||||
/// Dependency that performs manual-d duct sizing calculations.
|
||||
public var manualD: ManualDClient {
|
||||
get { self[ManualDClient.self] }
|
||||
set { self[ManualDClient.self] = newValue }
|
||||
@@ -15,12 +16,24 @@ extension DependencyValues {
|
||||
///
|
||||
@DependencyClient
|
||||
public struct ManualDClient: Sendable {
|
||||
public var ductSize: @Sendable (DuctSizeRequest) async throws -> DuctSizeResponse
|
||||
public var ductSize: @Sendable (CFM, DesignFrictionRate) async throws -> DuctSizeResponse
|
||||
public var frictionRate: @Sendable (FrictionRateRequest) async throws -> FrictionRate
|
||||
public var totalEquivalentLength: @Sendable (TotalEquivalentLengthRequest) async throws -> Int
|
||||
public var rectangularSize:
|
||||
@Sendable (RectangularSizeRequest) async throws -> RectangularSizeResponse
|
||||
|
||||
public func ductSize(
|
||||
cfm designCFM: Int,
|
||||
frictionRate designFrictionRate: Double
|
||||
) async throws -> DuctSizeResponse {
|
||||
try await ductSize(.init(rawValue: designCFM), .init(rawValue: designFrictionRate))
|
||||
}
|
||||
|
||||
public func ductSize(
|
||||
cfm designCFM: Double,
|
||||
frictionRate designFrictionRate: Double
|
||||
) async throws -> DuctSizeResponse {
|
||||
try await ductSize(.init(rawValue: Int(designCFM)), .init(rawValue: designFrictionRate))
|
||||
}
|
||||
}
|
||||
|
||||
extension ManualDClient: TestDependencyKey {
|
||||
@@ -29,19 +42,6 @@ extension ManualDClient: TestDependencyKey {
|
||||
|
||||
extension ManualDClient {
|
||||
|
||||
public struct DuctSizeRequest: Codable, Equatable, Sendable {
|
||||
public let designCFM: Int
|
||||
public let frictionRate: Double
|
||||
|
||||
public init(
|
||||
designCFM: Int,
|
||||
frictionRate: Double
|
||||
) {
|
||||
self.designCFM = designCFM
|
||||
self.frictionRate = frictionRate
|
||||
}
|
||||
}
|
||||
|
||||
public struct DuctSizeResponse: Codable, Equatable, Sendable {
|
||||
|
||||
public let calculatedSize: Double
|
||||
@@ -82,31 +82,14 @@ extension ManualDClient {
|
||||
public struct FrictionRateResponse: Codable, Equatable, Sendable {
|
||||
|
||||
public let availableStaticPressure: Double
|
||||
public let frictionRate: Double
|
||||
public let frictionRate: DesignFrictionRate
|
||||
|
||||
public init(availableStaticPressure: Double, frictionRate: Double) {
|
||||
public init(availableStaticPressure: Double, frictionRate: DesignFrictionRate) {
|
||||
self.availableStaticPressure = availableStaticPressure
|
||||
self.frictionRate = frictionRate
|
||||
}
|
||||
}
|
||||
|
||||
public struct TotalEquivalentLengthRequest: 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
|
||||
}
|
||||
}
|
||||
|
||||
public struct RectangularSizeRequest: Codable, Equatable, Sendable {
|
||||
public let roundSize: Int
|
||||
public let height: Int
|
||||
|
||||
@@ -4,19 +4,19 @@ import ManualDCore
|
||||
|
||||
extension ManualDClient: DependencyKey {
|
||||
public static let liveValue: Self = .init(
|
||||
ductSize: { request in
|
||||
guard request.designCFM > 0 else {
|
||||
ductSize: { cfm, frictionRate in
|
||||
guard cfm > 0 else {
|
||||
throw ManualDError(message: "Design CFM should be greater than 0.")
|
||||
}
|
||||
let fr = pow(request.frictionRate, 0.5)
|
||||
let ductulatorSize = pow(Double(request.designCFM) / (3.12 * fr), 0.38)
|
||||
let fr = pow(frictionRate.rawValue, 0.5)
|
||||
let ductulatorSize = pow(Double(cfm.rawValue) / (3.12 * fr), 0.38)
|
||||
let finalSize = try roundSize(ductulatorSize)
|
||||
let flexSize = try flexSize(request)
|
||||
let flexSize = try flexSize(cfm, frictionRate)
|
||||
return .init(
|
||||
calculatedSize: ductulatorSize,
|
||||
finalSize: finalSize,
|
||||
flexSize: flexSize,
|
||||
velocity: velocity(cfm: request.designCFM, roundSize: finalSize)
|
||||
velocity: velocity(cfm: cfm, roundSize: finalSize)
|
||||
)
|
||||
},
|
||||
frictionRate: { request in
|
||||
@@ -28,14 +28,17 @@ extension ManualDClient: DependencyKey {
|
||||
let totalComponentLosses = request.componentPressureLosses.total
|
||||
let availableStaticPressure = request.externalStaticPressure - totalComponentLosses
|
||||
let frictionRate = availableStaticPressure * 100.0 / Double(request.totalEffectiveLength)
|
||||
return .init(availableStaticPressure: availableStaticPressure, value: frictionRate)
|
||||
},
|
||||
totalEquivalentLength: { 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
|
||||
return .init(
|
||||
availableStaticPressure: availableStaticPressure,
|
||||
value: .init(rawValue: frictionRate)
|
||||
)
|
||||
},
|
||||
// totalEquivalentLength: { 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
|
||||
// },
|
||||
rectangularSize: { request in
|
||||
let width = (Double.pi * (pow(Double(request.roundSize) / 2.0, 2.0))) / Double(request.height)
|
||||
return .init(height: request.height, width: Int(width.rounded(.toNearestOrEven)))
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
import Foundation
|
||||
import Tagged
|
||||
|
||||
extension Tagged where RawValue == Double {
|
||||
public func string(digits: Int = 2) -> String {
|
||||
rawValue.string(digits: digits)
|
||||
}
|
||||
}
|
||||
|
||||
extension Tagged where RawValue == Int {
|
||||
public func string() -> String {
|
||||
rawValue.string()
|
||||
}
|
||||
}
|
||||
|
||||
extension Double {
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@ public struct FrictionRate: Codable, Equatable, Sendable {
|
||||
/// minus the ``ComponentPressureLoss``es for the project.
|
||||
public let availableStaticPressure: Double
|
||||
/// The calculated design friction rate value.
|
||||
public let value: Double
|
||||
public let value: DesignFrictionRate
|
||||
/// Whether the design friction rate is within a valid range.
|
||||
public var hasErrors: Bool { error != nil }
|
||||
|
||||
public init(
|
||||
availableStaticPressure: Double,
|
||||
value: Double
|
||||
value: DesignFrictionRate
|
||||
) {
|
||||
self.availableStaticPressure = availableStaticPressure
|
||||
self.value = value
|
||||
|
||||
7
Sources/ManualDCore/TaggedTypes.swift
Normal file
7
Sources/ManualDCore/TaggedTypes.swift
Normal file
@@ -0,0 +1,7 @@
|
||||
import Tagged
|
||||
|
||||
public enum CFMTag {}
|
||||
public typealias CFM = Tagged<CFMTag, Int>
|
||||
|
||||
public enum DesignFrictionRateTag {}
|
||||
public typealias DesignFrictionRate = Tagged<DesignFrictionRateTag, Double>
|
||||
@@ -52,7 +52,8 @@ extension ManualDClient {
|
||||
let coolingCFM = coolingPercent * Double(sharedRequest.equipmentInfo.coolingCFM)
|
||||
let designCFM = DuctSizes.DesignCFM(heating: heatingCFM, cooling: coolingCFM)
|
||||
let sizes = try await self.ductSize(
|
||||
.init(designCFM: Int(designCFM.value), frictionRate: sharedRequest.designFrictionRate)
|
||||
cfm: designCFM.value,
|
||||
frictionRate: sharedRequest.designFrictionRate
|
||||
)
|
||||
|
||||
for n in 1...room.registerCount {
|
||||
@@ -111,7 +112,8 @@ extension ManualDClient {
|
||||
let coolingCFM = coolingPercent * Double(sharedRequest.equipmentInfo.coolingCFM)
|
||||
let designCFM = DuctSizes.DesignCFM(heating: heatingCFM, cooling: coolingCFM)
|
||||
let sizes = try await self.ductSize(
|
||||
.init(designCFM: Int(designCFM.value), frictionRate: sharedRequest.designFrictionRate)
|
||||
cfm: designCFM.value,
|
||||
frictionRate: sharedRequest.designFrictionRate
|
||||
)
|
||||
var width: Int? = nil
|
||||
if let height = trunk.height {
|
||||
|
||||
@@ -42,13 +42,10 @@ extension ProjectClient: DependencyKey {
|
||||
request: database.makePdfRequest(projectID)
|
||||
)
|
||||
|
||||
let response = try await fileClient.streamFile(
|
||||
pdfResponse.pdfPath,
|
||||
{
|
||||
try await fileClient.removeFile(pdfResponse.htmlPath)
|
||||
try await fileClient.removeFile(pdfResponse.pdfPath)
|
||||
}
|
||||
)
|
||||
let response = try await fileClient.streamFile(at: pdfResponse.pdfPath) {
|
||||
try await fileClient.removeFile(pdfResponse.htmlPath)
|
||||
try await fileClient.removeFile(pdfResponse.pdfPath)
|
||||
}
|
||||
|
||||
response.headers.replaceOrAdd(name: .contentType, value: "application/octet-stream")
|
||||
response.headers.replaceOrAdd(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Elementary
|
||||
import Tagged
|
||||
|
||||
public struct Badge<Inner: HTML>: HTML, Sendable where Inner: Sendable {
|
||||
|
||||
@@ -25,4 +26,12 @@ extension Badge where Inner == Number {
|
||||
public init(number: Double, digits: Int = 2) {
|
||||
self.inner = Number(number, digits: digits)
|
||||
}
|
||||
|
||||
public init<T>(number: Tagged<T, Int>) {
|
||||
self.inner = Number(number.rawValue)
|
||||
}
|
||||
|
||||
public init<T>(number: Tagged<T, Double>, digits: Int = 2) {
|
||||
self.inner = Number(number.rawValue, digits: digits)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,14 +54,14 @@ extension ViewController: DependencyKey {
|
||||
extension ViewController.Request {
|
||||
|
||||
func currentUser() throws -> User {
|
||||
@Dependency(\.authClient.currentUser) var currentUser
|
||||
@Dependency(\.auth.currentUser) var currentUser
|
||||
return try currentUser()
|
||||
}
|
||||
|
||||
func authenticate(
|
||||
_ login: User.Login
|
||||
) async throws -> User {
|
||||
@Dependency(\.authClient) var auth
|
||||
@Dependency(\.auth) var auth
|
||||
return try await auth.login(login)
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ extension ViewController.Request {
|
||||
func createAndAuthenticate(
|
||||
_ signup: User.Create
|
||||
) async throws -> User {
|
||||
@Dependency(\.authClient) var auth
|
||||
@Dependency(\.auth) var auth
|
||||
return try await auth.createAndLogin(signup)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user