summaryrefslogtreecommitdiff
path: root/crates/typst-syntax/src/span.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-08-29 17:35:35 +0200
committerLaurenz <laurmaedje@gmail.com>2023-08-29 17:35:35 +0200
commita71a2057f286677b5bf2e064fea05024aeca0dd2 (patch)
tree716d85481aca232abdb6c2e01a0a545c003f4c6b /crates/typst-syntax/src/span.rs
parent7bdf1f57b09ea605045254013a8200373451baf0 (diff)
More type safety for spans
Diffstat (limited to 'crates/typst-syntax/src/span.rs')
-rw-r--r--crates/typst-syntax/src/span.rs69
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);
}
}