Packages/MoonriseKit/Sources/Library/Tours.swift
55 lines · 1979 bytes
1import Foundation
2
3/// A guided walk around one season's evening sky.
4public struct Tour: Sendable, Decodable, Identifiable {
5 public struct Target: Sendable, Decodable, Hashable {
6 /// IAU abbreviation, e.g. "Ori".
7 public var constellation: String?
8 public var hr: Int?
9 /// Body raw value, e.g. "jupiter".
10 public var body: String?
11
12 public init(constellation: String? = nil, hr: Int? = nil, body: String? = nil) {
13 self.constellation = constellation
14 self.hr = hr
15 self.body = body
16 }
17 }
18
19 public struct Step: Sendable, Decodable, Hashable {
20 public var title: String
21 public var target: Target
22 public var text: String
23 /// Library page to open, when it is not the target's own page.
24 public var page: String?
25 }
26
27 public var id: String
28 public var title: String
29 /// Months (1–12) when the tour's stars are up in the evening (the same in both hemispheres).
30 public var months: [Int]
31 public var intro: String
32 public var steps: [Step]
33
34 public static let all: [Tour] = {
35 let url = Bundle.module.url(forResource: "tours", withExtension: "json", subdirectory: "tours")!
36 return try! JSONDecoder().decode([Tour].self, from: Data(contentsOf: url))
37 }()
38
39 /// Tours in order from the one for `month` onwards.
40 public static func ordered(month: Int) -> [Tour] {
41 let start = all.firstIndex { $0.months.contains(month) } ?? 0
42 return Array(all[start...] + all[..<start])
43 }
44}
45
46extension Tour.Step {
47 /// The step's own Library page, else the target's.
48 public var libraryPage: LibraryPage? {
49 if let page { return Library.page(id: page) }
50 if let hr = target.hr { return Library.page(hr: hr) }
51 if let c = target.constellation { return Library.page(constellation: c) }
52 if let b = target.body { return Library.page(body: b) }
53 return nil
54 }
55}