krz/hutch

an ios client for sourcehut

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

v2.15.1: Hutch/App/DeepLink.swift · raw

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