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}