Skip to main content

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
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/// A [URI reference](https://datatracker.ietf.org/doc/html/rfc3986#section-4.1) that specifies
38/// the location of a source file, whether on disk, on the network, or elsewhere.
39#[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    /// Returns the scheme portion of this URI, if present.
59    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    /// Returns the scheme portion of this URI, if present.
74    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    /// Returns the path portion of this URI.
84    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}