1# Upgrade guide
2
3This guide contains steps for upgrading crates in this project between major
4versions.
5
6## Upgrading from url 1.x to 2.1+
7
8* The minimum supported Rust version is now v1.33.0. Verify that you can bump
9  your library or application to the same MSRV.
10
11* `Url` no longer implements `std::net::ToSocketAddrs`. You will instead need to
12  explicitly call `socket_addrs` to convert your `Url` to a type that implements
13  `ToSocketAddrs`.
14
15  Note that v2.0 removed support for `std::net::ToSocketAddrs` with no
16  replacement; the `socket_addrs` method was not added until v2.1.
17
18    Before upgrading:
19
20    ```rust
21    let url = Url::parse("http://github.com:80").unwrap();
22    let stream = TcpStream::connect(url).unwrap();
23    ```
24
25    After upgrading:
26
27    ```rust
28    let url = Url::parse("http://github.com:80").unwrap();
29    let addrs = url.socket_addrs(|| None).unwrap();
30    let stream = TcpStream::connect(addrs).unwrap();
31    ```
32
33    Before upgrading:
34
35    ```rust
36    let url = Url::parse("socks5://localhost").unwrap();
37    let stream = TcpStream::connect(url.with_default_port(|url| match url.scheme() {
38        "socks5" => Ok(1080),
39        _ => Err(()),
40    })).unwrap();
41    ```
42
43    After upgrading:
44
45    ```rust
46    let url = Url::parse("http://github.com:80").unwrap();
47    let stream = TcpStream::connect(url.socket_addrs(|| match url.scheme() {
48        "socks5" => Some(1080),
49        _ => Err(()),
50    })).unwrap();
51    ```
52
53* `url_serde` is no longer required to use `Url` with Serde 1.x. Remove
54  references to `url_serde` and enable the `serde` feature instead.
55
56     ```toml
57     # Cargo.toml
58     [dependencies]
59     url = { version = "2.0", features = ["serde"] }
60     ```
61
62* The `idna` and `percent_export` crates are no longer exported by the `url`
63  crate. Depend on those crates directly instead. See below for additional
64  breaking changes in the percent-export package.
65
66    Before upgrading:
67
68    ```rust
69    use url::percent_encoding::percent_decode;
70    ```
71
72    After upgrading:
73
74    ```rust
75    use percent_encoding::percent_decode;
76    ```
77
78## Upgrading from percent-encoding 1.x to 2.x
79
80* Prepackaged encoding sets, like `QUERY_ENCODE_SET` and
81  `PATH_SEGMENT_ENCODE_SET`, are no longer provided. You
82  will need to read the specifications relevant to your domain and construct
83  your own encoding sets by using the `percent_encoding::AsciiSet` builder
84  methods on either of the base encoding sets, `percent_encoding::CONTROLS` or
85  `percent_encoding::NON_ALPHANUMERIC`.
86
87    Before upgrading:
88
89    ```rust
90    use percent_encoding::QUERY_ENCODE_SET;
91
92    percent_encoding::utf8_percent_encode(value, QUERY_ENCODE_SET);
93    ```
94
95    After upgrading:
96
97    ```rust
98    /// https://url.spec.whatwg.org/#query-state
99    const QUERY: &AsciiSet = &CONTROLS.add(b' ').add(b'"').add(b'#').add(b'<').add(b'>');
100
101    percent_encoding::utf8_percent_encode(value, QUERY);
102    ```
103
104
105## Upgrading from url 0.x to 1.x
106
107* The fields of `Url` are now private because the `Url` constructor, parser,
108  and setters maintain invariants that could be violated if you were to set the fields directly.
109  Instead of accessing, for example, `url.scheme`, use the getter method, such as `url.scheme()`.
110  Instead of assigning directly to a field, for example `url.scheme = "https".to_string()`,
111  use the setter method, such as `url.set_scheme("https").unwrap()`.
112  (Some setters validate the new value and return a `Result` that must be used).
113
114* The methods of `Url` now return `&str` instead of `String`,
115  thus reducing allocations and making serialization cheap.
116
117* The `path()` method on `url::Url` instances used to return `Option<&[String]>`;
118  now it returns `&str`.
119  If you would like functionality more similar to the old behavior of `path()`,
120  use `path_segments()` that returns `Option<str::Split<char>>`.
121
122    Before upgrading:
123
124    ```rust
125    let issue_list_url = Url::parse(
126         "https://github.com/rust-lang/rust/issues?labels=E-easy&state=open"
127    ).unwrap();
128    assert_eq!(issue_list_url.path(), Some(&["rust-lang".to_string(),
129                                             "rust".to_string(),
130                                             "issues".to_string()][..]));
131    ```
132
133    After upgrading:
134
135    ```rust
136    let issue_list_url = Url::parse(
137         "https://github.com/rust-lang/rust/issues?labels=E-easy&state=open"
138    ).unwrap();
139    assert_eq!(issue_list_url.path(), "/rust-lang/rust/issues");
140    assert_eq!(issue_list_url.path_segments().map(|c| c.collect::<Vec<_>>()),
141               Some(vec!["rust-lang", "rust", "issues"]));
142    ```
143
144* The `path_mut()` method on `url::Url` instances that allowed modification of a URL's path
145  has been replaced by `path_segments_mut()`.
146
147    Before upgrading:
148
149    ```rust
150    let mut url = Url::parse("https://github.com/rust-lang/rust").unwrap();
151    url.path_mut().unwrap().push("issues");
152    ```
153
154    After upgrading:
155
156    ```rust
157    let mut url = Url::parse("https://github.com/rust-lang/rust").unwrap();
158    url.path_segments_mut().unwrap().push("issues");
159    ```
160
161* The `domain_mut()` method on `url::Url` instances that allowed modification of a URL's domain
162  has been replaced by `set_host()` and `set_ip_host()`.
163
164* The `host()` method on `url::Url` instances used to return `Option<&Host>`;
165  now it returns `Option<Host<&str>>`.
166  The `serialize_host()` method that returned `Option<String>`
167  has been replaced by the `host_str()` method that returns `Option<&str>`.
168
169* The `serialize()` method on `url::Url` instances that returned `String`
170  has been replaced by an `as_str()` method that returns `&str`.
171
172    Before upgrading:
173
174    ```rust
175    let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html").unwrap();
176    assert_eq!(this_document.serialize(), "http://servo.github.io/rust-url/url/index.html".to_string());
177    ```
178
179    After upgrading:
180
181    ```rust
182    let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html").unwrap();
183    assert_eq!(this_document.as_str(), "http://servo.github.io/rust-url/url/index.html");
184    ```
185
186* `url::UrlParser` has been replaced by `url::Url::parse()` and `url::Url::join()`.
187
188    Before upgrading:
189
190    ```rust
191    let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html").unwrap();
192    let css_url = UrlParser::new().base_url(&this_document).parse("../main.css").unwrap();
193    assert_eq!(css_url.serialize(), "http://servo.github.io/rust-url/main.css".to_string());
194    ```
195
196    After upgrading:
197
198    ```rust
199    let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html").unwrap();
200    let css_url = this_document.join("../main.css").unwrap();
201    assert_eq!(css_url.as_str(), "http://servo.github.io/rust-url/main.css");
202    ```
203
204* `url::parse_path()` and `url::UrlParser::parse_path()` have been removed without replacement.
205  As a workaround, you can give a base URL that you then ignore too `url::Url::parse()`.
206
207    Before upgrading:
208
209    ```rust
210    let (path, query, fragment) = url::parse_path("/foo/bar/../baz?q=42").unwrap();
211    assert_eq!(path, vec!["foo".to_string(), "baz".to_string()]);
212    assert_eq!(query, Some("q=42".to_string()));
213    assert_eq!(fragment, None);
214    ```
215
216    After upgrading:
217
218    ```rust
219    let base = Url::parse("http://example.com").unwrap();
220    let with_path = base.join("/foo/bar/../baz?q=42").unwrap();
221    assert_eq!(with_path.path(), "/foo/baz");
222    assert_eq!(with_path.query(), Some("q=42"));
223    assert_eq!(with_path.fragment(), None);
224    ```
225
226* The `url::form_urlencoded::serialize()` method
227  has been replaced with the `url::form_urlencoded::Serializer` struct.
228  Instead of calling `serialize()` with key/value pairs,
229  create a new `Serializer` with a new string,
230  call the `extend_pairs()` method on the `Serializer` instance with the key/value pairs as the argument,
231  then call `finish()`.
232
233    Before upgrading:
234
235    ```rust
236    let form = url::form_urlencoded::serialize(form.iter().map(|(k, v)| {
237        (&k[..], &v[..])
238    }));
239    ```
240
241    After upgrading:
242
243    ```rust
244    let form = url::form_urlencoded::Serializer::new(String::new()).extend_pairs(
245        form.iter().map(|(k, v)| { (&k[..], &v[..]) })
246    ).finish();
247    ```
248
249* The `set_query_from_pairs()` method on `url::Url` instances that took key/value pairs
250  has been replaced with `query_pairs_mut()`, which allows you to modify the `url::Url`'s query pairs.
251
252    Before upgrading:
253
254    ```rust
255    let mut url = Url::parse("https://duckduckgo.com/").unwrap();
256    let pairs = vec![
257        ("q", "test"),
258        ("ia", "images"),
259    ];
260    url.set_query_from_pairs(pairs.iter().map(|&(k, v)| {
261        (&k[..], &v[..])
262    }));
263    ```
264
265    After upgrading:
266
267    ```rust
268    let mut url = Url::parse("https://duckduckgo.com/").unwrap();
269    let pairs = vec![
270        ("q", "test"),
271        ("ia", "images"),
272    ];
273    url.query_pairs_mut().clear().extend_pairs(
274      pairs.iter().map(|&(k, v)| { (&k[..], &v[..]) })
275    );
276    ```
277
278* `url::SchemeData`, its variants `Relative` and `NonRelative`,
279  and the struct `url::RelativeSchemeData` have been removed.
280  Instead of matching on these variants
281  to determine if you have a URL in a relative scheme such as HTTP
282  versus a URL in a non-relative scheme as data,
283  use the `cannot_be_a_base()` method to determine which kind you have.
284
285    Before upgrading:
286
287    ```rust
288    match url.scheme_data {
289        url::SchemeData::Relative(..) => {}
290        url::SchemeData::NonRelative(..) => {
291            return Err(human(format!("`{}` must have relative scheme \
292                                      data: {}", field, url)))
293        }
294    }
295    ```
296
297    After upgrading:
298
299    ```rust
300    if url.cannot_be_a_base() {
301        return Err(human(format!("`{}` must have relative scheme \
302                                  data: {}", field, url)))
303    }
304    ```
305
306* The functions `url::whatwg_scheme_type_mapper()`, the `SchemeType` enum,
307  and the `scheme_type_mapper()` method on `url::UrlParser` instances have been removed.
308  `SchemeType` had a method for getting the `default_port()`;
309  to replicate this functionality, use the method `port_or_known_default()` on `url::Url` instances.
310  The `port_or_default()` method on `url::Url` instances has been removed;
311  use `port_or_known_default()` instead.
312
313    Before upgrading:
314
315    ```rust
316    let port = match whatwg_scheme_type_mapper(&url.scheme) {
317        SchemeType::Relative(port) => port,
318        _ => return Err(format!("Invalid special scheme: `{}`",
319                                raw_url.scheme)),
320    };
321    ```
322
323    After upgrading:
324
325    ```rust
326    let port = match url.port_or_known_default() {
327        Some(port) => port,
328        _ => return Err(format!("Invalid special scheme: `{}`",
329                                url.scheme())),
330    };
331    ```
332
333* The following formatting utilities have been removed without replacement;
334  look at their linked previous implementations
335  if you would like to replicate the functionality in your code:
336  * [`url::format::PathFormatter`](https://github.com/servo/rust-url/pull/176/commits/9e759f18726c8e1343162922b87163d4dd08fe3c#diff-0bb16ac13b75e9b568fa4aff61b0e71dL24)
337  * [`url::format::UserInfoFormatter`](https://github.com/servo/rust-url/pull/176/commits/9e759f18726c8e1343162922b87163d4dd08fe3c#diff-0bb16ac13b75e9b568fa4aff61b0e71dL50)
338  * [`url::format::UrlNoFragmentFormatter`](https://github.com/servo/rust-url/pull/176/commits/9e759f18726c8e1343162922b87163d4dd08fe3c#diff-0bb16ac13b75e9b568fa4aff61b0e71dL70)
339
340* `url::percent_encoding::percent_decode()` used to have a return type of `Vec<u8>`;
341  now it returns an iterator of decoded `u8` bytes that also implements `Into<Cow<u8>>`.
342  Use `.into().to_owned()` to obtain a `Vec<u8>`.
343  (`.collect()` also works but might not be as efficient.)
344
345* The `url::percent_encoding::EncodeSet` struct and constant instances
346  used with `url::percent_encoding::percent_encode()`
347  have been changed to structs that implement the trait `url::percent_encoding::EncodeSet`.
348  * `SIMPLE_ENCODE_SET`, `QUERY_ENCODE_SET`, `DEFAULT_ENCODE_SET`,
349    and `USERINFO_ENCODE_SET` have the same behavior.
350  * `USERNAME_ENCODE_SET` and `PASSWORD_ENCODE_SET` have been removed;
351    use `USERINFO_ENCODE_SET` instead.
352  * `HTTP_VALUE_ENCODE_SET` has been removed;
353    an implementation of it in the new types can be found [in hyper's source](
354    https://github.com/hyperium/hyper/blob/67436c5bf615cf5a55a71e32b788afef5985570e/src/header/parsing.rs#L131-L138)
355    if you need to replicate this functionality in your code.
356  * `FORM_URLENCODED_ENCODE_SET` has been removed;
357    instead, use the functionality in `url::form_urlencoded`.
358  * `PATH_SEGMENT_ENCODE_SET` has been added for use on '/'-separated path segments.
359
360* `url::percent_encoding::percent_decode_to()` has been removed.
361  Use `url::percent_encoding::percent_decode()` which returns an iterator.
362  You can then use the iterator’s `collect()` method
363  or give it to some data structure’s `extend()` method.
364* A number of `ParseError` variants have changed.
365  [See the documentation for the current set](http://servo.github.io/rust-url/url/enum.ParseError.html).
366* `url::OpaqueOrigin::new()` and `url::Origin::UID(OpaqueOrigin)`
367  have been replaced by `url::Origin::new_opaque()` and `url::Origin::Opaque(OpaqueOrigin)`, respectively.
368