read_url/context/
asynchronous.rs

1use super::{
2    super::{errors::*, url::*, util::*},
3    context::*,
4};
5
6impl UrlContext {
7    /// Parses the argument as either an absolute URL or a path relative to
8    /// one of the context's base URls. Relative paths support ".." and ".".
9    ///
10    /// The returned URL will always have had [URL::conform_async] called on it, so
11    /// there is no need to call it again.
12    ///
13    /// Relative paths are tested against the base URLs argument in order. The
14    /// first valid URL will be returned and the remaining bases will be ignored.
15    /// Note that bases can be any of any URL type.
16    ///
17    /// If you are expecting either a URL or a file path, consider
18    /// [url_or_file_path_async](UrlContext::url_or_file_path_async).
19    pub async fn url_async(self: &UrlContextRef, url_representation: &str) -> Result<UrlRef, UrlError> {
20        self.url_or_maybe_file_path_async(url_representation, false).await
21    }
22
23    /// Parses the argument as an absolute URL, or an absolute file path, or a
24    /// path relative to one of the context's base URLs. Relative paths support
25    /// ".." and ".".
26    ///
27    /// The returned URL will always have had [URL::conform_async] called on it, so
28    /// there is no need to call it again.
29    ///
30    /// Relative paths are tested against the base URLs argument in order. The
31    /// first valid URL will be returned and the remaining bases will be ignored.
32    /// Note that bases can be any of any URL type.
33    ///
34    /// On Windows note a rare edge case: If there happens to be a drive that has the
35    /// same name as a supported URL scheme (e.g. "http") then callers would have to
36    /// provide a full file URL, e.g. instead of "http:\Dir\file" provide
37    /// "file:///http:/Dir/file". Otherwise it would be parsed as a URL of that scheme.
38    /// rather than a file path.
39    #[cfg(feature = "file")]
40    pub async fn url_or_file_path_async(
41        self: &UrlContextRef,
42        url_or_file_path_representation: &str,
43    ) -> Result<UrlRef, UrlError> {
44        self.url_or_maybe_file_path_async(url_or_file_path_representation, true).await
45    }
46
47    async fn url_or_maybe_file_path_async(
48        self: &UrlContextRef,
49        url_or_file_path_representation: &str,
50        or_file_path: bool,
51    ) -> Result<UrlRef, UrlError> {
52        let url_or_file_path_representation = self.get_url_or_override(url_or_file_path_representation.into())?;
53        match url::Url::parse(&url_or_file_path_representation) {
54            Ok(url) => match url.scheme() {
55                "internal" => {
56                    let (query, fragment) = url_query_and_fragment(&url);
57                    let url =
58                        self.internal_url(url.path().into(), url.host_str().map(|host| host.into()), query, fragment);
59                    Ok(url.conform_async()?.await?)
60                }
61
62                #[cfg(feature = "file")]
63                "file" => {
64                    let (query, fragment) = url_query_and_fragment(&url);
65                    let url = self.file_url(url.path().into(), url.host_str().map(|host| host.into()), query, fragment);
66                    Ok(url.conform_async()?.await?)
67                }
68
69                #[cfg(feature = "http")]
70                "http" | "https" => {
71                    let url = self.http_url(url);
72                    Ok(url.conform_async()?.await?)
73                }
74
75                #[cfg(feature = "tar")]
76                "tar" => {
77                    use super::super::tar::*;
78
79                    let (archive_url_representation, path) = TarUrl::parse(url.as_str())?;
80
81                    // Box::pin to allow recursion
82                    let archive_url =
83                        Box::pin(self.url_or_maybe_file_path_async(&archive_url_representation, or_file_path)).await?;
84
85                    let compression = TarUrl::compression_from(&archive_url)?;
86                    let url = self.tar_url(archive_url, path.into(), compression);
87                    Ok(url.conform_async()?.await?)
88                }
89
90                #[cfg(feature = "zip")]
91                "zip" => {
92                    use super::super::zip::*;
93
94                    let (archive_url_representation, path) = ZipUrl::parse(url.as_str())?;
95
96                    // Box::pin to allow recursion
97                    let archive_url =
98                        Box::pin(self.url_or_maybe_file_path_async(&archive_url_representation, or_file_path)).await?;
99
100                    let url = self.zip_url(archive_url, path.into());
101                    Ok(url.conform_async()?.await?)
102                }
103
104                #[cfg(feature = "git")]
105                "git" => {
106                    use super::super::git::*;
107
108                    let (repository_url_representation, path) = GitUrl::parse(url.as_str())?;
109                    let repository_url = self.absolute_url(&repository_url_representation)?;
110                    let url = self.git_url(repository_url, path.into())?;
111                    Ok(url.conform_async()?.await?)
112                }
113
114                scheme => Err(UrlError::UnsupportedScheme(scheme.into())),
115            },
116
117            // Not a URL
118            Err(_) => {
119                if or_file_path {
120                    // Maybe it's an absolute file path
121                    #[cfg(feature = "file")]
122                    {
123                        use std::path::*;
124
125                        let path = Path::new(&url_or_file_path_representation);
126                        if path.is_absolute() {
127                            let url = self.file_url(path.into(), None, None, None);
128                            return Ok(url.conform_async()?.await?);
129                        }
130                    }
131                }
132
133                // Try as relative
134                for base_url in self.base_urls.iter() {
135                    let url = base_url.relative(&url_or_file_path_representation);
136                    if let Ok(url) = url.conform_async()?.await {
137                        return Ok(url);
138                    }
139                }
140
141                Err(UrlError::new_io_not_found(url_or_file_path_representation))
142            }
143        }
144    }
145}