Packages/MoonriseKit/Sources/Library/Tours.swift
49 lines · 1778 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
13 public struct Step: Sendable, Decodable, Hashable {
14 public var title: String
15 public var target: Target
16 public var text: String
17 /// Library page to open, when it is not the target's own page.
18 public var page: String?
19 }
20
21 public var id: String
22 public var title: String
23 /// Months (1–12) when the tour's stars are up in the evening (the same in both hemispheres).
24 public var months: [Int]
25 public var intro: String
26 public var steps: [Step]
27
28 public static let all: [Tour] = {
29 let url = Bundle.module.url(forResource: "tours", withExtension: "json", subdirectory: "tours")!
30 return try! JSONDecoder().decode([Tour].self, from: Data(contentsOf: url))
31 }()
32
33 /// Tours in order from the one for `month` onwards.
34 public static func ordered(month: Int) -> [Tour] {
35 let start = all.firstIndex { $0.months.contains(month) } ?? 0
36 return Array(all[start...] + all[..<start])
37 }
38}
39
40extension Tour.Step {
41 /// The step's own Library page, else the target's.
42 public var libraryPage: LibraryPage? {
43 if let page { return Library.page(id: page) }
44 if let hr = target.hr { return Library.page(hr: hr) }
45 if let c = target.constellation { return Library.page(constellation: c) }
46 if let b = target.body { return Library.page(body: b) }
47 return nil
48 }
49}