Files
aris-old/IrisCompanion/iris/Models/WinnerEnvelope.swift

101 lines
2.9 KiB
Swift
Raw Normal View History

2026-01-08 19:16:32 +00:00
//
// WinnerEnvelope.swift
// iris
//
// Created by Codex.
//
import Foundation
struct WinnerEnvelope: Codable, Equatable {
struct DebugInfo: Codable, Equatable {
let reason: String
let source: String
}
let schema: Int
let generatedAt: Int
let winner: Winner
let debug: DebugInfo?
enum CodingKeys: String, CodingKey {
case schema
case generatedAt = "generated_at"
case winner
case debug
}
static func allQuiet(now: Int, reason: String = "no_candidates", source: String = "engine") -> WinnerEnvelope {
let winner = Winner(
id: "quiet-000",
type: .allQuiet,
title: "All Quiet",
subtitle: "No urgent updates",
priority: 0.05,
ttlSec: 300
)
return WinnerEnvelope(schema: 1, generatedAt: now, winner: winner, debug: .init(reason: reason, source: source))
}
}
enum EnvelopeValidationError: Error, LocalizedError {
case invalidSchema(Int)
case invalidPriority(Double)
case invalidTTL(Int)
var errorDescription: String? {
switch self {
case .invalidSchema(let schema):
return "Invalid schema \(schema). Expected 1."
case .invalidPriority(let priority):
return "Invalid priority \(priority). Must be between 0 and 1."
case .invalidTTL(let ttl):
return "Invalid ttl \(ttl). Must be greater than 0."
}
}
}
func validateEnvelope(_ envelope: WinnerEnvelope) throws -> WinnerEnvelope {
guard envelope.schema == 1 else {
throw EnvelopeValidationError.invalidSchema(envelope.schema)
}
guard envelope.winner.priority >= 0.0, envelope.winner.priority <= 1.0 else {
throw EnvelopeValidationError.invalidPriority(envelope.winner.priority)
}
guard envelope.winner.ttlSec > 0 else {
throw EnvelopeValidationError.invalidTTL(envelope.winner.ttlSec)
}
let validatedWinner = Winner(
id: envelope.winner.id,
type: envelope.winner.type,
title: envelope.winner.title.truncated(maxLength: TextConstraints.titleMax),
subtitle: envelope.winner.subtitle.truncated(maxLength: TextConstraints.subtitleMax),
priority: envelope.winner.priority,
ttlSec: envelope.winner.ttlSec
)
return WinnerEnvelope(
schema: envelope.schema,
generatedAt: envelope.generatedAt,
winner: validatedWinner,
debug: envelope.debug
)
}
enum TextConstraints {
static let titleMax = 26
static let subtitleMax = 30
static let ellipsis = "..."
}
extension String {
func truncated(maxLength: Int) -> String {
guard count > maxLength else { return self }
let ellipsisCount = TextConstraints.ellipsis.count
guard maxLength > ellipsisCount else { return String(prefix(maxLength)) }
return String(prefix(maxLength - ellipsisCount)) + TextConstraints.ellipsis
}
}