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