summaryrefslogtreecommitdiff
path: root/Shared/SourceHutWebDeepLinkMapper.swift
blob: 236e32e0cb1f2b370a192607904468874b163480 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import Foundation

enum SourceHutWebDeepLinkMapper {
    static let supportedHosts: Set<String> = [
        "git.sr.ht",
        "hg.sr.ht",
        "todo.sr.ht",
        "builds.sr.ht",
        "lists.sr.ht",
        "meta.sr.ht",
        "sr.ht",
    ]

    static func deepLink(for webURL: URL) -> URL? {
        guard let components = URLComponents(url: webURL, resolvingAgainstBaseURL: false),
              components.scheme == "https",
              let host = components.host?.lowercased(),
              supportedHosts.contains(host)
        else {
            return nil
        }

        let path = normalizedPercentEncodedPath(from: components.percentEncodedPath)
        let deepLinkPath = normalizedDeepLinkPath(for: host, path: path)
        let service = deepLinkService(for: host, path: deepLinkPath)
        var deepLinkComponents = URLComponents()
        deepLinkComponents.scheme = "hutch"
        deepLinkComponents.host = service

        if !deepLinkPath.isEmpty {
            deepLinkComponents.percentEncodedPath = "/" + deepLinkPath
        }
        deepLinkComponents.percentEncodedQuery = components.percentEncodedQuery
        deepLinkComponents.percentEncodedFragment = components.percentEncodedFragment
        return deepLinkComponents.url
    }

    private static func deepLinkService(for host: String, path: String) -> String {
        if isOwnerRootPath(path) {
            return "lookup"
        }

        switch host {
        case "git.sr.ht":
            return "git"
        case "hg.sr.ht":
            return "hg"
        case "todo.sr.ht":
            return "todo"
        case "builds.sr.ht":
            return "builds"
        case "lists.sr.ht":
            return "lists"
        default:
            return "lookup"
        }
    }

    private static func isOwnerRootPath(_ path: String) -> Bool {
        let components = path.split(separator: "/", omittingEmptySubsequences: true)
        return components.count == 1 && components[0].hasPrefix("~")
    }

    private static func normalizedDeepLinkPath(for host: String, path: String) -> String {
        let components = path.split(separator: "/", omittingEmptySubsequences: true)
        if host == "sr.ht",
           components.count == 2,
           components[0] == "projects",
           components[1].hasPrefix("~") {
            return String(components[1])
        }
        return path
    }

    private static func normalizedPercentEncodedPath(from path: String) -> String {
        path
            .split(separator: "/", omittingEmptySubsequences: true)
            .joined(separator: "/")
    }
}