read_url/context/
blocking.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] 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](UrlContext::url_or_file_path).
19    pub fn url(self: &UrlContextRef, url_representation: &str) -> Result<UrlRef, UrlError> {
20        self.url_or_maybe_file_path(url_representation, false)
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] 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 fn url_or_file_path(self: &UrlContextRef, url_or_file_path_representation: &str) -> Result<UrlRef, UrlError> {
41        self.url_or_maybe_file_path(url_or_file_path_representation, true)
42    }
43
44    fn url_or_maybe_file_path(
45        self: &UrlContextRef,
46        url_or_file_path_representation: &str,
47        or_file_path: bool,
48    ) -> Result<UrlRef, UrlError> {
49        let url_or_file_path_representation = self.get_url_or_override(url_or_file_path_representation.into())?;
50        match url::Url::parse(&url_or_file_path_representation) {
51            Ok(url) => match url.scheme() {
52                "internal" => {
53                    let (query, fragment) = url_query_and_fragment(&url);
54                    let mut url =
55                        self.internal_url(url.path().into(), url.host_str().map(|host| host.into()), query, fragment);
56                    url.conform()?;
57                    Ok(url)
58                }
59
60                #[cfg(feature = "file")]
61                "file" => {
62                    let (query, fragment) = url_query_and_fragment(&url);
63                    let mut url =
64                        self.file_url(url.path().into(), url.host_str().map(|host| host.into()), query, fragment);
65                    url.conform()?;
66                    Ok(url)
67                }
68
69                #[cfg(feature = "http")]
70                "http" | "https" => {
71                    let mut url = self.http_url(url);
72                    url.conform()?;
73                    Ok(url)
74                }
75
76                #[cfg(feature = "tar")]
77                "tar" => {
78                    use super::super::tar::*;
79
80                    let (archive_url_representation, path) = TarUrl::parse(url.as_str())?;
81                    let archive_url = self.url_or_maybe_file_path(&archive_url_representation, or_file_path)?;
82                    let compression = TarUrl::compression_from(&archive_url)?;
83                    let mut url = self.tar_url(archive_url, path.into(), compression);
84                    url.conform()?;
85                    Ok(url)
86                }
87
88                #[cfg(feature = "zip")]
89                "zip" => {
90                    use super::super::zip::*;
91
92                    let (repository_url_representation, path) = ZipUrl::parse(url.as_str())?;
93                    let repository_url = self.url_or_maybe_file_path(&repository_url_representation, or_file_path)?;
94                    let mut url = self.zip_url(repository_url, path.into());
95                    url.conform()?;
96                    Ok(url)
97                }
98
99                #[cfg(feature = "git")]
100                "git" => {
101                    use super::super::git::*;
102
103                    let (repository_url_representation, path) = GitUrl::parse(url.as_str())?;
104                    let repository_url = self.url_or_maybe_file_path(&repository_url_representation, or_file_path)?;
105                    let mut url = self.git_url(repository_url, path.into())?;
106                    url.conform()?;
107                    Ok(url)
108                }
109
110                scheme => Err(UrlError::UnsupportedScheme(scheme.into())),
111            },
112
113            // Not a URL
114            Err(_) => {
115                if or_file_path {
116                    // Maybe it's an absolute file path
117                    #[cfg(feature = "file")]
118                    {
119                        use std::path::*;
120
121                        let path = Path::new(&url_or_file_path_representation);
122                        if path.is_absolute() {
123                            let mut url = self.file_url(path.into(), None, None, None);
124                            url.conform()?;
125                            return Ok(url);
126                        }
127                    }
128                }
129
130                // Try as relative
131                for base_url in self.base_urls.iter() {
132                    let mut url = base_url.relative(&url_or_file_path_representation);
133                    if url.conform().is_ok() {
134                        return Ok(url);
135                    }
136                }
137
138                Err(UrlError::new_io_not_found(url_or_file_path_representation))
139            }
140        }
141    }
142}