miden_debug_types/
lib.rs

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