erebus 0.1.7

A CLI message generation library
Documentation
use unicode_segmentation::UnicodeSegmentation;

pub trait FileId: Eq + Clone {}
impl<T> FileId for T where T: Eq + Clone {}

/// A trait for types that can provide information about files.
///
/// The default implementations should be overridden with more efficient implementations when possible.
pub trait Files<'a> {
    type FileId: FileId;
    type Content: 'a + AsRef<str>;
    /// Get the name of a file.
    fn name(&self, id: &'a Self::FileId) -> Option<&'a str>;
    /// Get the content of a file.
    fn content(&self, id: &'a Self::FileId) -> Option<Self::Content>;
    /// Get the line number from a character index.
    fn line_from_char(&self, id: &'a Self::FileId, char: usize) -> Option<usize> {
        self.content(id)?
            .as_ref()
            .graphemes(true)
            .take(char)
            .filter(|c| *c == "\n")
            .count()
            .checked_add(1)
    }
    /// Get the character index from a line number.
    fn char_from_line(&self, id: &'a Self::FileId, line: usize) -> Option<usize> {
        Some(
            self.content(id)?
                .as_ref()
                .lines()
                .take(line - 1)
                .map(|l| l.graphemes(true).count() + 1)
                .sum::<usize>(),
        )
    }
}

#[derive(Debug, Clone, Copy, Default)]
pub struct NoFile;
impl PartialEq for NoFile {
    fn eq(&self, _: &Self) -> bool {
        true
    }
}
impl Eq for NoFile {}
impl<'a> Files<'a> for NoFile {
    type FileId = Self;
    type Content = &'a str;
    fn name(&self, _: &'a Self::FileId) -> Option<&'a str> {
        None
    }
    fn content(&self, _: &'a Self::FileId) -> Option<Self::Content> {
        None
    }
}