> From for Context {
fn from(sym: P) -> Self {
Self(sym.into())
}
}
impl Display for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl AsRef for Context {
fn as_ref(&self) -> &Path {
Path::new(self.0.lookup_str())
}
}
/// A closed interval (range of values including its endpoints) representing
/// source bytes associated with a token.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct ClosedByteInterval(pub T, pub T)
where
T: Copy + PartialOrd;
impl From<(T, T)> for ClosedByteInterval {
/// Convert a tuple into a closed byte interval where the first index
/// represents the start of the interval and the second index the
/// end.
///
/// Panics
/// ======
/// The second value must be ≥ the first.
fn from(src: (T, T)) -> Self {
assert!(src.1 >= src.0);
Self(src.0, src.1)
}
}
assert_eq_size!(ClosedByteInterval, u64);
/// Dummy spans for testing.
#[cfg(test)]
pub mod dummy {
use super::{st16, Context, Span};
/// A dummy span that can be used in contexts where a span is expected
/// but is not important.
///
/// This is intended primarily for tests;
/// you should always use an appropriate span to permit sensible error
/// messages and source analysis.
/// For spans that are actually unknown,
/// use [`super::UNKNOWN_SPAN`].
///
/// Additional dummy spans can be derived from this one.
pub const DUMMY_SPAN: Span = Span::st_ctx(st16::CTX_DUMMY);
/// A dummy context that can be used where a span is expected but is not
/// important.
///
/// This is intended primarily for tests;
/// you should always use an appropriate span to permit sensible error
/// messages and source analysis.
/// For contexts that are actually unknown,
/// use [`super::UNKNOWN_CONTEXT`].
///
/// See also [`UNKNOWN_CONTEXT`].
pub const DUMMY_CONTEXT: Context = Context(st16::raw::CTX_DUMMY);
// This name is for brevity;
// we don't want to expose it because we don't want anyone to assume
// that a different name means that it's somehow different from
// `DUMMY_SPAN`.
const S0: Span = DUMMY_SPAN;
pub const S1: Span = S0.offset_add(1).unwrap();
pub const S2: Span = S0.offset_add(2).unwrap();
pub const S3: Span = S0.offset_add(3).unwrap();
pub const S4: Span = S0.offset_add(4).unwrap();
pub const S5: Span = S0.offset_add(5).unwrap();
pub const S6: Span = S0.offset_add(6).unwrap();
pub const S7: Span = S0.offset_add(7).unwrap();
pub const S8: Span = S0.offset_add(8).unwrap();
pub const S9: Span = S0.offset_add(9).unwrap();
pub const S10: Span = S0.offset_add(10).unwrap();
pub const S11: Span = S0.offset_add(11).unwrap();
pub const S12: Span = S0.offset_add(12).unwrap();
pub const S13: Span = S0.offset_add(13).unwrap();
pub const S14: Span = S0.offset_add(14).unwrap();
pub const S15: Span = S0.offset_add(15).unwrap();
pub const S16: Span = S0.offset_add(16).unwrap();
pub const S17: Span = S0.offset_add(17).unwrap();
pub const S18: Span = S0.offset_add(18).unwrap();
pub const S19: Span = S0.offset_add(19).unwrap();
pub const S20: Span = S0.offset_add(20).unwrap();
}
#[cfg(test)]
mod test {
use super::*;
// Little endian check.
//
// This ensures that the byte ordering is as expected,
// otherwise the resulting integer will not have the properties we
// require for sorting and comparison.
#[cfg(target_endian = "little")]
#[test]
fn span_pack_le() {
let span =
Span::new(0xA3A2A1A0, 0xB1B0, SymbolId::test_from_int(0xC1C0));
assert_eq!(
0xC1C0_A3A2A1A0_B1B0,
// ^ ^ ^
// ctx offset len
span.as_u64(),
"endianness check failed: {:X?}",
span.as_u64()
);
}
#[cfg(target_endian = "big")]
#[test]
fn span_pack_be_not_supported() {
panic!("Big-endian systems are not currently supported");
}
// The tests that follow are corollaries of the above, but the below
// tests do test that the implementations function as intended.
#[test]
fn span_at_later_offset_in_same_context_compares_greater() {
let ctx = Context::from("imaginary");
let first = ctx.span(10, 5);
let second = ctx.span(20, 5);
// These two assertions must be identical.
assert!(second > first);
assert!(second.as_u64() > first.as_u64());
}
#[test]
fn spans_order_by_context_start_and_len() {
let ctxa = Context::from("context a");
let ctxb = Context::from("context b");
// Sanity check, otherwise this test won't work as expected.
assert!(ctxa.0 < ctxb.0);
let sa1 = ctxa.span(10, 1);
let sa2 = ctxa.span(22, 1);
let sa3 = ctxa.span(35, 1);
let sb1 = ctxb.span(11, 1);
let sb2 = ctxb.span(20, 1);
let sb3 = ctxb.span(33, 1);
let mut spans = vec![sa3, sb2, sb1, sa2, sa1, sb3];
spans.sort();
assert_eq!(spans, vec![sa1, sa2, sa3, sb1, sb2, sb3]);
}
#[test]
fn retrieve_span_components() {
let ctx = Context::from("foo");
let offset = 100;
let len = 50;
let span = ctx.span(offset, len);
assert_eq!(
(offset, len, ctx),
(span.offset(), span.len(), span.context())
);
}
#[test]
fn span_offset_add() {
let ctx = Context::from("addtest");
let offset = 10;
let len = 5;
let span = ctx.span(offset, len);
// Successful add.
assert_eq!(
span.offset_add(10),
Some(Span {
offset: offset + 10,
len,
ctx
})
);
// Fail, do not wrap.
assert_eq!(span.offset_add(SpanOffsetSize::MAX), None);
}
#[test]
fn span_into_twospan() {
let ctx = Context::from("foo");
let span = ctx.span(10, 50);
assert_eq!((span, span), span.into());
}
#[test]
fn span_endpoints() {
let ctx = Context::from("end");
let span = ctx.span(10, 20);
let (start, end) = span.endpoints();
assert_eq!(start, Span::new(10, 0, ctx));
assert_eq!(end, Some(Span::new(30, 0, ctx)));
}
#[test]
fn span_endpoints_exceeding_max_offset() {
let ctx = Context::from("end");
let offset = SpanOffsetSize::MAX - 5;
let span = ctx.span(offset, 10);
let (start, end) = span.endpoints();
assert_eq!(start, Span::new(offset, 0, ctx));
assert_eq!(end, None);
}
#[test]
fn span_endpoints_saturated() {
let ctx = Context::from("end");
let offset = SpanOffsetSize::MAX - 5;
let span = ctx.span(offset, 10);
let (start, end) = span.endpoints_saturated();
assert_eq!(start, Span::new(offset, 0, ctx));
assert_eq!(end, Span::new(SpanOffsetSize::MAX, 0, ctx));
}
#[test]
fn span_slice_yields_slice_within_original() {
let ctx = Context::from("slice");
let span = ctx.span(10, 10);
assert_eq!(ctx.span(15, 5), span.slice(5, 5));
// and from the opposite direction
assert_eq!(ctx.span(17, 2), span.rslice(3, 2));
// While we're at it,
// these also use the above:
assert_eq!(ctx.span(10, 5), span.slice_head(5));
assert_eq!(ctx.span(15, 5), span.slice_tail(5));
}
#[test]
fn span_slice_large_values_yield_original() {
let span = Context::from("slice").span(0, 50);
// Too large of an offset should return original even though legnth
// is okay.
assert_eq!(span, span.slice(usize::MAX, 5));
// Too large of length should return original even though offset is
// okay.
assert_eq!(span, span.slice(0, usize::MAX));
}
#[test]
fn span_merge_one_after_other() {
let ctx = Context::from("merge");
// "an example string"
// [-----] [----]
// 3 9 11 16
// | A B |
// [------------]
// 3 16
// C
let a = ctx.span(3, 7);
let b = ctx.span(11, 6);
let c = ctx.span(3, 14);
assert_eq!(a.merge(b), Some(c));
assert_eq!(b.merge(a), Some(c));
}
#[test]
fn span_merge_overlap() {
let ctx = Context::from("merge");
// "an example string"
// [---+-] |
// 3 | 9 |
// | A| |
// | [--------]
// | 7 16
// | B |
// [------------]
// 3 16
// C
let a = ctx.span(3, 7);
let b = ctx.span(7, 10);
let c = ctx.span(3, 14);
// We compare in both orders,
// so this will test when a span overlaps on either side.
assert_eq!(a.merge(b), Some(c));
assert_eq!(b.merge(a), Some(c));
}
#[test]
fn span_merge_overlap_within() {
let ctx = Context::from("merge");
// "an example string"
// |[----] |
// |1 6 |
// | B |
// [--------]
// 0 9
// C
let b = ctx.span(1, 6);
let c = ctx.span(0, 10);
assert_eq!(b.merge(c), Some(c));
assert_eq!(c.merge(b), Some(c));
}
// It doesn't make sense to merge two spans that are located in
// different contexts.
#[test]
fn span_merge_different_contexts() {
let ctx_a = Context::from("merge_a");
let ctx_b = Context::from("merge_b");
assert_eq!(ctx_a.span(0, 1).merge(ctx_b.span(1, 2)), None);
}
}