1#![no_std]
2
3extern crate alloc;
4
5#[cfg(any(feature = "std", test))]
6extern crate std;
7
8mod location;
9mod selection;
10mod source_file;
11mod source_manager;
12mod span;
13
14#[cfg(feature = "arbitrary")]
15use alloc::vec;
16use alloc::{string::String, sync::Arc};
17
18use miden_crypto::utils::{
19 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
20};
21#[cfg(feature = "arbitrary")]
22use proptest::prelude::*;
23#[cfg(feature = "serde")]
24use serde::{Deserialize, Serialize};
25#[cfg(feature = "serde")]
26pub use serde_spanned;
27
28#[cfg(feature = "std")]
29pub use self::source_manager::SourceManagerExt;
30pub use self::{
31 location::{FileLineCol, Location},
32 selection::{Position, Selection},
33 source_file::{
34 ByteIndex, ByteOffset, ColumnIndex, ColumnNumber, LineIndex, LineNumber, SourceContent,
35 SourceContentUpdateError, SourceFile, SourceFileRef, SourceLanguage,
36 },
37 source_manager::{DefaultSourceManager, SourceId, SourceManager, SourceManagerSync},
38 span::{SourceSpan, Span, Spanned},
39};
40
41#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
48#[cfg_attr(
49 all(feature = "arbitrary", test),
50 miden_test_serde_macros::serde_test(binary_serde(true))
51)]
52pub struct Uri(Arc<str>);
53
54impl Uri {
55 pub fn new(uri: impl AsRef<str>) -> Self {
56 uri.as_ref().into()
57 }
58
59 #[inline]
60 pub fn as_str(&self) -> &str {
61 self.0.as_ref()
62 }
63
64 #[inline]
65 pub fn as_bytes(&self) -> &[u8] {
66 self.0.as_bytes()
67 }
68
69 pub fn scheme(&self) -> Option<&str> {
71 match self.0.split_once(':') {
72 Some((prefix, _))
73 if prefix.contains(|c: char| {
74 !c.is_ascii_alphanumeric() && !matches!(c, '+' | '-' | '.')
75 }) =>
76 {
77 None
78 },
79 Some((prefix, _)) => Some(prefix),
80 None => None,
81 }
82 }
83
84 pub fn authority(&self) -> Option<&str> {
86 let uri = self.0.as_ref();
87 let (_, rest) = uri.split_once("//")?;
88 match rest.split_once(['/', '?', '#']) {
89 Some((authority, _)) => Some(authority),
90 None => Some(rest),
91 }
92 }
93
94 pub fn path(&self) -> &str {
96 let uri = self.0.as_ref();
97 let path = match uri.split_once("//") {
98 Some((_, rest)) => match rest.find('/').map(|pos| rest.split_at(pos)) {
99 Some((_, path)) => path,
100 None => return "",
101 },
102 None => match uri.split_once(':') {
103 Some((prefix, _))
104 if prefix.contains(|c: char| {
105 !c.is_ascii_alphanumeric() && !matches!(c, '+' | '-' | '.')
106 }) =>
107 {
108 uri
109 },
110 Some((_, path)) => path,
111 None => uri,
112 },
113 };
114 match path.split_once(['?', '#']) {
115 Some((path, _)) => path,
116 None => path,
117 }
118 }
119}
120
121impl core::fmt::Display for Uri {
122 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123 core::fmt::Display::fmt(&self.0, f)
124 }
125}
126
127impl AsRef<str> for Uri {
128 fn as_ref(&self) -> &str {
129 self.0.as_ref()
130 }
131}
132
133impl From<&str> for Uri {
134 #[inline]
135 fn from(value: &str) -> Self {
136 use alloc::string::ToString;
137
138 value.to_string().into()
139 }
140}
141
142impl From<Uri> for Arc<str> {
143 fn from(value: Uri) -> Self {
144 value.0
145 }
146}
147
148impl From<Arc<str>> for Uri {
149 #[inline]
150 fn from(uri: Arc<str>) -> Self {
151 Self(uri)
152 }
153}
154
155impl From<alloc::boxed::Box<str>> for Uri {
156 #[inline]
157 fn from(uri: alloc::boxed::Box<str>) -> Self {
158 Self(uri.into())
159 }
160}
161
162impl From<String> for Uri {
163 #[inline]
164 fn from(uri: String) -> Self {
165 Self(uri.into_boxed_str().into())
166 }
167}
168
169#[cfg(feature = "std")]
170impl<'a> From<&'a std::path::Path> for Uri {
171 fn from(path: &'a std::path::Path) -> Self {
172 use alloc::string::ToString;
173
174 Self::from(path.display().to_string())
175 }
176}
177
178impl Serializable for Uri {
179 fn write_into<W: ByteWriter>(&self, target: &mut W) {
180 self.as_str().write_into(target);
181 }
182}
183
184impl Deserializable for Uri {
185 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
186 String::read_from(source).map(Self::from)
187 }
188}
189
190impl core::str::FromStr for Uri {
191 type Err = ();
192
193 fn from_str(s: &str) -> Result<Self, Self::Err> {
194 Ok(Self::from(s))
195 }
196}
197
198#[cfg(feature = "arbitrary")]
199impl Arbitrary for Uri {
200 type Parameters = ();
201 type Strategy = BoxedStrategy<Self>;
202
203 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
204 use alloc::string::String;
205
206 proptest::collection::vec(
207 proptest::prop_oneof![
208 proptest::char::range('a', 'z'),
209 proptest::char::range('A', 'Z'),
210 proptest::char::range('0', '9'),
211 Just('/'),
212 Just(':'),
213 Just('.'),
214 Just('-'),
215 Just('_'),
216 Just('#'),
217 Just('?'),
218 Just('@'),
219 ],
220 1..48,
221 )
222 .prop_map(|chars| Self::from(chars.into_iter().collect::<String>()))
223 .boxed()
224 }
225}
226
227#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn uri_scheme_extraction() {
236 let relative_file = Uri::new("foo.masm");
237 let relative_file_path = Uri::new("./foo.masm");
238 let relative_file_path_with_scheme = Uri::new("file:foo.masm");
239 let absolute_file_path = Uri::new("file:///tmp/foo.masm");
240 let http_simple_uri = Uri::new("http://www.example.com");
241 let http_simple_uri_with_userinfo = Uri::new("http://foo:bar@www.example.com");
242 let http_simple_uri_with_userinfo_and_port = Uri::new("http://foo:bar@www.example.com:443");
243 let http_simple_uri_with_userinfo_and_path =
244 Uri::new("http://foo:bar@www.example.com/api/v1");
245 let http_simple_uri_with_userinfo_and_query =
246 Uri::new("http://foo:bar@www.example.com?param=1");
247 let http_simple_uri_with_userinfo_and_fragment =
248 Uri::new("http://foo:bar@www.example.com#about");
249 let http_simple_uri_with_userinfo_and_path_and_query =
250 Uri::new("http://foo:bar@www.example.com/api/v1/user?id=1");
251 let http_simple_uri_with_userinfo_and_path_and_query_and_fragment =
252 Uri::new("http://foo:bar@www.example.com/api/v1/user?id=1#redirect=/home");
253
254 assert_eq!(relative_file.scheme(), None);
255 assert_eq!(relative_file_path.scheme(), None);
256 assert_eq!(relative_file_path_with_scheme.scheme(), Some("file"));
257 assert_eq!(absolute_file_path.scheme(), Some("file"));
258 assert_eq!(http_simple_uri.scheme(), Some("http"));
259 assert_eq!(http_simple_uri_with_userinfo.scheme(), Some("http"));
260 assert_eq!(http_simple_uri_with_userinfo_and_port.scheme(), Some("http"));
261 assert_eq!(http_simple_uri_with_userinfo_and_path.scheme(), Some("http"));
262 assert_eq!(http_simple_uri_with_userinfo_and_query.scheme(), Some("http"));
263 assert_eq!(http_simple_uri_with_userinfo_and_fragment.scheme(), Some("http"));
264 assert_eq!(http_simple_uri_with_userinfo_and_path_and_query.scheme(), Some("http"));
265 assert_eq!(
266 http_simple_uri_with_userinfo_and_path_and_query_and_fragment.scheme(),
267 Some("http")
268 );
269 }
270
271 #[test]
272 fn uri_authority_extraction() {
273 let relative_file = Uri::new("foo.masm");
274 let relative_file_path = Uri::new("./foo.masm");
275 let relative_file_path_with_scheme = Uri::new("file:foo.masm");
276 let absolute_file_path = Uri::new("file:///tmp/foo.masm");
277 let http_simple_uri = Uri::new("http://www.example.com");
278 let http_simple_uri_with_userinfo = Uri::new("http://foo:bar@www.example.com");
279 let http_simple_uri_with_userinfo_and_port = Uri::new("http://foo:bar@www.example.com:443");
280 let http_simple_uri_with_userinfo_and_path =
281 Uri::new("http://foo:bar@www.example.com/api/v1");
282 let http_simple_uri_with_userinfo_and_query =
283 Uri::new("http://foo:bar@www.example.com?param=1");
284 let http_simple_uri_with_userinfo_and_fragment =
285 Uri::new("http://foo:bar@www.example.com#about");
286 let http_simple_uri_with_userinfo_and_path_and_query =
287 Uri::new("http://foo:bar@www.example.com/api/v1/user?id=1");
288 let http_simple_uri_with_userinfo_and_path_and_query_and_fragment =
289 Uri::new("http://foo:bar@www.example.com/api/v1/user?id=1#redirect=/home");
290
291 assert_eq!(relative_file.authority(), None);
292 assert_eq!(relative_file_path.authority(), None);
293 assert_eq!(relative_file_path_with_scheme.authority(), None);
294 assert_eq!(absolute_file_path.authority(), Some(""));
295 assert_eq!(http_simple_uri.authority(), Some("www.example.com"));
296 assert_eq!(http_simple_uri_with_userinfo.authority(), Some("foo:bar@www.example.com"));
297 assert_eq!(
298 http_simple_uri_with_userinfo_and_port.authority(),
299 Some("foo:bar@www.example.com:443")
300 );
301 assert_eq!(
302 http_simple_uri_with_userinfo_and_path.authority(),
303 Some("foo:bar@www.example.com")
304 );
305 assert_eq!(
306 http_simple_uri_with_userinfo_and_query.authority(),
307 Some("foo:bar@www.example.com")
308 );
309 assert_eq!(
310 http_simple_uri_with_userinfo_and_fragment.authority(),
311 Some("foo:bar@www.example.com")
312 );
313 assert_eq!(
314 http_simple_uri_with_userinfo_and_path_and_query.authority(),
315 Some("foo:bar@www.example.com")
316 );
317 assert_eq!(
318 http_simple_uri_with_userinfo_and_path_and_query_and_fragment.authority(),
319 Some("foo:bar@www.example.com")
320 );
321 }
322
323 #[test]
324 fn uri_path_extraction() {
325 let relative_file = Uri::new("foo.masm");
326 let relative_file_path = Uri::new("./foo.masm");
327 let relative_file_path_with_scheme = Uri::new("file:foo.masm");
328 let absolute_file_path = Uri::new("file:///tmp/foo.masm");
329 let http_simple_uri = Uri::new("http://www.example.com");
330 let http_simple_uri_with_userinfo = Uri::new("http://foo:bar@www.example.com");
331 let http_simple_uri_with_userinfo_and_port = Uri::new("http://foo:bar@www.example.com:443");
332 let http_simple_uri_with_userinfo_and_path =
333 Uri::new("http://foo:bar@www.example.com/api/v1");
334 let http_simple_uri_with_userinfo_and_query =
335 Uri::new("http://foo:bar@www.example.com?param=1");
336 let http_simple_uri_with_userinfo_and_fragment =
337 Uri::new("http://foo:bar@www.example.com#about");
338 let http_simple_uri_with_userinfo_and_path_and_query =
339 Uri::new("http://foo:bar@www.example.com/api/v1/user?id=1");
340 let http_simple_uri_with_userinfo_and_path_and_query_and_fragment =
341 Uri::new("http://foo:bar@www.example.com/api/v1/user?id=1#redirect=/home");
342
343 assert_eq!(relative_file.path(), "foo.masm");
344 assert_eq!(relative_file_path.path(), "./foo.masm");
345 assert_eq!(relative_file_path_with_scheme.path(), "foo.masm");
346 assert_eq!(absolute_file_path.path(), "/tmp/foo.masm");
347 assert_eq!(http_simple_uri.path(), "");
348 assert_eq!(http_simple_uri_with_userinfo.path(), "");
349 assert_eq!(http_simple_uri_with_userinfo_and_port.path(), "");
350 assert_eq!(http_simple_uri_with_userinfo_and_path.path(), "/api/v1");
351 assert_eq!(http_simple_uri_with_userinfo_and_query.path(), "");
352 assert_eq!(http_simple_uri_with_userinfo_and_fragment.path(), "");
353 assert_eq!(http_simple_uri_with_userinfo_and_path_and_query.path(), "/api/v1/user");
354 assert_eq!(
355 http_simple_uri_with_userinfo_and_path_and_query_and_fragment.path(),
356 "/api/v1/user"
357 );
358 }
359}