miden_debug_types/
lib.rs

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