Skip to main content

fyrox_impl/resource/gltf/
uri.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21use base64::engine::general_purpose::URL_SAFE_NO_PAD as Base64Engine;
22use base64::engine::Engine as _;
23
24/// A breakdown of the data contained within a URI.
25#[derive(Copy, Clone, Debug)]
26pub struct Uri<'a> {
27    /// Reference to the full URI str.
28    pub original: &'a str,
29    /// The recognized scheme type, if any.
30    pub scheme: Scheme,
31    /// The text that was used to identify the scheme, preceeding the first ':', if any.
32    /// For example, if the URI were <https://www.somewebsite.org/books/RestInPractice.pdf>
33    /// then  `scheme_name` would be the "https" from the beginning.
34    pub scheme_name: Option<&'a str>,
35    /// Everything that follows the initial ':', or the whole original str if there is no ':'.
36    /// If the URI were <https://www.somewebsite.org/books/RestInPractice.pdf>
37    /// then `after_scheme` would be <www.somewebsite.org/books/RestInPractice.pdf>.
38    pub after_scheme: &'a str,
39    /// If the scheme is "data" then `data_type` is the slice between the first ':' and the first ','.
40    /// For example, if the URI were ""data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAF", then
41    /// `data_type` would be "image/png;base64". If there is no ',' then `data_type` is the same as
42    /// `after_scheme`. If the scheme is not "data" then `data_type` is None.
43    pub data_type: Option<&'a str>,
44    /// If the scheme is "data" then `data` is everything following the first ','.
45    /// `data_type` is None if the scheme is not "data" or if there is no ','.
46    pub data: Option<&'a str>,
47}
48
49/// One of the scheme types that the parser can recognize.
50#[derive(Copy, Clone, Eq, PartialEq, Debug)]
51pub enum Scheme {
52    /// A file URI indicates that this is an absolute path to a file.
53    /// This method of identifying files is not supported.
54    File,
55    /// A data URI contains image or model data encoded in base64.
56    Data,
57    /// There was no ':' to identify a scheme name, which means that this URI
58    /// will be interpreted as a relative file path and the file system will
59    /// be searched for the named asset.
60    None,
61    /// A scheme name was specified using a ':', but it was not among these
62    /// expected schemes.
63    Other,
64}
65
66/// Turn a URI str slice into a Uri structure containing the parts of the URI.
67pub fn parse_uri(source: &str) -> Uri {
68    let (scheme_name, after_scheme): (Option<&str>, &str) = {
69        let mut scheme_it = source.splitn(2, ':');
70        let first = scheme_it.next();
71        let second = scheme_it.next();
72        if let Some(rest) = second {
73            (first, rest)
74        } else {
75            (None, first.unwrap())
76        }
77    };
78    if scheme_name == Some("data") {
79        let mut it = after_scheme.splitn(2, ',');
80        Uri {
81            original: source,
82            scheme: Scheme::Data,
83            scheme_name,
84            after_scheme,
85            data_type: it.next(),
86            data: it.next(),
87        }
88    } else if let Some(name) = scheme_name {
89        let scheme = match name {
90            "file" => Scheme::File,
91            _ => Scheme::Other,
92        };
93        Uri {
94            original: source,
95            scheme,
96            scheme_name,
97            after_scheme,
98            data_type: None,
99            data: None,
100        }
101    } else {
102        Uri {
103            original: source,
104            scheme: Scheme::None,
105            scheme_name,
106            after_scheme,
107            data_type: None,
108            data: None,
109        }
110    }
111}
112
113pub fn decode_base64(source: &str) -> Result<Vec<u8>, base64::DecodeError> {
114    Base64Engine.decode(source)
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn split() {
123        let r: Vec<_> = "abc:def".splitn(2, ':').collect();
124        assert!(r == vec!["abc", "def"], "{r:?}");
125    }
126    #[test]
127    fn simple() {
128        let u: Uri = parse_uri("Random stuff");
129        assert!(
130            matches!(
131                u,
132                Uri {
133                    original: "Random stuff",
134                    scheme: Scheme::None,
135                    scheme_name: None,
136                    after_scheme: "Random stuff",
137                    data_type: None,
138                    data: None,
139                }
140            ),
141            "{u:?}"
142        )
143    }
144    #[test]
145    fn file() {
146        let u: Uri = parse_uri("file:filename.test");
147        assert!(
148            matches!(
149                u,
150                Uri {
151                    original: "file:filename.test",
152                    scheme: Scheme::File,
153                    scheme_name: Some("file"),
154                    after_scheme: "filename.test",
155                    data_type: None,
156                    data: None,
157                }
158            ),
159            "{u:?}"
160        )
161    }
162    #[test]
163    fn other() {
164        let u: Uri = parse_uri("what:Stuff");
165        assert!(
166            matches!(
167                u,
168                Uri {
169                    original: "what:Stuff",
170                    scheme: Scheme::Other,
171                    scheme_name: Some("what"),
172                    after_scheme: "Stuff",
173                    data_type: None,
174                    data: None,
175                }
176            ),
177            "{u:?}"
178        )
179    }
180    #[test]
181    fn data() {
182        let u: Uri = parse_uri("data:type,ABCDEFG");
183        assert!(
184            matches!(
185                u,
186                Uri {
187                    original: "data:type,ABCDEFG",
188                    scheme: Scheme::Data,
189                    scheme_name: Some("data"),
190                    after_scheme: "type,ABCDEFG",
191                    data_type: Some("type"),
192                    data: Some("ABCDEFG"),
193                }
194            ),
195            "{u:?}"
196        )
197    }
198}