feat: Moves TrunkSize to be it's own namespace rather than being under DuctSizing, as it's got it's own database model, etc.

This commit is contained in:
2026-01-16 10:48:07 -05:00
parent b5436c2073
commit 146baa7815
9 changed files with 159 additions and 135 deletions

View File

@@ -74,7 +74,7 @@ extension DatabaseClient.Migrations: DependencyKey {
EquipmentInfo.Migrate(),
Room.Migrate(),
EffectiveLength.Migrate(),
DuctSizing.TrunkSize.Migrate(),
TrunkSize.Migrate(),
]
}
)

View File

@@ -7,13 +7,13 @@ import ManualDCore
extension DatabaseClient {
@DependencyClient
public struct TrunkSizes: Sendable {
public var create: @Sendable (DuctSizing.TrunkSize.Create) async throws -> DuctSizing.TrunkSize
public var delete: @Sendable (DuctSizing.TrunkSize.ID) async throws -> Void
public var fetch: @Sendable (Project.ID) async throws -> [DuctSizing.TrunkSize]
public var get: @Sendable (DuctSizing.TrunkSize.ID) async throws -> DuctSizing.TrunkSize?
public var create: @Sendable (TrunkSize.Create) async throws -> TrunkSize
public var delete: @Sendable (TrunkSize.ID) async throws -> Void
public var fetch: @Sendable (Project.ID) async throws -> [TrunkSize]
public var get: @Sendable (TrunkSize.ID) async throws -> TrunkSize?
public var update:
@Sendable (DuctSizing.TrunkSize.ID, DuctSizing.TrunkSize.Update) async throws ->
DuctSizing.TrunkSize
@Sendable (TrunkSize.ID, TrunkSize.Update) async throws ->
TrunkSize
}
}
@@ -26,7 +26,7 @@ extension DatabaseClient.TrunkSizes: TestDependencyKey {
try request.validate()
let trunk = request.toModel()
var roomProxies = [DuctSizing.TrunkSize.RoomProxy]()
var roomProxies = [TrunkSize.RoomProxy]()
try await trunk.save(on: database)
@@ -90,7 +90,7 @@ extension DatabaseClient.TrunkSizes: TestDependencyKey {
}
}
extension DuctSizing.TrunkSize.Create {
extension TrunkSize.Create {
func validate() throws(ValidationError) {
guard rooms.count > 0 else {
@@ -113,7 +113,7 @@ extension DuctSizing.TrunkSize.Create {
}
}
extension DuctSizing.TrunkSize.Update {
extension TrunkSize.Update {
func validate() throws(ValidationError) {
if let rooms {
guard rooms.count > 0 else {
@@ -128,7 +128,7 @@ extension DuctSizing.TrunkSize.Update {
}
}
extension DuctSizing.TrunkSize {
extension TrunkSize {
struct Migrate: AsyncMigration {
let name = "CreateTrunkSize"
@@ -192,7 +192,7 @@ final class TrunkRoomModel: Model, @unchecked Sendable {
trunkID: TrunkModel.IDValue,
roomID: RoomModel.IDValue,
registers: [Int],
type: DuctSizing.TrunkSize.TrunkType
type: TrunkSize.TrunkType
) {
self.id = id
$trunk.id = trunkID
@@ -201,7 +201,7 @@ final class TrunkRoomModel: Model, @unchecked Sendable {
self.type = type.rawValue
}
func toDTO(on database: any Database) async throws -> DuctSizing.TrunkSize.RoomProxy {
func toDTO(on database: any Database) async throws -> TrunkSize.RoomProxy {
guard let room = try await RoomModel.find($room.id, on: database) else {
throw NotFoundError()
}
@@ -240,7 +240,7 @@ final class TrunkModel: Model, @unchecked Sendable {
init(
id: UUID? = nil,
projectID: Project.ID,
type: DuctSizing.TrunkSize.TrunkType,
type: TrunkSize.TrunkType,
height: Int? = nil,
name: String? = nil
) {
@@ -251,15 +251,15 @@ final class TrunkModel: Model, @unchecked Sendable {
self.name = name
}
func toDTO(on database: any Database) async throws -> DuctSizing.TrunkSize {
let rooms = try await withThrowingTaskGroup(of: DuctSizing.TrunkSize.RoomProxy.self) { group in
func toDTO(on database: any Database) async throws -> TrunkSize {
let rooms = try await withThrowingTaskGroup(of: TrunkSize.RoomProxy.self) { group in
for room in self.rooms {
group.addTask {
try await room.toDTO(on: database)
}
}
return try await group.reduce(into: [DuctSizing.TrunkSize.RoomProxy]()) {
return try await group.reduce(into: [TrunkSize.RoomProxy]()) {
$0.append($1)
}
@@ -277,7 +277,7 @@ final class TrunkModel: Model, @unchecked Sendable {
}
func applyUpdates(
_ updates: DuctSizing.TrunkSize.Update,
_ updates: TrunkSize.Update,
on database: any Database
) async throws {
if let type = updates.type, type.rawValue != self.type {
@@ -340,15 +340,15 @@ final class TrunkModel: Model, @unchecked Sendable {
extension Array where Element == TrunkModel {
func toDTO(on database: any Database) async throws -> [DuctSizing.TrunkSize] {
try await withThrowingTaskGroup(of: DuctSizing.TrunkSize.self) { group in
func toDTO(on database: any Database) async throws -> [TrunkSize] {
try await withThrowingTaskGroup(of: TrunkSize.self) { group in
for model in self {
group.addTask {
try await model.toDTO(on: database)
}
}
return try await group.reduce(into: [DuctSizing.TrunkSize]()) {
return try await group.reduce(into: [TrunkSize]()) {
$0.append($1)
}
}

View File

@@ -14,7 +14,7 @@ extension Room {
}
}
extension DuctSizing.TrunkSize.RoomProxy {
extension TrunkSize.RoomProxy {
// We need to make sure if registers got removed after a trunk
// was already made / saved that we do not include registers that
@@ -35,7 +35,7 @@ extension DuctSizing.TrunkSize.RoomProxy {
}
}
extension DuctSizing.TrunkSize {
extension TrunkSize {
var totalHeatingLoad: Double {
rooms.reduce(into: 0) { $0 += $1.totalHeatingLoad }

View File

@@ -114,7 +114,7 @@ extension DuctSizing {
self.ductSize = ductSize
}
public subscript<T>(dynamicMember keyPath: KeyPath<DuctSizing.TrunkSize, T>) -> T {
public subscript<T>(dynamicMember keyPath: KeyPath<TrunkSize, T>) -> T {
trunk[keyPath: keyPath]
}
@@ -122,99 +122,4 @@ extension DuctSizing {
ductSize[keyPath: keyPath]
}
}
// TODO: Add an optional label that the user can set.
// Represents the database model.
public struct TrunkSize: Codable, Equatable, Identifiable, Sendable {
public let id: UUID
public let projectID: Project.ID
public let type: TrunkType
public let rooms: [RoomProxy]
public let height: Int?
public let name: String?
public init(
id: UUID,
projectID: Project.ID,
type: DuctSizing.TrunkSize.TrunkType,
rooms: [DuctSizing.TrunkSize.RoomProxy],
height: Int? = nil,
name: String? = nil
) {
self.id = id
self.projectID = projectID
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
}
extension DuctSizing.TrunkSize {
public struct Create: Codable, Equatable, Sendable {
public let projectID: Project.ID
public let type: TrunkType
public let rooms: [Room.ID: [Int]]
public let height: Int?
public let name: String?
public init(
projectID: Project.ID,
type: DuctSizing.TrunkSize.TrunkType,
rooms: [Room.ID: [Int]],
height: Int? = nil,
name: String? = nil
) {
self.projectID = projectID
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
public struct Update: Codable, Equatable, Sendable {
public let type: TrunkType?
public let rooms: [Room.ID: [Int]]?
public let height: Int?
public let name: String?
public init(
type: DuctSizing.TrunkSize.TrunkType? = nil,
rooms: [Room.ID: [Int]]? = nil,
height: Int? = nil,
name: String? = nil
) {
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
// TODO: Make registers non-optional
public struct RoomProxy: Codable, Equatable, Identifiable, Sendable {
public var id: Room.ID { room.id }
public let room: Room
public let registers: [Int]
public init(room: Room, registers: [Int]) {
self.room = room
self.registers = registers
}
}
public enum TrunkType: String, CaseIterable, Codable, Equatable, Sendable {
case `return`
case supply
public static let allCases = [Self.supply, .return]
}
}

View File

@@ -668,9 +668,9 @@ extension SiteRoute.View.ProjectRoute {
}
public enum TrunkRoute: Equatable, Sendable {
case delete(DuctSizing.TrunkSize.ID)
case delete(TrunkSize.ID)
case submit(TrunkSizeForm)
case update(DuctSizing.TrunkSize.ID, TrunkSizeForm)
case update(TrunkSize.ID, TrunkSizeForm)
public static let rootPath = "trunk"
@@ -678,7 +678,7 @@ extension SiteRoute.View.ProjectRoute {
Route(.case(Self.delete)) {
Path {
rootPath
DuctSizing.TrunkSize.ID.parser()
TrunkSize.ID.parser()
}
Method.delete
}
@@ -690,7 +690,7 @@ extension SiteRoute.View.ProjectRoute {
Body {
FormData {
Field("projectID") { Project.ID.parser() }
Field("type") { DuctSizing.TrunkSize.TrunkType.parser() }
Field("type") { TrunkSize.TrunkType.parser() }
Optionally {
Field("height") { Int.parser() }
@@ -708,13 +708,13 @@ extension SiteRoute.View.ProjectRoute {
Route(.case(Self.update)) {
Path {
rootPath
DuctSizing.TrunkSize.ID.parser()
TrunkSize.ID.parser()
}
Method.patch
Body {
FormData {
Field("projectID") { Project.ID.parser() }
Field("type") { DuctSizing.TrunkSize.TrunkType.parser() }
Field("type") { TrunkSize.TrunkType.parser() }
Optionally {
Field("height") { Int.parser() }
}
@@ -732,17 +732,43 @@ extension SiteRoute.View.ProjectRoute {
}
public struct RoomRectangularForm: Equatable, Sendable {
public let id: Room.RectangularSize.ID?
public let register: Int
public let height: Int
public init(
id: Room.RectangularSize.ID? = nil,
register: Int,
height: Int
) {
self.id = id
self.register = register
self.height = height
}
}
public struct TrunkSizeForm: Equatable, Sendable {
public let projectID: Project.ID
public let type: DuctSizing.TrunkSize.TrunkType
public let type: TrunkSize.TrunkType
public let height: Int?
public let name: String?
public let rooms: [String]
public init(
projectID: Project.ID,
type: TrunkSize.TrunkType,
height: Int? = nil,
name: String? = nil,
rooms: [String]
) {
self.projectID = projectID
self.type = type
self.height = height
self.name = name
self.rooms = rooms
}
}
}
}

View File

@@ -0,0 +1,93 @@
import Foundation
// Represents the database model.
public struct TrunkSize: Codable, Equatable, Identifiable, Sendable {
public let id: UUID
public let projectID: Project.ID
public let type: TrunkType
public let rooms: [RoomProxy]
public let height: Int?
public let name: String?
public init(
id: UUID,
projectID: Project.ID,
type: TrunkType,
rooms: [RoomProxy],
height: Int? = nil,
name: String? = nil
) {
self.id = id
self.projectID = projectID
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
extension TrunkSize {
public struct Create: Codable, Equatable, Sendable {
public let projectID: Project.ID
public let type: TrunkType
public let rooms: [Room.ID: [Int]]
public let height: Int?
public let name: String?
public init(
projectID: Project.ID,
type: TrunkType,
rooms: [Room.ID: [Int]],
height: Int? = nil,
name: String? = nil
) {
self.projectID = projectID
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
public struct Update: Codable, Equatable, Sendable {
public let type: TrunkType?
public let rooms: [Room.ID: [Int]]?
public let height: Int?
public let name: String?
public init(
type: TrunkType? = nil,
rooms: [Room.ID: [Int]]? = nil,
height: Int? = nil,
name: String? = nil
) {
self.type = type
self.rooms = rooms
self.height = height
self.name = name
}
}
// TODO: Make registers non-optional
public struct RoomProxy: Codable, Equatable, Identifiable, Sendable {
public var id: Room.ID { room.id }
public let room: Room
public let registers: [Int]
public init(room: Room, registers: [Int]) {
self.room = room
self.registers = registers
}
}
public enum TrunkType: String, CaseIterable, Codable, Equatable, Sendable {
case `return`
case supply
public static let allCases = [Self.supply, .return]
}
}

View File

@@ -16,7 +16,7 @@ extension ManualDClient {
func calculateDuctSizes(
rooms: [Room],
trunks: [DuctSizing.TrunkSize],
trunks: [TrunkSize],
sharedRequest: DuctSizeSharedRequest,
logger: Logger? = nil
) async throws -> ProjectClient.DuctSizeResponse {
@@ -93,7 +93,7 @@ extension ManualDClient {
func calculateTrunkSizes(
rooms: [Room],
trunks: [DuctSizing.TrunkSize],
trunks: [TrunkSize],
sharedRequest: DuctSizeSharedRequest,
logger: Logger? = nil
) async throws -> [DuctSizing.TrunkContainer] {
@@ -190,7 +190,7 @@ extension Room {
}
}
extension DuctSizing.TrunkSize.RoomProxy {
extension TrunkSize.RoomProxy {
// We need to make sure if registers got removed after a trunk
// was already made / saved that we do not include registers that
@@ -211,7 +211,7 @@ extension DuctSizing.TrunkSize.RoomProxy {
}
}
extension DuctSizing.TrunkSize {
extension TrunkSize {
var totalHeatingLoad: Double {
rooms.reduce(into: 0) { $0 += $1.totalHeatingLoad }

View File

@@ -4,7 +4,7 @@ import ManualDCore
extension SiteRoute.View.ProjectRoute.DuctSizingRoute.TrunkSizeForm {
func toCreate(logger: Logger? = nil) throws -> DuctSizing.TrunkSize.Create {
func toCreate(logger: Logger? = nil) throws -> TrunkSize.Create {
try .init(
projectID: projectID,
type: type,
@@ -14,7 +14,7 @@ extension SiteRoute.View.ProjectRoute.DuctSizingRoute.TrunkSizeForm {
)
}
func toUpdate(logger: Logger? = nil) throws -> DuctSizing.TrunkSize.Update {
func toUpdate(logger: Logger? = nil) throws -> TrunkSize.Update {
try .init(
type: type,
rooms: makeRooms(logger: logger),

View File

@@ -17,7 +17,7 @@ struct TrunkSizeForm: HTML, Sendable {
let rooms: [DuctSizing.RoomContainer]
let dismiss: Bool
var trunk: DuctSizing.TrunkSize? {
var trunk: TrunkSize? {
container?.trunk
}
@@ -56,7 +56,7 @@ struct TrunkSizeForm: HTML, Sendable {
label(.class("select w-full")) {
span(.class("label")) { "Type" }
select(.name("type")) {
for type in DuctSizing.TrunkSize.TrunkType.allCases {
for type in TrunkSize.TrunkType.allCases {
option(.value(type.rawValue)) { type.rawValue.capitalized }
.attributes(.selected, when: trunk?.type == type)
}
@@ -121,7 +121,7 @@ struct TrunkSizeForm: HTML, Sendable {
}
extension Array where Element == DuctSizing.TrunkSize.RoomProxy {
extension Array where Element == TrunkSize.RoomProxy {
func hasRoom(_ room: DuctSizing.RoomContainer) -> Bool {
first {
$0.id == room.roomID