krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v3.1.3: Hutch/App/DeepLink.swift · raw

 1import Foundation
 2
 3/// Represents a parsed `hutch://` deep link.
 4enum DeepLink: Equatable {
 5    case home
 6    case work
 7    /// hutch://git/<owner>/<repo>
 8    case repository(owner: String, repo: String)
 9    /// hutch://todo/<owner>/<tracker>/<ticketId>
10    case ticket(owner: String, tracker: String, ticketId: Int)
11    /// hutch://builds/<jobId>
12    case build(jobId: Int)
13    /// hutch://builds (tab-level)
14    case buildsTab
15    /// hutch://repositories (tab-level)
16    case repositoriesTab
17    /// hutch://trackers (tab-level)
18    case trackersTab
19    /// hutch://status
20    case systemStatus
21    /// hutch://lookup
22    case lookup
23
24    /// Attempt to parse a URL into a DeepLink.
25    /// Expected format: hutch://<path>
26    init?(url: URL) {
27        guard url.scheme == "hutch" else { return nil }
28
29        // url.host gives the first path component for opaque URLs;
30        // use standardized path components from the full string.
31        let components = url.pathComponents(fromScheme: "hutch")
32
33        switch components.first {
34        case "home", nil:
35            self = .home
36
37        case "work", "inbox":
38            self = .work
39
40        case "git" where components.count >= 3:
41            let owner = components[1]
42            let repo = components[2]
43            self = .repository(owner: owner, repo: repo)
44
45        case "todo" where components.count >= 4:
46            let owner = components[1]
47            let tracker = components[2]
48            guard let ticketId = Int(components[3]) else { return nil }
49            self = .ticket(owner: owner, tracker: tracker, ticketId: ticketId)
50
51        case "builds" where components.count >= 2:
52            guard let jobId = Int(components[1]) else { return nil }
53            self = .build(jobId: jobId)
54
55        case "builds":
56            self = .buildsTab
57
58        case "repositories":
59            self = .repositoriesTab
60
61        case "trackers":
62            self = .trackersTab
63
64        case "status":
65            self = .systemStatus
66
67        case "lookup":
68            self = .lookup
69
70        default:
71            return nil
72        }
73    }
74}
75
76private extension URL {
77    /// Parse path components from a custom-scheme URL.
78    /// For `hutch://git/~user/repo`, returns `["git", "~user", "repo"]`.
79    func pathComponents(fromScheme scheme: String) -> [String] {
80        // Remove scheme prefix and split by "/"
81        var str = absoluteString
82        if str.hasPrefix("\(scheme)://") {
83            str = String(str.dropFirst("\(scheme)://".count))
84        }
85        return str.split(separator: "/").map(String.init)
86    }
87}