This repository has been archived on 2026-02-12. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

282 lines
7.9 KiB
Swift

import Dependencies
import DependenciesMacros
import Fluent
import Foundation
import ManualDCore
extension DatabaseClient {
@DependencyClient
public struct Rooms: Sendable {
public var create: @Sendable (Room.Create) async throws -> Room
public var delete: @Sendable (Room.ID) async throws -> Void
public var deleteRectangularSize:
@Sendable (Room.ID, Room.RectangularSize.ID) async throws -> Room
public var get: @Sendable (Room.ID) async throws -> Room?
public var fetch: @Sendable (Project.ID) async throws -> [Room]
public var update: @Sendable (Room.ID, Room.Update) async throws -> Room
public var updateRectangularSize: @Sendable (Room.ID, Room.RectangularSize) async throws -> Room
}
}
extension DatabaseClient.Rooms: TestDependencyKey {
public static let testValue = Self()
public static func live(database: any Database) -> Self {
.init(
create: { request in
let model = try request.toModel()
try await model.save(on: database)
return try model.toDTO()
},
delete: { id in
guard let model = try await RoomModel.find(id, on: database) else {
throw NotFoundError()
}
try await model.delete(on: database)
},
deleteRectangularSize: { roomID, rectangularDuctID in
guard let model = try await RoomModel.find(roomID, on: database) else {
throw NotFoundError()
}
model.rectangularSizes?.removeAll {
$0.id == rectangularDuctID
}
if model.hasChanges {
try await model.save(on: database)
}
return try model.toDTO()
},
get: { id in
try await RoomModel.find(id, on: database).map { try $0.toDTO() }
},
fetch: { projectID in
try await RoomModel.query(on: database)
.with(\.$project)
.filter(\.$project.$id, .equal, projectID)
.sort(\.$name, .ascending)
.all()
.map { try $0.toDTO() }
},
update: { id, updates in
guard let model = try await RoomModel.find(id, on: database) else {
throw NotFoundError()
}
try updates.validate()
model.applyUpdates(updates)
if model.hasChanges {
try await model.save(on: database)
}
return try model.toDTO()
},
updateRectangularSize: { id, size in
guard let model = try await RoomModel.find(id, on: database) else {
throw NotFoundError()
}
var rectangularSizes = model.rectangularSizes ?? []
rectangularSizes.removeAll {
$0.id == size.id
}
rectangularSizes.append(size)
model.rectangularSizes = rectangularSizes
try await model.save(on: database)
return try model.toDTO()
}
)
}
}
extension Room.Create {
func toModel() throws(ValidationError) -> RoomModel {
try validate()
return .init(
name: name,
heatingLoad: heatingLoad,
coolingTotal: coolingTotal,
coolingSensible: coolingSensible,
registerCount: registerCount,
projectID: projectID
)
}
func validate() throws(ValidationError) {
guard !name.isEmpty else {
throw ValidationError("Room name should not be empty.")
}
guard heatingLoad >= 0 else {
throw ValidationError("Room heating load should not be less than 0.")
}
guard coolingTotal >= 0 else {
throw ValidationError("Room cooling total should not be less than 0.")
}
if let coolingSensible {
guard coolingSensible >= 0 else {
throw ValidationError("Room cooling sensible should not be less than 0.")
}
}
guard registerCount >= 1 else {
throw ValidationError("Room cooling sensible should not be less than 1.")
}
}
}
extension Room.Update {
func validate() throws(ValidationError) {
if let name {
guard !name.isEmpty else {
throw ValidationError("Room name should not be empty.")
}
}
if let heatingLoad {
guard heatingLoad >= 0 else {
throw ValidationError("Room heating load should not be less than 0.")
}
}
if let coolingTotal {
guard coolingTotal >= 0 else {
throw ValidationError("Room cooling total should not be less than 0.")
}
}
if let coolingSensible {
guard coolingSensible >= 0 else {
throw ValidationError("Room cooling sensible should not be less than 0.")
}
}
if let registerCount {
guard registerCount >= 1 else {
throw ValidationError("Room cooling sensible should not be less than 1.")
}
}
}
}
extension Room {
struct Migrate: AsyncMigration {
let name = "CreateRoom"
func prepare(on database: any Database) async throws {
try await database.schema(RoomModel.schema)
.id()
.field("name", .string, .required)
.field("heatingLoad", .double, .required)
.field("coolingTotal", .double, .required)
.field("coolingSensible", .double)
.field("registerCount", .int8, .required)
.field("rectangularSizes", .array)
.field("createdAt", .datetime)
.field("updatedAt", .datetime)
.field(
"projectID", .uuid, .required, .references(ProjectModel.schema, "id", onDelete: .cascade)
)
.unique(on: "projectID", "name")
.create()
}
func revert(on database: any Database) async throws {
try await database.schema(RoomModel.schema).delete()
}
}
}
final class RoomModel: Model, @unchecked Sendable {
static let schema = "room"
@ID(key: .id)
var id: UUID?
@Field(key: "name")
var name: String
@Field(key: "heatingLoad")
var heatingLoad: Double
@Field(key: "coolingTotal")
var coolingTotal: Double
@Field(key: "coolingSensible")
var coolingSensible: Double?
@Field(key: "registerCount")
var registerCount: Int
@Field(key: "rectangularSizes")
var rectangularSizes: [Room.RectangularSize]?
@Timestamp(key: "createdAt", on: .create, format: .iso8601)
var createdAt: Date?
@Timestamp(key: "updatedAt", on: .update, format: .iso8601)
var updatedAt: Date?
@Parent(key: "projectID")
var project: ProjectModel
init() {}
init(
id: UUID? = nil,
name: String,
heatingLoad: Double,
coolingTotal: Double,
coolingSensible: Double? = nil,
registerCount: Int,
rectangularSizes: [Room.RectangularSize]? = nil,
createdAt: Date? = nil,
updatedAt: Date? = nil,
projectID: Project.ID
) {
self.id = id
self.name = name
self.heatingLoad = heatingLoad
self.coolingTotal = coolingTotal
self.coolingSensible = coolingSensible
self.registerCount = registerCount
self.rectangularSizes = rectangularSizes
self.createdAt = createdAt
self.updatedAt = updatedAt
$project.id = projectID
}
func toDTO() throws -> Room {
try .init(
id: requireID(),
projectID: $project.id,
name: name,
heatingLoad: heatingLoad,
coolingTotal: coolingTotal,
coolingSensible: coolingSensible,
registerCount: registerCount,
rectangularSizes: rectangularSizes,
createdAt: createdAt!,
updatedAt: updatedAt!
)
}
func applyUpdates(_ updates: Room.Update) {
if let name = updates.name, name != self.name {
self.name = name
}
if let heatingLoad = updates.heatingLoad, heatingLoad != self.heatingLoad {
self.heatingLoad = heatingLoad
}
if let coolingTotal = updates.coolingTotal, coolingTotal != self.coolingTotal {
self.coolingTotal = coolingTotal
}
if let coolingSensible = updates.coolingSensible, coolingSensible != self.coolingSensible {
self.coolingSensible = coolingSensible
}
if let registerCount = updates.registerCount, registerCount != self.registerCount {
self.registerCount = registerCount
}
if let rectangularSizes = updates.rectangularSizes, rectangularSizes != self.rectangularSizes {
self.rectangularSizes = rectangularSizes
}
}
}