|
1 | | -// |
2 | 1 | // URL.swift |
3 | 2 | // SwiftFoundation |
4 | 3 | // |
5 | 4 | // Created by Alsey Coleman Miller on 6/29/15. |
6 | 5 | // Copyright © 2015 PureSwift. All rights reserved. |
7 | 6 | // |
8 | 7 |
|
9 | | -#if os(Linux) || XcodeLinux |
10 | | - |
11 | | -/// Encapsulates the components of an URL. |
12 | | -public struct URL: CustomStringConvertible { |
13 | | - |
14 | | - // MARK: - Properties |
15 | | - |
16 | | - public var scheme: String |
17 | | - |
18 | | - public var user: String? |
19 | | - |
20 | | - public var password: String? |
21 | | - |
22 | | - /// The host URL subcomponent (e.g. domain name, IP address) |
23 | | - public var host: String? |
24 | | - |
25 | | - public var port: UInt? |
26 | | - |
27 | | - public var path: String? |
28 | | - |
29 | | - public var query: [(String, String)]? |
30 | | - |
31 | | - /// The fragment URL component (the part after a # symbol) |
32 | | - public var fragment: String? |
33 | | - |
34 | | - // MARK: - Initialization |
35 | | - |
36 | | - public init(scheme: String) { |
37 | | - |
38 | | - self.scheme = scheme |
| 8 | +/** |
| 9 | + A URL is a type that can potentially contain the location of a resource on a remote server, the path of a local file on disk, or even an arbitrary piece of encoded data. |
| 10 | + |
| 11 | + You can construct URLs and access their parts. For URLs that represent local files, you can also manipulate properties of those files directly, such as changing the file's last modification date. Finally, you can pass URLs to other APIs to retrieve the contents of those URLs. For example, you can use the URLSession classes to access the contents of remote resources, as described in URL Session Programming Guide. |
| 12 | + |
| 13 | + URLs are the preferred way to refer to local files. Most objects that read data from or write data to a file have methods that accept a URL instead of a pathname as the file reference. For example, you can get the contents of a local file URL as `String` by calling `func init(contentsOf:encoding) throws`, or as a `Data` by calling `func init(contentsOf:options) throws`. |
| 14 | +*/ |
| 15 | +public struct URL { |
| 16 | + |
| 17 | + internal let stringValue: String |
| 18 | + |
| 19 | + /// Initialize with string. |
| 20 | + /// |
| 21 | + /// Returns `nil` if a `URL` cannot be formed with the string (for example, if the string contains characters that are illegal in a URL, or is an empty string). |
| 22 | + public init?(string: String) { |
| 23 | + // TODO: Validate URL string |
| 24 | + guard string.isEmpty == false |
| 25 | + else { return nil } |
| 26 | + self.init(string: string) |
39 | 27 | } |
40 | 28 |
|
41 | | - /// Creates an instance from the string. String must be a valid URL. |
42 | | - public init?(stringValue: String) { |
43 | | - |
44 | | - // parse string |
45 | | - |
46 | | - debugPrint("URL parsing from string is not implemented yet!") |
47 | | - |
48 | | - return nil |
| 29 | + /// Returns the absolute string for the URL. |
| 30 | + public var absoluteString: String { |
| 31 | + return stringValue |
49 | 32 | } |
| 33 | +} |
| 34 | + |
| 35 | +// MARK: - Equatable |
| 36 | + |
| 37 | +extension URL: Equatable { |
50 | 38 |
|
51 | | - // MARK: - Generated Properties |
| 39 | + public static func == (lhs: URL, rhs: URL) -> Bool { |
| 40 | + return lhs.stringValue == rhs.stringValue |
| 41 | + } |
| 42 | +} |
| 43 | + |
| 44 | +// MARK: - Hashable |
| 45 | + |
| 46 | +extension URL: Hashable { |
52 | 47 |
|
53 | | - /// Whether the URL components form a valid URL |
54 | | - public var valid: Bool { |
55 | | - |
56 | | - // validate scheme |
57 | | - |
58 | | - // host must exist for port to be specified |
59 | | - if port != nil { guard host != nil else { return false } } |
60 | | - |
61 | | - // user and password must both be nil or non-nil |
62 | | - guard !((user != nil || password != nil) && (user == nil || password == nil)) else { return false } |
63 | | - |
64 | | - // query must have at least one item |
65 | | - if query != nil { guard query!.count > 0 else { return false } } |
66 | | - |
67 | | - return true |
| 48 | + public func hash(into hasher: inout Hasher) { |
| 49 | + stringValue.hash(into: &hasher) |
68 | 50 | } |
| 51 | +} |
| 52 | + |
| 53 | +// MARK: - CustomStringConvertible |
| 54 | + |
| 55 | +extension URL: CustomStringConvertible { |
69 | 56 |
|
70 | | - /// Returns a valid URL string or ```nil``` |
71 | | - public var URLString: String? { |
72 | | - |
73 | | - guard self.valid else { return nil } |
74 | | - |
75 | | - var stringValue = scheme + "://" |
76 | | - |
77 | | - if let user = user { stringValue += user } |
78 | | - |
79 | | - if let password = password { stringValue += ":\(password)"} |
80 | | - |
81 | | - if user != nil { stringValue += "@" } |
82 | | - |
83 | | - if let host = host { stringValue += host } |
84 | | - |
85 | | - if let port = port { stringValue += ":\(port)" } |
86 | | - |
87 | | - if let path = path { stringValue += "/\(path)" } |
88 | | - |
89 | | - if let query = query { |
90 | | - |
91 | | - stringValue += "?" |
92 | | - |
93 | | - for (index, queryItem) in query.enumerated() { |
94 | | - |
95 | | - let (name, value) = queryItem |
96 | | - |
97 | | - stringValue += name + "=" + value |
98 | | - |
99 | | - if index != query.count - 1 { |
100 | | - |
101 | | - stringValue += "&" |
102 | | - } |
103 | | - } |
104 | | - } |
105 | | - |
106 | | - if let fragment = fragment { stringValue += "#\(fragment)" } |
107 | | - |
| 57 | + public var description: String { |
108 | 58 | return stringValue |
109 | 59 | } |
| 60 | +} |
| 61 | + |
| 62 | +// MARK: - CustomDebugStringConvertible |
| 63 | + |
| 64 | +extension URL: CustomDebugStringConvertible { |
110 | 65 |
|
111 | | - public var description: String { |
112 | | - |
113 | | - let separator = " " |
114 | | - |
115 | | - var description = "" |
116 | | - |
117 | | - if let URLString = URLString { |
118 | | - |
119 | | - description += "URL: " + URLString + separator |
120 | | - } |
121 | | - |
122 | | - description += "Scheme: " + scheme |
123 | | - |
124 | | - if let user = user { |
125 | | - |
126 | | - description += separator + "User: " + user |
127 | | - } |
128 | | - |
129 | | - if let password = password { |
130 | | - |
131 | | - description += separator + "Password: " + password |
132 | | - } |
133 | | - |
134 | | - if let host = host { |
135 | | - |
136 | | - description += separator + "Host: " + host |
137 | | - } |
138 | | - |
139 | | - if let port = port { |
140 | | - |
141 | | - description += separator + "Port: " + "\(port)" |
142 | | - } |
143 | | - |
144 | | - if let path = path { |
145 | | - |
146 | | - description += separator + "Path: " + path |
147 | | - } |
148 | | - |
149 | | - if let query = query { |
150 | | - |
151 | | - var stringValue = "" |
152 | | - |
153 | | - for (index, queryItem) in query.enumerated() { |
154 | | - |
155 | | - let (name, value) = queryItem |
156 | | - |
157 | | - stringValue += name + "=" + value |
158 | | - |
159 | | - if index != query.count - 1 { |
160 | | - |
161 | | - stringValue += "&" |
162 | | - } |
163 | | - } |
164 | | - |
165 | | - description += separator + "Query: " + stringValue |
166 | | - } |
167 | | - |
168 | | - if let fragment = fragment { |
169 | | - |
170 | | - description += separator + "Fragment: " + fragment |
171 | | - } |
172 | | - |
| 66 | + public var debugDescription: String { |
173 | 67 | return description |
174 | 68 | } |
175 | 69 | } |
176 | 70 |
|
177 | | -#endif |
| 71 | +// MARK: - Codable |
| 72 | + |
| 73 | +extension URL: Codable { |
| 74 | + |
| 75 | + public init(from decoder: Decoder) throws { |
| 76 | + let container = try decoder.singleValueContainer() |
| 77 | + let string = try container.decode(String.self) |
| 78 | + guard let url = URL(string: string) else { |
| 79 | + throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, |
| 80 | + debugDescription: "Invalid URL string.")) |
| 81 | + } |
| 82 | + self = url |
| 83 | + } |
| 84 | + |
| 85 | + public func encode(to encoder: Encoder) throws { |
| 86 | + var container = encoder.singleValueContainer() |
| 87 | + try container.encode(stringValue) |
| 88 | + } |
| 89 | +} |
0 commit comments