krz/hutch
an ios client for sourcehut
clone: git clone https://gitbay.org/krz/hutch.git
v2.15.1: Hutch/Extensions/SRHTWebURL.swift · raw
1import Foundation
2
3enum SRHTWebURL {
4 static let chat = URL(string: "https://chat.sr.ht")!
5 static let status = URL(string: "https://status.sr.ht")!
6
7 static func repository(_ repository: RepositorySummary) -> URL? {
8 userScopedURL(
9 host: "\(repository.service.rawValue).sr.ht",
10 ownerCanonicalName: repository.owner.canonicalName,
11 pathComponents: [repository.name]
12 )
13 }
14
15 static func commit(repository: RepositorySummary, commitId: String) -> URL? {
16 userScopedURL(
17 host: "\(repository.service.rawValue).sr.ht",
18 ownerCanonicalName: repository.owner.canonicalName,
19 pathComponents: [repository.name, commitPathComponent(for: repository.service), commitId]
20 )
21 }
22
23 static func file(repository: RepositorySummary, revspec: String, path: String) -> URL? {
24 switch repository.service {
25 case .git:
26 return userScopedURL(
27 host: "git.sr.ht",
28 ownerCanonicalName: repository.owner.canonicalName,
29 pathComponents: [repository.name, "tree", revspec, "item"] + pathComponents(from: path)
30 )
31 case .hg:
32 var components = URLComponents()
33 components.scheme = "https"
34 components.host = "hg.sr.ht"
35
36 let ownerUsername = username(from: repository.owner.canonicalName)
37 let encodedRepository = repository.name.addingPercentEncoding(withAllowedCharacters: pathComponentCharacterSet) ?? repository.name
38 let encodedPath = path.split(separator: "/").map {
39 String($0).addingPercentEncoding(withAllowedCharacters: pathComponentCharacterSet) ?? String($0)
40 }.joined(separator: "/")
41
42 components.percentEncodedPath = "/~\(ownerUsername)/\(encodedRepository)/browse/\(encodedPath)"
43 components.queryItems = [URLQueryItem(name: "rev", value: revspec)]
44 return components.url
45 default:
46 return nil
47 }
48 }
49
50 static func build(jobId: Int, ownerCanonicalName: String) -> URL? {
51 userScopedURL(
52 host: "builds.sr.ht",
53 ownerCanonicalName: ownerCanonicalName,
54 pathComponents: ["job", String(jobId)]
55 )
56 }
57
58 static func tracker(ownerUsername: String, trackerName: String) -> URL? {
59 userScopedURL(
60 host: "todo.sr.ht",
61 ownerUsername: ownerUsername,
62 pathComponents: [trackerName]
63 )
64 }
65
66 static func ticket(ownerUsername: String, trackerName: String, ticketId: Int) -> URL? {
67 userScopedURL(
68 host: "todo.sr.ht",
69 ownerUsername: ownerUsername,
70 pathComponents: [trackerName, String(ticketId)]
71 )
72 }
73
74 static func profile(canonicalName: String) -> URL? {
75 userScopedURL(
76 host: "sr.ht",
77 ownerCanonicalName: canonicalName,
78 pathComponents: []
79 )
80 }
81
82 static func paste(ownerCanonicalName: String, pasteId: String) -> URL? {
83 userScopedURL(
84 host: "paste.sr.ht",
85 ownerCanonicalName: ownerCanonicalName,
86 pathComponents: [pasteId]
87 )
88 }
89
90 private static func userScopedURL(
91 host: String,
92 ownerCanonicalName: String,
93 pathComponents: [String]
94 ) -> URL? {
95 userScopedURL(
96 host: host,
97 ownerUsername: username(from: ownerCanonicalName),
98 pathComponents: pathComponents
99 )
100 }
101
102 private static func userScopedURL(
103 host: String,
104 ownerUsername: String,
105 pathComponents: [String]
106 ) -> URL? {
107 var components = URLComponents()
108 components.scheme = "https"
109 components.host = host
110
111 let encodedComponents = (["~\(ownerUsername)"] + pathComponents).map { pathComponent in
112 pathComponent.addingPercentEncoding(withAllowedCharacters: pathComponentCharacterSet) ?? pathComponent
113 }
114 components.percentEncodedPath = "/" + encodedComponents.joined(separator: "/")
115 return components.url
116 }
117
118 private static func username(from canonicalName: String) -> String {
119 if canonicalName.hasPrefix("~") {
120 return String(canonicalName.dropFirst())
121 }
122 return canonicalName
123 }
124
125 private static func commitPathComponent(for service: SRHTService) -> String {
126 switch service {
127 case .hg:
128 return "rev"
129 default:
130 return "commit"
131 }
132 }
133
134 private static let pathComponentCharacterSet: CharacterSet = {
135 var characterSet = CharacterSet.urlPathAllowed
136 characterSet.remove(charactersIn: "/")
137 return characterSet
138 }()
139
140 private static func pathComponents(from path: String) -> [String] {
141 path.split(separator: "/").map(String.init)
142 }
143}