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