use std::ops::Deref;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Span<'src> {
data: &'src str,
line: usize,
col: usize,
offset: usize,
}
impl<'src> Span<'src> {
pub const fn new(data: &'src str) -> Self {
Self {
data,
line: 1,
col: 1,
offset: 0,
}
}
pub fn line(&self) -> usize {
self.line
}
pub fn col(&self) -> usize {
self.col
}
pub fn byte_offset(&self) -> usize {
self.offset
}
pub fn data(&self) -> &'src str {
self.data
}
}
impl AsRef<str> for Span<'_> {
fn as_ref(&self) -> &str {
self.data
}
}
const EMPTY_STR: &str = "";
impl Default for Span<'_> {
fn default() -> Self {
Self {
data: EMPTY_STR,
line: 1,
col: 1,
offset: 0,
}
}
}
impl<'src> Deref for Span<'src> {
type Target = &'src str;
fn deref(&self) -> &Self::Target {
&self.data
}
}
mod discard;
mod line;
mod matched_item;
mod primitives;
mod r#slice;
mod split;
mod take;
mod trim;
pub(crate) use matched_item::MatchedItem;
pub trait HasSpan<'src> {
fn span(&self) -> Span<'src>;
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
#[test]
fn simple_case() {
let span = crate::Span::new(r#"{"hello": "world 🙌"}"#);
assert_eq!(span.line(), 1);
assert_eq!(span.col(), 1);
assert_eq!(span.byte_offset(), 0);
}
#[test]
fn impl_default() {
let span = crate::Span::default();
assert_eq!(span.data(), "");
assert_eq!(span.line(), 1);
assert_eq!(span.col(), 1);
assert_eq!(span.byte_offset(), 0);
}
#[test]
fn impl_as_ref() {
let span = crate::Span::new("abcdef");
assert_eq!(span.as_ref(), "abcdef");
}
#[test]
fn into_parse_result() {
let s = crate::Span::new("abc");
let mi = s.into_parse_result(1);
assert_eq!(mi.item.data(), "a");
assert_eq!(mi.item.line(), 1);
assert_eq!(mi.item.col(), 1);
assert_eq!(mi.item.byte_offset(), 0);
assert_eq!(mi.after.data(), "bc");
assert_eq!(mi.after.line(), 1);
assert_eq!(mi.after.col(), 2);
assert_eq!(mi.after.byte_offset(), 1);
}
mod split_at_match_non_empty {
#[test]
fn empty_source() {
let s = crate::Span::default();
assert!(s.split_at_match_non_empty(|c| c == ':').is_none());
}
#[test]
fn empty_subspan() {
let s = crate::Span::new(":abc");
assert!(s.split_at_match_non_empty(|c| c == ':').is_none());
}
#[test]
fn match_after_first() {
let s = crate::Span::new("ab:cd");
let mi = s.split_at_match_non_empty(|c| c == ':').unwrap();
assert_eq!(mi.item.data(), "ab");
assert_eq!(mi.item.line(), 1);
assert_eq!(mi.item.col(), 1);
assert_eq!(mi.item.byte_offset(), 0);
assert_eq!(mi.after.data(), ":cd");
assert_eq!(mi.after.line(), 1);
assert_eq!(mi.after.col(), 3);
assert_eq!(mi.after.byte_offset(), 2);
}
}
}