neodyn_xc 0.4.0

Neodyn Exchange is the serialization format for the Neodyn database engine
Documentation
//! Manipulating source locations.

use std::fmt::{ Display, Formatter, Result as FmtResult };
use typemap::Key;
use unicode_segmentation::UnicodeSegmentation;

/// The location of a single extended grapheme cluster within some source code.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Location {
    /// 1-based line index within the source.
    pub line: usize,
    /// 1-based character index within the line.
    pub column: usize,
}

impl Location {
    /// Increments the number of lines and/or characters according to the
    /// line breaks found in `lexeme`. Returns the updated `Location`.
    #[must_use]
    pub fn advanced_by(&self, lexeme: &str) -> Location {
        // TODO(H2CO3): Keep this list in sync with the parser
        let line_breaks: &[char] = &[
            '\n', '\x0b', '\x0c', '\r',
            '\u{0085}', '\u{2028}', '\u{2029}',
        ];
        match lexeme.rfind(line_breaks) {
            // +1 because humans start counting at 1,
            // -1 because the \n itself doesn't count.
            Some(index) => Location {
                line: self.line + grapheme_count_by(
                    lexeme,
                    |g| g.contains(line_breaks)
                ),
                column: grapheme_count(&lexeme[index..]) + 1 - 1,
            },
            None => Location {
                line: self.line,
                column: self.column + grapheme_count(lexeme),
            },
        }
    }
}

/// The default is the beginning of the source, i.e. `{ line: 1, column: 1 }`
impl Default for Location {
    fn default() -> Self {
        Location { line: 1, column: 1 }
    }
}

impl Display for Location {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "line {} char {}", self.line, self.column)
    }
}

impl Key for Location {
    type Value = Location;
}

/// A half-open range representing a source span.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Span {
    /// Location at the beginning of the source range.
    pub start: Location,
    /// Location one past the end of the source range.
    pub end: Location,
}

impl Display for Span {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}...{}", self.start, self.end)
    }
}

impl Key for Span {
    type Value = Span;
}

impl Spanned for Span {
    fn span(&self) -> Span {
        *self
    }
}

/// Types that point to a span inside the source.
pub trait Spanned {
    /// The span where this value comes from.
    fn span(&self) -> Span;

    /// Creates a span between the start of `self` and the end of `other`.
    ///
    /// Should not be overridden.
    fn span_to<T: Spanned>(&self, other: &T) -> Span {
        Span {
            start: self.span().start,
            end: other.span().end,
        }
    }
}

/// Returns an iterator over the extended grapheme clusters in `string`.
fn graphemes(string: &str) -> impl Iterator<Item = &str> {
    string.graphemes(true)
}

/// Returns the number of extended grapheme clusters in `string`.
/// Useful for counting 'characters' in accordance with a user's
/// notion of a 'character' or grapheme. Mainly used by the lexer
/// for generating visually accurate source location data.
///
/// # Arguments:
///
/// * `string`: a string slice.
///
/// # Return value:
///
/// The number of extended grapheme clusters in `string`.
#[must_use]
fn grapheme_count(string: &str) -> usize {
    graphemes(string).count()
}

/// Counts the grapheme clusters satisfying a predicate.
///
/// # Arguments:
///
/// * `string`: a string slice.
/// * `pred`: a predicate function invoked for each extended grapheme
///   cluster in `string`.
///
/// # Return value:
///
/// The number of extended grapheme clusters in `string`
/// for which `pred` returned `true`.
fn grapheme_count_by<P>(string: &str, pred: P) -> usize
    where
        P: Fn(&str) -> bool,
{
    graphemes(string).filter(|&g| pred(g)).count()
}