diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-08-29 17:35:35 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-08-29 17:35:35 +0200 |
| commit | a71a2057f286677b5bf2e064fea05024aeca0dd2 (patch) | |
| tree | 716d85481aca232abdb6c2e01a0a545c003f4c6b /crates/typst-syntax/src/span.rs | |
| parent | 7bdf1f57b09ea605045254013a8200373451baf0 (diff) | |
More type safety for spans
Diffstat (limited to 'crates/typst-syntax/src/span.rs')
| -rw-r--r-- | crates/typst-syntax/src/span.rs | 69 |
1 files changed, 36 insertions, 33 deletions
diff --git a/crates/typst-syntax/src/span.rs b/crates/typst-syntax/src/span.rs index 8715e476..d715af1c 100644 --- a/crates/typst-syntax/src/span.rs +++ b/crates/typst-syntax/src/span.rs @@ -28,55 +28,58 @@ pub struct Span(NonZeroU64); impl Span { /// The full range of numbers available for span numbering. - pub const FULL: Range<u64> = 2..(1 << Self::BITS); + pub(super) const FULL: Range<u64> = 2..(1 << Self::BITS); + + /// The value reserved for the detached span. const DETACHED: u64 = 1; - // Data layout: - // | 16 bits source id | 48 bits number | + /// Data layout: + /// | 16 bits source id | 48 bits number | const BITS: usize = 48; /// Create a new span from a source id and a unique number. /// - /// Panics if the `number` is not contained in `FULL`. - #[track_caller] - pub const fn new(id: FileId, number: u64) -> Self { - assert!( - Self::FULL.start <= number && number < Self::FULL.end, - "span number outside valid range" - ); - - Self::pack(id, number) - } + /// Returns `None` if `number` is not contained in `FULL`. + pub(super) const fn new(id: FileId, number: u64) -> Option<Self> { + if number < Self::FULL.start || number >= Self::FULL.end { + return None; + } - /// A span that does not point into any source file. - pub const fn detached() -> Self { - Self::pack(FileId::detached(), Self::DETACHED) + let bits = ((id.into_raw() as u64) << Self::BITS) | number; + match NonZeroU64::new(bits) { + Some(v) => Some(Self(v)), + None => unreachable!(), + } } - /// Pack the components into a span. - #[track_caller] - const fn pack(id: FileId, number: u64) -> Span { - let bits = ((id.as_u16() as u64) << Self::BITS) | number; - match NonZeroU64::new(bits) { + /// Create a span that does not point into any source file. + pub const fn detached() -> Self { + match NonZeroU64::new(Self::DETACHED) { Some(v) => Self(v), - None => panic!("span encoding is zero"), + None => unreachable!(), } } + /// Whether the span is detached. + pub const fn is_detached(self) -> bool { + self.0.get() == Self::DETACHED + } + /// The id of the source file the span points into. - pub const fn id(self) -> FileId { - FileId::from_u16((self.0.get() >> Self::BITS) as u16) + /// + /// Returns `None` if the span is detached. + pub const fn id(self) -> Option<FileId> { + if self.is_detached() { + return None; + } + let bits = (self.0.get() >> Self::BITS) as u16; + Some(FileId::from_raw(bits)) } - /// The unique number of the span within its source file. + /// The unique number of the span within its [`Source`](super::Source). pub const fn number(self) -> u64 { self.0.get() & ((1 << Self::BITS) - 1) } - - /// Whether the span is detached. - pub const fn is_detached(self) -> bool { - self.id().is_detached() - } } /// A value with a span locating it in the source code. @@ -120,9 +123,9 @@ mod tests { #[test] fn test_span_encoding() { - let id = FileId::from_u16(5); - let span = Span::new(id, 10); - assert_eq!(span.id(), id); + let id = FileId::from_raw(5); + let span = Span::new(id, 10).unwrap(); + assert_eq!(span.id(), Some(id)); assert_eq!(span.number(), 10); } } |
