1// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3//
4// SPDX-License-Identifier: MIT OR Apache-2.0
5use cxx::{type_id, ExternType};
6use std::fmt;
7use std::mem::MaybeUninit;
89#[cxx::bridge]
10mod ffi {
11unsafe extern "C++" {
12include!("cxx-qt-lib/qbytearray.h");
13type QByteArray = crate::QByteArray;
14include!("cxx-qt-lib/qstring.h");
15type QString = crate::QString;
16include!("cxx-qt-lib/qstringlist.h");
17type QStringList = crate::QStringList;
18include!("cxx-qt-lib/qurl.h");
19type QUrl = super::QUrl;
2021/// Resets the content of the QUrl. After calling this function,
22 /// the QUrl is equal to one that has been constructed with the default empty constructor.
23fn clear(self: &mut QUrl);
2425/// Returns an error message if the last operation that modified this QUrl object ran into a parsing error.
26 /// If no error was detected, this function returns an empty string and isValid() returns true.
27#[rust_name = "error_string"]
28fn errorString(self: &QUrl) -> QString;
2930/// Returns true if this URL contains a fragment (i.e., if # was seen on it).
31#[rust_name = "has_fragment"]
32fn hasFragment(self: &QUrl) -> bool;
3334/// Returns true if this URL contains a Query (i.e., if ? was seen on it).
35#[rust_name = "has_query"]
36fn hasQuery(self: &QUrl) -> bool;
3738/// Returns true if the URL has no data; otherwise returns false.
39#[rust_name = "is_empty"]
40fn isEmpty(self: &QUrl) -> bool;
4142/// Returns true if this URL is pointing to a local file path. A URL is a local file path if the scheme is "file".
43#[rust_name = "is_local_file"]
44fn isLocalFile(self: &QUrl) -> bool;
4546/// Returns true if this URL is a parent of child_url.
47 /// childUrl is a child of this URL if the two URLs share the same scheme and authority,
48 /// and this URL's path is a parent of the path of child_url.
49#[rust_name = "is_parent_of"]
50fn isParentOf(self: &QUrl, child_url: &QUrl) -> bool;
5152/// Returns true if the URL is relative; otherwise returns false.
53 /// A URL is relative reference if its scheme is undefined;
54 /// this function is therefore equivalent to calling scheme().is_empty().
55#[rust_name = "is_relative"]
56fn isRelative(self: &QUrl) -> bool;
5758/// Returns true if the URL is non-empty and valid; otherwise returns false.
59#[rust_name = "is_valid"]
60fn isValid(self: &QUrl) -> bool;
6162/// Returns the port of the URL, or defaultPort if the port is unspecified.
63#[rust_name = "port_or"]
64fn port(self: &QUrl, port: i32) -> i32;
6566/// Returns the result of the merge of this URL with relative. This URL is used as a base to convert relative to an absolute URL.
67fn resolved(self: &QUrl, relative: &QUrl) -> QUrl;
6869/// Returns the scheme of the URL. If an empty string is returned,
70 /// this means the scheme is undefined and the URL is then relative.
71 ///
72 /// The scheme can only contain US-ASCII letters or digits,
73 /// which means it cannot contain any character that would otherwise require encoding
74 /// Additionally, schemes are always returned in lowercase form.
75#[rust_name = "scheme_or_default"]
76fn scheme(self: &QUrl) -> QString;
7778/// Sets the port of the URL to port.
79 ///
80 /// port must be between 0 and 65535 inclusive. Setting the port to -1 indicates that the port is unspecified.
81#[rust_name = "set_port"]
82fn setPort(self: &mut QUrl, port: i32);
8384/// Returns the path of this URL formatted as a local file path.
85 /// The path returned will use forward slashes, even if it was originally created from one with backslashes.
86#[rust_name = "to_local_file_or_default"]
87fn toLocalFile(self: &QUrl) -> QString;
88 }
8990// Bitwise enums don't work well with Rust and CXX, so lets just use the defaults for now
91#[namespace = "rust::cxxqtlib1"]
92unsafe extern "C++" {
93#[doc(hidden)]
94 #[rust_name = "qurl_init_from_string"]
95fn qurlInitFromString(string: &str) -> QUrl;
96#[doc(hidden)]
97 #[rust_name = "qurl_to_rust_string"]
98fn qurlToRustString(url: &QUrl) -> String;
99100#[rust_name = "qurl_authority"]
101fn qurlAuthority(url: &QUrl) -> QString;
102#[rust_name = "qurl_file_name"]
103fn qurlFileName(url: &QUrl) -> QString;
104#[rust_name = "qurl_fragment"]
105fn qurlFragment(url: &QUrl) -> QString;
106#[rust_name = "qurl_from_encoded"]
107fn qurlFromEncoded(input: &QByteArray) -> QUrl;
108#[rust_name = "qurl_from_local_file"]
109fn qurlFromLocalFile(local_file: &QString) -> QUrl;
110#[rust_name = "qurl_from_percent_encoding"]
111fn qurlFromPercentEncoding(input: &QByteArray) -> QString;
112#[rust_name = "qurl_from_user_input"]
113fn qurlFromUserInput(user_input: &QString, working_directory: &QString) -> QUrl;
114#[rust_name = "qurl_host"]
115fn qurlHost(url: &QUrl) -> QString;
116#[rust_name = "qurl_idn_whitelist"]
117fn qurlIdnWhitelist() -> QStringList;
118#[rust_name = "qurl_path"]
119fn qurlPath(url: &QUrl) -> QString;
120#[rust_name = "qurl_password"]
121fn qurlPassword(url: &QUrl) -> QString;
122#[rust_name = "qurl_query"]
123fn qurlQuery(url: &QUrl) -> QString;
124#[rust_name = "qurl_set_authority"]
125fn qurlSetAuthority(url: &mut QUrl, authority: &QString);
126#[rust_name = "qurl_set_fragment"]
127fn qurlSetFragment(url: &mut QUrl, fragment: &QString);
128#[rust_name = "qurl_set_host"]
129fn qurlSetHost(url: &mut QUrl, host: &QString);
130#[rust_name = "qurl_set_idn_whitelist"]
131fn qurlSetIdnWhitelist(list: &QStringList);
132#[rust_name = "qurl_set_password"]
133fn qurlSetPassword(url: &mut QUrl, password: &QString);
134#[rust_name = "qurl_set_path"]
135fn qurlSetPath(url: &mut QUrl, path: &QString);
136#[rust_name = "qurl_set_query"]
137fn qurlSetQuery(url: &mut QUrl, query: &QString);
138#[rust_name = "qurl_set_scheme"]
139fn qurlSetScheme(url: &mut QUrl, scheme: &QString);
140#[rust_name = "qurl_set_url"]
141fn qurlSetUrl(url: &mut QUrl, new_url: &QString);
142#[rust_name = "qurl_set_user_info"]
143fn qurlSetUserInfo(url: &mut QUrl, user_info: &QString);
144#[rust_name = "qurl_set_user_name"]
145fn qurlSetUserName(url: &mut QUrl, user_name: &QString);
146#[rust_name = "qurl_to_display_string"]
147fn qurlToDisplayString(url: &QUrl) -> QString;
148#[rust_name = "qurl_to_encoded"]
149fn qurlToEncoded(url: &QUrl) -> QByteArray;
150#[rust_name = "qurl_to_percent_encoding"]
151fn qurlToPercentEncoding(
152 input: &QString,
153 exclude: &QByteArray,
154 include: &QByteArray,
155 ) -> QByteArray;
156#[doc(hidden)]
157 #[rust_name = "qurl_to_qstring"]
158fn qurlToQString(url: &QUrl) -> QString;
159#[rust_name = "qurl_user_info"]
160fn qurlUserInfo(url: &QUrl) -> QString;
161#[rust_name = "qurl_user_name"]
162fn qurlUserName(url: &QUrl) -> QString;
163 }
164165#[namespace = "rust::cxxqtlib1"]
166unsafe extern "C++" {
167include!("cxx-qt-lib/common.h");
168169#[doc(hidden)]
170 #[rust_name = "qurl_drop"]
171fn drop(url: &mut QUrl);
172173#[doc(hidden)]
174 #[rust_name = "qurl_init_default"]
175fn construct() -> QUrl;
176#[doc(hidden)]
177 #[rust_name = "qurl_init_from_qstring"]
178fn construct(string: &QString) -> QUrl;
179#[doc(hidden)]
180 #[rust_name = "qurl_init_from_qurl"]
181fn construct(url: &QUrl) -> QUrl;
182183#[doc(hidden)]
184 #[rust_name = "qurl_eq"]
185fn operatorEq(a: &QUrl, b: &QUrl) -> bool;
186187#[doc(hidden)]
188 #[rust_name = "qurl_debug"]
189fn toQString(url: &QUrl) -> QString;
190 }
191}
192193/// The QUrl class provides a convenient interface for working with URLs.
194#[repr(C)]
195pub struct QUrl {
196 _space: MaybeUninit<usize>,
197}
198199impl QUrl {
200/// Returns the authority of the URL if it is defined; otherwise an empty string is returned.
201pub fn authority_or_default(&self) -> ffi::QString {
202 ffi::qurl_authority(self)
203 }
204205/// Returns the name of the file, excluding the directory path.
206 ///
207 /// Note that, if this QUrl object is given a path ending in a slash, the name of the file is considered empty.
208 ///
209 /// If the path doesn't contain any slash, it is fully returned as the fileName.
210pub fn file_name(&self) -> ffi::QString {
211 ffi::qurl_file_name(self)
212 }
213214/// Returns the fragment of the URL.
215pub fn fragment(&self) -> Option<ffi::QString> {
216if self.has_fragment() {
217Some(self.fragment_or_default())
218 } else {
219None
220}
221 }
222223/// Returns the fragment of the URL if it is defined; otherwise an empty string is returned.
224pub fn fragment_or_default(&self) -> ffi::QString {
225 ffi::qurl_fragment(self)
226 }
227228/// Parses input and returns the corresponding QUrl. input is assumed to be in encoded form, containing only ASCII characters.
229pub fn from_encoded(input: &ffi::QByteArray) -> Self {
230 ffi::qurl_from_encoded(input)
231 }
232233/// Returns a QUrl representation of localFile, interpreted as a local file.
234 /// This function accepts paths separated by slashes as well as the native separator for this platform.
235pub fn from_local_file(local_file: &ffi::QString) -> Self {
236 ffi::qurl_from_local_file(local_file)
237 }
238239/// Returns a decoded copy of input. input is first decoded from percent encoding,
240 /// then converted from UTF-8 to unicode.
241pub fn from_percent_encoding(input: &ffi::QByteArray) -> ffi::QString {
242 ffi::qurl_from_percent_encoding(input)
243 }
244245/// Returns a valid URL from a user supplied userInput string if one can be deduced.
246 /// In the case that is not possible, an invalid QUrl() is returned.
247pub fn from_user_input(user_input: &ffi::QString, working_directory: &ffi::QString) -> Self {
248 ffi::qurl_from_user_input(user_input, working_directory)
249 }
250251/// Returns the host of the URL if it is defined; otherwise an empty string is returned.
252pub fn host_or_default(&self) -> ffi::QString {
253 ffi::qurl_host(self)
254 }
255256/// Returns the current whitelist of top-level domains that are allowed to have non-ASCII characters in their compositions.
257pub fn idn_whitelist() -> ffi::QStringList {
258 ffi::qurl_idn_whitelist()
259 }
260261/// Returns the password of the URL if it is defined; otherwise an empty string is returned.
262pub fn password_or_default(&self) -> ffi::QString {
263 ffi::qurl_password(self)
264 }
265266/// Returns the path of the URL.
267pub fn path(&self) -> ffi::QString {
268 ffi::qurl_path(self)
269 }
270271/// Returns the query string of the URL if there's a query string
272pub fn query(&self) -> Option<ffi::QString> {
273if self.has_query() {
274Some(self.query_or_default())
275 } else {
276None
277}
278 }
279280/// Returns the query string of the URL if it is defined; otherwise an empty string is returned.
281pub fn query_or_default(&self) -> ffi::QString {
282 ffi::qurl_query(self)
283 }
284285/// Returns the scheme of the URL. If the Option is None,
286 /// this means the scheme is undefined and the URL is then relative.
287 ///
288 /// The scheme can only contain US-ASCII letters or digits,
289 /// which means it cannot contain any character that would otherwise require encoding
290 /// Additionally, schemes are always returned in lowercase form.
291pub fn scheme(&self) -> Option<ffi::QString> {
292let scheme = self.scheme_or_default();
293if scheme.is_empty() {
294None
295} else {
296Some(scheme)
297 }
298 }
299300/// Sets the authority of the URL to authority.
301pub fn set_authority(&mut self, authority: &ffi::QString) {
302 ffi::qurl_set_authority(self, authority)
303 }
304305/// Sets the fragment of the URL to fragment.
306 /// The fragment is the last part of the URL, represented by a '#' followed by a string of characters.
307pub fn set_fragment(&mut self, fragment: &ffi::QString) {
308 ffi::qurl_set_fragment(self, fragment)
309 }
310311/// Sets the host of the URL to host. The host is part of the authority.
312pub fn set_host(&mut self, host: &ffi::QString) {
313 ffi::qurl_set_host(self, host)
314 }
315316/// Sets the whitelist of Top-Level Domains (TLDs) that are allowed to have non-ASCII characters in domains to the value of list.
317pub fn set_idn_whitelist(list: &ffi::QStringList) {
318 ffi::qurl_set_idn_whitelist(list)
319 }
320321/// Sets the URL's password to password.
322pub fn set_password(&mut self, password: &ffi::QString) {
323 ffi::qurl_set_password(self, password)
324 }
325326/// Sets the path of the URL to path.
327 /// The path is the part of the URL that comes after the authority but before the query string.
328pub fn set_path(&mut self, path: &ffi::QString) {
329 ffi::qurl_set_path(self, path)
330 }
331332/// Sets the query string of the URL to query.
333pub fn set_query(&mut self, query: &ffi::QString) {
334 ffi::qurl_set_query(self, query)
335 }
336337/// Sets the scheme of the URL to scheme. As a scheme can only contain ASCII characters,
338 /// no conversion or decoding is done on the input. It must also start with an ASCII letter.
339pub fn set_scheme(&mut self, scheme: &ffi::QString) {
340 ffi::qurl_set_scheme(self, scheme)
341 }
342343/// Parses url and sets this object to that value.
344 /// QUrl will automatically percent encode all characters that are not allowed in a URL
345 /// and decode the percent-encoded sequences that represent an unreserved character
346 /// (letters, digits, hyphens, underscores, dots and tildes).
347 /// All other characters are left in their original forms.
348pub fn set_url(&mut self, url: &ffi::QString) {
349 ffi::qurl_set_url(self, url)
350 }
351352/// Sets the user info of the URL to userInfo.
353pub fn set_user_info(&mut self, user_info: &ffi::QString) {
354 ffi::qurl_set_user_info(self, user_info)
355 }
356357/// Sets the URL's user name to userName.
358pub fn set_user_name(&mut self, user_name: &ffi::QString) {
359 ffi::qurl_set_user_name(self, user_name)
360 }
361362/// Returns a human-displayable string representation of the URL.
363 /// The option RemovePassword is always enabled, since passwords should never be shown back to users.
364pub fn to_display_string(&self) -> ffi::QString {
365 ffi::qurl_to_display_string(self)
366 }
367368/// Returns the encoded representation of the URL if it's valid; otherwise an empty QByteArray is returned.
369pub fn to_encoded(&self) -> ffi::QByteArray {
370 ffi::qurl_to_encoded(self)
371 }
372373/// Returns the path of this URL formatted as a local file path.
374 /// The path returned will use forward slashes, even if it was originally created from one with backslashes.
375pub fn to_local_file(&self) -> Option<ffi::QString> {
376if self.is_local_file() {
377Some(self.to_local_file_or_default())
378 } else {
379None
380}
381 }
382383/// Returns an encoded copy of input. input is first converted to UTF-8,
384 /// and all ASCII-characters that are not in the unreserved group are percent encoded.
385 /// To prevent characters from being percent encoded pass them to exclude.
386 /// To force characters to be percent encoded pass them to include.
387pub fn to_percent_encoding(
388 input: &ffi::QString,
389 exclude: &ffi::QByteArray,
390 include: &ffi::QByteArray,
391 ) -> ffi::QByteArray {
392 ffi::qurl_to_percent_encoding(input, exclude, include)
393 }
394395/// Returns a QString representation of the URL.
396pub fn to_qstring(&self) -> ffi::QString {
397 ffi::qurl_to_qstring(self)
398 }
399400/// Returns the user info of the URL, or an empty string if the user info is undefined.
401pub fn user_info_or_default(&self) -> ffi::QString {
402 ffi::qurl_user_info(self)
403 }
404405/// Returns the user name of the URL if it is defined; otherwise an empty string is returned.
406pub fn user_name_or_default(&self) -> ffi::QString {
407 ffi::qurl_user_name(self)
408 }
409}
410411impl Clone for QUrl {
412/// Constructs a copy of other.
413fn clone(&self) -> Self {
414 ffi::qurl_init_from_qurl(self)
415 }
416}
417418impl Default for QUrl {
419/// Constructs an empty QUrl object.
420fn default() -> Self {
421 ffi::qurl_init_default()
422 }
423}
424425impl std::cmp::PartialEq for QUrl {
426fn eq(&self, other: &Self) -> bool {
427 ffi::qurl_eq(self, other)
428 }
429}
430431impl std::cmp::Eq for QUrl {}
432433impl fmt::Display for QUrl {
434/// Convert the QUrl to a Rust string
435 ///
436 /// Note that this converts from UTF-16 to UTF-8
437fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
438write!(f, "{}", ffi::qurl_to_rust_string(self))
439 }
440}
441442impl fmt::Debug for QUrl {
443fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
444write!(f, "{}", ffi::qurl_debug(self))
445 }
446}
447448impl Drop for QUrl {
449/// Destructor; called immediately before the object is deleted.
450fn drop(&mut self) {
451 ffi::qurl_drop(self)
452 }
453}
454455impl From<&ffi::QString> for QUrl {
456/// Constructs a QUrl from a QString
457fn from(str: &ffi::QString) -> Self {
458 ffi::qurl_init_from_qstring(str)
459 }
460}
461462impl From<&str> for QUrl {
463/// Constructs a QUrl from a Rust string
464 ///
465 /// Note that this converts from UTF-8 to UTF-16
466fn from(str: &str) -> Self {
467 ffi::qurl_init_from_string(str)
468 }
469}
470471impl From<&String> for QUrl {
472/// Constructs a QUrl from a Rust string
473 ///
474 /// Note that this converts from UTF-8 to UTF-16
475fn from(str: &String) -> Self {
476 ffi::qurl_init_from_string(str)
477 }
478}
479480#[cfg(feature = "http")]
481impl From<&http::Uri> for QUrl {
482fn from(value: &http::Uri) -> Self {
483 QUrl::from(&value.to_string())
484 }
485}
486487#[cfg(feature = "http")]
488impl TryFrom<&QUrl> for http::Uri {
489type Error = http::uri::InvalidUri;
490491fn try_from(value: &QUrl) -> Result<Self, Self::Error> {
492 value.to_string().parse::<http::Uri>()
493 }
494}
495496#[cfg(feature = "url")]
497impl From<&url::Url> for QUrl {
498fn from(value: &url::Url) -> Self {
499 QUrl::from(&value.to_string())
500 }
501}
502503#[cfg(feature = "url")]
504impl TryFrom<&QUrl> for url::Url {
505type Error = url::ParseError;
506507fn try_from(value: &QUrl) -> Result<Self, Self::Error> {
508 url::Url::parse(value.to_string().as_str())
509 }
510}
511512// Safety:
513//
514// Static checks on the C++ side to ensure the size is the same.
515unsafe impl ExternType for QUrl {
516type Id = type_id!("QUrl");
517type Kind = cxx::kind::Trivial;
518}
519520#[cfg(test)]
521mod tests {
522#[cfg(any(feature = "http", feature = "url"))]
523use super::*;
524525#[cfg(feature = "http")]
526 #[test]
527fn test_http() {
528let uri = "https://github.com/kdab/cxx-qt"
529.parse::<http::Uri>()
530 .unwrap();
531let qurl = QUrl::from(&uri);
532assert_eq!(uri.to_string(), qurl.to_string());
533534let http_uri = http::Uri::try_from(&qurl).unwrap();
535assert_eq!(http_uri, uri);
536 }
537538#[cfg(feature = "url")]
539 #[test]
540fn test_url() {
541let url = url::Url::parse("https://github.com/kdab/cxx-qt").unwrap();
542let qurl = QUrl::from(&url);
543assert_eq!(url.to_string(), qurl.to_string());
544545let url_url = url::Url::try_from(&qurl).unwrap();
546assert_eq!(url_url, url);
547 }
548}