101 lines
2.9 KiB
Swift
101 lines
2.9 KiB
Swift
|
|
//
|
||
|
|
// 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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|