feat(domain): add AnySport conformance to Sport enum
Existing Sport enum now conforms to AnySport protocol, enabling unified handling with future DynamicSport types. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,9 +15,9 @@ enum Sport: String, Codable, CaseIterable, Identifiable {
|
|||||||
case wnba = "WNBA"
|
case wnba = "WNBA"
|
||||||
case nwsl = "NWSL"
|
case nwsl = "NWSL"
|
||||||
|
|
||||||
var id: String { rawValue }
|
nonisolated var id: String { rawValue }
|
||||||
|
|
||||||
var displayName: String {
|
nonisolated var displayName: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .mlb: return "Major League Baseball"
|
case .mlb: return "Major League Baseball"
|
||||||
case .nba: return "National Basketball Association"
|
case .nba: return "National Basketball Association"
|
||||||
@@ -29,7 +29,7 @@ enum Sport: String, Codable, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var iconName: String {
|
nonisolated var iconName: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .mlb: return "baseball.fill"
|
case .mlb: return "baseball.fill"
|
||||||
case .nba: return "basketball.fill"
|
case .nba: return "basketball.fill"
|
||||||
@@ -41,7 +41,7 @@ enum Sport: String, Codable, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var color: Color {
|
nonisolated var color: Color {
|
||||||
switch self {
|
switch self {
|
||||||
case .mlb: return .red
|
case .mlb: return .red
|
||||||
case .nba: return .orange
|
case .nba: return .orange
|
||||||
@@ -54,7 +54,7 @@ enum Sport: String, Codable, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Season start and end months (1-12). End may be less than start for seasons that wrap around the year.
|
/// Season start and end months (1-12). End may be less than start for seasons that wrap around the year.
|
||||||
var seasonMonths: (start: Int, end: Int) {
|
nonisolated var seasonMonths: (start: Int, end: Int) {
|
||||||
switch self {
|
switch self {
|
||||||
case .mlb: return (3, 10) // March - October
|
case .mlb: return (3, 10) // March - October
|
||||||
case .nba: return (10, 6) // October - June (wraps)
|
case .nba: return (10, 6) // October - June (wraps)
|
||||||
@@ -66,7 +66,7 @@ enum Sport: String, Codable, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isInSeason(for date: Date) -> Bool {
|
nonisolated func isInSeason(for date: Date) -> Bool {
|
||||||
let calendar = Calendar.current
|
let calendar = Calendar.current
|
||||||
let month = calendar.component(.month, from: date)
|
let month = calendar.component(.month, from: date)
|
||||||
|
|
||||||
@@ -81,11 +81,20 @@ enum Sport: String, Codable, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Currently supported sports
|
/// Currently supported sports
|
||||||
static var supported: [Sport] {
|
nonisolated static var supported: [Sport] {
|
||||||
[.mlb, .nba, .nfl, .nhl, .mls, .wnba, .nwsl]
|
[.mlb, .nba, .nfl, .nhl, .mls, .wnba, .nwsl]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - AnySport Conformance
|
||||||
|
|
||||||
|
extension Sport: AnySport, @unchecked Sendable {
|
||||||
|
nonisolated var sportId: String { rawValue }
|
||||||
|
|
||||||
|
// Note: displayName, iconName, color, seasonMonths already exist on Sport
|
||||||
|
// They need nonisolated to satisfy AnySport protocol requirements
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Array Chunking
|
// MARK: - Array Chunking
|
||||||
|
|
||||||
extension Array {
|
extension Array {
|
||||||
|
|||||||
53
SportsTimeTests/Domain/SportTests.swift
Normal file
53
SportsTimeTests/Domain/SportTests.swift
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//
|
||||||
|
// SportTests.swift
|
||||||
|
// SportsTimeTests
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Testing
|
||||||
|
@testable import SportsTime
|
||||||
|
|
||||||
|
@Suite("Sport AnySport Conformance")
|
||||||
|
struct SportAnySportTests {
|
||||||
|
|
||||||
|
@Test("Sport conforms to AnySport protocol")
|
||||||
|
func sportConformsToAnySport() {
|
||||||
|
let sport: any AnySport = Sport.mlb
|
||||||
|
#expect(sport.sportId == "MLB")
|
||||||
|
#expect(sport.displayName == "Major League Baseball")
|
||||||
|
#expect(sport.iconName == "baseball.fill")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Sport.id equals Sport.sportId")
|
||||||
|
func sportIdEqualsSportId() {
|
||||||
|
for sport in Sport.allCases {
|
||||||
|
#expect(sport.id == sport.sportId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Sport isInSeason works correctly")
|
||||||
|
func sportIsInSeason() {
|
||||||
|
let mlb = Sport.mlb
|
||||||
|
|
||||||
|
// April is in MLB season (March-October)
|
||||||
|
let april = Calendar.current.date(from: DateComponents(year: 2026, month: 4, day: 15))!
|
||||||
|
#expect(mlb.isInSeason(for: april))
|
||||||
|
|
||||||
|
// January is not in MLB season
|
||||||
|
let january = Calendar.current.date(from: DateComponents(year: 2026, month: 1, day: 15))!
|
||||||
|
#expect(!mlb.isInSeason(for: january))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Sport with wrap-around season works correctly")
|
||||||
|
func sportWrapAroundSeason() {
|
||||||
|
let nba = Sport.nba
|
||||||
|
|
||||||
|
// December is in NBA season (October-June wraps)
|
||||||
|
let december = Calendar.current.date(from: DateComponents(year: 2026, month: 12, day: 15))!
|
||||||
|
#expect(nba.isInSeason(for: december))
|
||||||
|
|
||||||
|
// July is not in NBA season
|
||||||
|
let july = Calendar.current.date(from: DateComponents(year: 2026, month: 7, day: 15))!
|
||||||
|
#expect(!nba.isInSeason(for: july))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user