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
14#[cfg(feature = "arbitrary")]
15use alloc::vec;
16use alloc::{string::String, sync::Arc};
17
18use miden_crypto::utils::{
19    ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
20};
21#[cfg(feature = "arbitrary")]
22use proptest::prelude::*;
23#[cfg(feature = "serde")]
24use serde::{Deserialize, Serialize};
25#[cfg(feature = "serde")]
26pub use serde_spanned;
27
28#[cfg(feature = "std")]
29pub use self::source_manager::SourceManagerExt;
30pub use self::{
31    location::{FileLineCol, Location},
32    selection::{Position, Selection},
33    source_file::{
34        ByteIndex, ByteOffset, ColumnIndex, ColumnNumber, LineIndex, LineNumber, SourceContent,
35        SourceContentUpdateError, SourceFile, SourceFileRef, SourceLanguage,
36    },
37    source_manager::{DefaultSourceManager, SourceId, SourceManager, SourceManagerSync},
38    span::{SourceSpan, Span, Spanned},
39};
40
41// URI
42// ================================================================================================
43
44/// A [URI reference](https://datatracker.ietf.org/doc/html/rfc3986#section-4.1) that specifies
45/// the location of a source file, whether on disk, on the network, or elsewhere.
46#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
47#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
48#[cfg_attr(
49    all(feature = "arbitrary", test),
50    miden_test_serde_macros::serde_test(binary_serde(true))
51)]
52pub struct Uri(Arc<str>);
53
54impl Uri {
55    pub fn new(uri: impl AsRef<str>) -> Self {
56        uri.as_ref().into()
57    }
58
59    #[inline]
60    pub fn as_str(&self) -> &str {
61        self.0.as_ref()
62    }
63
64    #[inline]
65    pub fn as_bytes(&self) -> &[u8] {
66        self.0.as_bytes()
67    }
68
69    /// Returns the scheme portion of this URI, if present.
70    pub fn scheme(&self) -> Option<&str> {
71        match self.0.split_once(':') {
72            Some((prefix, _))
73                if prefix.contains(|c: char| {
74                    !c.is_ascii_alphanumeric() && !matches!(c, '+' | '-' | '.')
75                }) =>
76            {
77                None
78            },
79            Some((prefix, _)) => Some(prefix),
80            None => None,
81        }
82    }
83
84    /// Returns the authority portion of this URI, if present.
85    pub fn authority(&self) -> Option<&str> {
86        let uri = self.0.as_ref();
87        let (_, rest) = uri.split_once("//")?;
88        match rest.split_once(['/', '?', '#']) {
89            Some((authority, _)) => Some(authority),
90            None => Some(rest),
91        }
92    }
93
94    /// Returns the path portion of this URI.
95    pub fn path(&self) -> &str {
96        let uri = self.0.as_ref();
97        let path = match uri.split_once("//") {
98            Some((_, rest)) => match rest.find('/').map(|pos| rest.split_at(pos)) {
99                Some((_, path)) => path,
100                None => return "",
101            },
102            None => match uri.split_once(':') {
103                Some((prefix, _))
104                    if prefix.contains(|c: char| {
105                        !c.is_ascii_alphanumeric() && !matches!(c, '+' | '-' | '.')
106                    }) =>
107                {
108                    uri
109                },
110                Some((_, path)) => path,
111                None => uri,
112            },
113        };
114        match path.split_once(['?', '#']) {
115            Some((path, _)) => path,
116            None => path,
117        }
118    }
119}
120
121impl core::fmt::Display for Uri {
122    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123        core::fmt::Display::fmt(&self.0, f)
124    }
125}
126
127impl AsRef<str> for Uri {
128    fn as_ref(&self) -> &str {
129        self.0.as_ref()
130    }
131}
132
133impl From<&str> for Uri {
134    #[inline]
135    fn from(value: &str) -> Self {
136        use alloc::string::ToString;
137
138        value.to_string().into()
139    }
140}
141
142impl From<Uri> for Arc<str> {
143    fn from(value: Uri) -> Self {
144        value.0
145    }
146}
147
148impl From<Arc<str>> for Uri {
149    #[inline]
150    fn from(uri: Arc<str>) -> Self {
151        Self(uri)
152    }
153}
154
155impl From<alloc::boxed::Box<str>> for Uri {
156    #[inline]
157    fn from(uri: alloc::boxed::Box<str>) -> Self {
158        Self(uri.into())
159    }
160}
161
162impl From<String> for Uri {
163    #[inline]
164    fn from(uri: String) -> Self {
165        Self(uri.into_boxed_str().into())
166    }
167}
168
169#[cfg(feature = "std")]
170impl<'a> From<&'a std::path::Path> for Uri {
171    fn from(path: &'a std::path::Path) -> Self {
172        use alloc::string::ToString;
173
174        Self::from(path.display().to_string())
175    }
176}
177
178impl Serializable for Uri {
179    fn write_into<W: ByteWriter>(&self, target: &mut W) {
180        self.as_str().write_into(target);
181    }
182}
183
184impl Deserializable for Uri {
185    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
186        String::read_from(source).map(Self::from)
187    }
188}
189
190impl core::str::FromStr for Uri {
191    type Err = ();
192
193    fn from_str(s: &str) -> Result<Self, Self::Err> {
194        Ok(Self::from(s))
195    }
196}
197
198#[cfg(feature = "arbitrary")]
199impl Arbitrary for Uri {
200    type Parameters = ();
201    type Strategy = BoxedStrategy<Self>;
202
203    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
204        use alloc::string::String;
205
206        proptest::collection::vec(
207            proptest::prop_oneof![
208                proptest::char::range('a', 'z'),
209                proptest::char::range('A', 'Z'),
210                proptest::char::range('0', '9'),
211                Just('/'),
212                Just(':'),
213                Just('.'),
214                Just('-'),
215                Just('_'),
216                Just('#'),
217                Just('?'),
218                Just('@'),
219            ],
220            1..48,
221        )
222        .prop_map(|chars| Self::from(chars.into_iter().collect::<String>()))
223        .boxed()
224    }
225}
226
227// TESTS
228// ================================================================================================
229
230#[cfg(test)]
231mod tests {
232    use super::*;
233
234    #[test]
235    fn uri_scheme_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.scheme(), None);
255        assert_eq!(relative_file_path.scheme(), None);
256        assert_eq!(relative_file_path_with_scheme.scheme(), Some("file"));
257        assert_eq!(absolute_file_path.scheme(), Some("file"));
258        assert_eq!(http_simple_uri.scheme(), Some("http"));
259        assert_eq!(http_simple_uri_with_userinfo.scheme(), Some("http"));
260        assert_eq!(http_simple_uri_with_userinfo_and_port.scheme(), Some("http"));
261        assert_eq!(http_simple_uri_with_userinfo_and_path.scheme(), Some("http"));
262        assert_eq!(http_simple_uri_with_userinfo_and_query.scheme(), Some("http"));
263        assert_eq!(http_simple_uri_with_userinfo_and_fragment.scheme(), Some("http"));
264        assert_eq!(http_simple_uri_with_userinfo_and_path_and_query.scheme(), Some("http"));
265        assert_eq!(
266            http_simple_uri_with_userinfo_and_path_and_query_and_fragment.scheme(),
267            Some("http")
268        );
269    }
270
271    #[test]
272    fn uri_authority_extraction() {
273        let relative_file = Uri::new("foo.masm");
274        let relative_file_path = Uri::new("./foo.masm");
275        let relative_file_path_with_scheme = Uri::new("file:foo.masm");
276        let absolute_file_path = Uri::new("file:///tmp/foo.masm");
277        let http_simple_uri = Uri::new("http://www.example.com");
278        let http_simple_uri_with_userinfo = Uri::new("http://foo:bar@www.example.com");
279        let http_simple_uri_with_userinfo_and_port = Uri::new("http://foo:bar@www.example.com:443");
280        let http_simple_uri_with_userinfo_and_path =
281            Uri::new("http://foo:bar@www.example.com/api/v1");
282        let http_simple_uri_with_userinfo_and_query =
283            Uri::new("http://foo:bar@www.example.com?param=1");
284        let http_simple_uri_with_userinfo_and_fragment =
285            Uri::new("http://foo:bar@www.example.com#about");
286        let http_simple_uri_with_userinfo_and_path_and_query =
287            Uri::new("http://foo:bar@www.example.com/api/v1/user?id=1");
288        let http_simple_uri_with_userinfo_and_path_and_query_and_fragment =
289            Uri::new("http://foo:bar@www.example.com/api/v1/user?id=1#redirect=/home");
290
291        assert_eq!(relative_file.authority(), None);
292        assert_eq!(relative_file_path.authority(), None);
293        assert_eq!(relative_file_path_with_scheme.authority(), None);
294        assert_eq!(absolute_file_path.authority(), Some(""));
295        assert_eq!(http_simple_uri.authority(), Some("www.example.com"));
296        assert_eq!(http_simple_uri_with_userinfo.authority(), Some("foo:bar@www.example.com"));
297        assert_eq!(
298            http_simple_uri_with_userinfo_and_port.authority(),
299            Some("foo:bar@www.example.com:443")
300        );
301        assert_eq!(
302            http_simple_uri_with_userinfo_and_path.authority(),
303            Some("foo:bar@www.example.com")
304        );
305        assert_eq!(
306            http_simple_uri_with_userinfo_and_query.authority(),
307            Some("foo:bar@www.example.com")
308        );
309        assert_eq!(
310            http_simple_uri_with_userinfo_and_fragment.authority(),
311            Some("foo:bar@www.example.com")
312        );
313        assert_eq!(
314            http_simple_uri_with_userinfo_and_path_and_query.authority(),
315            Some("foo:bar@www.example.com")
316        );
317        assert_eq!(
318            http_simple_uri_with_userinfo_and_path_and_query_and_fragment.authority(),
319            Some("foo:bar@www.example.com")
320        );
321    }
322
323    #[test]
324    fn uri_path_extraction() {
325        let relative_file = Uri::new("foo.masm");
326        let relative_file_path = Uri::new("./foo.masm");
327        let relative_file_path_with_scheme = Uri::new("file:foo.masm");
328        let absolute_file_path = Uri::new("file:///tmp/foo.masm");
329        let http_simple_uri = Uri::new("http://www.example.com");
330        let http_simple_uri_with_userinfo = Uri::new("http://foo:bar@www.example.com");
331        let http_simple_uri_with_userinfo_and_port = Uri::new("http://foo:bar@www.example.com:443");
332        let http_simple_uri_with_userinfo_and_path =
333            Uri::new("http://foo:bar@www.example.com/api/v1");
334        let http_simple_uri_with_userinfo_and_query =
335            Uri::new("http://foo:bar@www.example.com?param=1");
336        let http_simple_uri_with_userinfo_and_fragment =
337            Uri::new("http://foo:bar@www.example.com#about");
338        let http_simple_uri_with_userinfo_and_path_and_query =
339            Uri::new("http://foo:bar@www.example.com/api/v1/user?id=1");
340        let http_simple_uri_with_userinfo_and_path_and_query_and_fragment =
341            Uri::new("http://foo:bar@www.example.com/api/v1/user?id=1#redirect=/home");
342
343        assert_eq!(relative_file.path(), "foo.masm");
344        assert_eq!(relative_file_path.path(), "./foo.masm");
345        assert_eq!(relative_file_path_with_scheme.path(), "foo.masm");
346        assert_eq!(absolute_file_path.path(), "/tmp/foo.masm");
347        assert_eq!(http_simple_uri.path(), "");
348        assert_eq!(http_simple_uri_with_userinfo.path(), "");
349        assert_eq!(http_simple_uri_with_userinfo_and_port.path(), "");
350        assert_eq!(http_simple_uri_with_userinfo_and_path.path(), "/api/v1");
351        assert_eq!(http_simple_uri_with_userinfo_and_query.path(), "");
352        assert_eq!(http_simple_uri_with_userinfo_and_fragment.path(), "");
353        assert_eq!(http_simple_uri_with_userinfo_and_path_and_query.path(), "/api/v1/user");
354        assert_eq!(
355            http_simple_uri_with_userinfo_and_path_and_query_and_fragment.path(),
356            "/api/v1/user"
357        );
358    }
359}