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