use std::{
convert::{AsRef, From},
fmt,
fs::File,
io::Read,
ops::Deref,
path::Path,
};
use failure::{Error, ResultExt};
use card::keyword::Keyword;
#[derive(Debug, PartialEq)]
pub enum Line<'a> {
OriginalLine(&'a [u8]),
ChangedLine(String),
}
impl<'a> AsRef<[u8]> for Line<'a> {
fn as_ref(&self) -> &[u8] {
match self {
Line::OriginalLine(l) => l,
Line::ChangedLine(s) => s.as_ref(),
}
}
}
impl<'a> fmt::Display for Line<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Line::*;
match self {
OriginalLine(l) => write!(f, "Line {{ {} }}", String::from_utf8_lossy(l)),
ChangedLine(s) => write!(f, "Line {{ {} }}", s),
}
}
}
#[derive(Debug, Default)]
pub struct Lines<'a>(Vec<Line<'a>>);
impl<'a> fmt::Display for Lines<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut s = String::new();
s.push_str("Lines {{\n");
for line in self.iter() {
s.push_str(&format!(" {{{}}}\n", String::from_utf8_lossy(line)));
}
s.push_str("}}\n");
write!(f, "{}", s)
}
}
pub struct LinesIter<'a, I>
where
I: Iterator<Item = &'a Line<'a>>,
{
li: I,
}
impl<'a> Lines<'a> {
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn from_vec(v: Vec<String>) -> Lines<'a> {
let w = v.into_iter().map(Line::ChangedLine).collect();
Lines(w)
}
pub fn from_strs(v: &'a [&'a str]) -> Lines<'a> {
let w: Vec<Line<'a>> = v
.into_iter()
.map(|l| Line::OriginalLine(l.as_ref()))
.collect();
Lines(w)
}
pub fn from_slice(v: &'a [u8]) -> Lines<'a> {
let w: Vec<Line> =
v.split(|b| *b == b'\n').map(Line::OriginalLine).collect();
Lines(w)
}
pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
let mut file = File::open(&path).with_context(|e| {
format!("Error opening {}: {}", path.as_ref().display(), e)
})?;
let l = file.metadata().unwrap().len();
let mut v: Vec<u8> = Vec::with_capacity(l as usize);
let _ = file.read_to_end(&mut v).with_context(|e| {
format!("Error reading {}: {}", path.as_ref().display(), e)
})?;
Ok(v)
}
pub fn update(&mut self, first: usize, last: usize, linedata: Vec<String>) {
let range = first..last;
let _ = self
.0
.splice(range, linedata.into_iter().map(Line::ChangedLine));
}
pub fn iter(&'a self) -> LinesIter<'a, impl Iterator<Item = &'a Line<'a>>> {
LinesIter { li: self.0.iter() }
}
}
impl<'a, I> Iterator for LinesIter<'a, I>
where
I: Iterator<Item = &'a Line<'a>>,
{
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
self.li.next().map(|o| o.as_ref())
}
}
impl<'a> From<Vec<String>> for Lines<'a> {
fn from(v: Vec<String>) -> Lines<'a> {
Lines(v.into_iter().map(Line::ChangedLine).collect())
}
}
#[derive(PartialEq, Debug)]
pub struct ParsedLine<'a> {
pub number: usize,
pub text: &'a [u8],
pub keyword: Option<&'a Keyword>,
}
impl<'a> ParsedLine<'a> {
pub fn try_into_keywordline(&self) -> Option<KeywordLine<'a>> {
if let Some(kw) = self.keyword {
return Some(KeywordLine {
number: self.number,
text: self.text,
keyword: kw,
});
} else {
return None;
}
}
}
impl<'a> From<(usize, (&'a Option<Keyword>, &'a [u8]))> for ParsedLine<'a> {
fn from(
(u, (k, t)): (usize, (&'a Option<Keyword>, &'a [u8])),
) -> ParsedLine<'a> {
ParsedLine {
number: u,
text: t,
keyword: k.as_ref(),
}
}
}
impl<'a> From<(usize, (&'a Option<Keyword>, &'a Line<'a>))> for ParsedLine<'a> {
fn from(
(u, (k, t)): (usize, (&'a Option<Keyword>, &'a Line<'a>)),
) -> ParsedLine<'a> {
ParsedLine {
number: u,
text: t.as_ref(),
keyword: k.as_ref(),
}
}
}
impl<'a> From<KeywordLine<'a>> for ParsedLine<'a> {
fn from(kl: KeywordLine<'a>) -> ParsedLine<'a> {
ParsedLine {
number: kl.number,
text: kl.text,
keyword: Some(kl.keyword),
}
}
}
impl<'a> fmt::Display for ParsedLine<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ParsedLine {{{}, text: {}, keyword: {:?}}}",
self.number,
String::from_utf8_lossy(self.text),
self.keyword
)
}
}
#[derive(PartialEq, Debug)]
pub struct KeywordLine<'a> {
pub number: usize,
pub text: &'a [u8],
pub keyword: &'a Keyword,
}
impl<'a> fmt::Display for KeywordLine<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"KeywordLine {{{}, text: {}, keyword: {:?}}}",
self.number,
String::from_utf8_lossy(self.text),
self.keyword
)
}
}
impl<'a> Deref for Lines<'a> {
type Target = [Line<'a>];
fn deref(&self) -> &[Line<'a>] {
&self.0
}
}
#[cfg(test)]
mod tests {
use lines::{Line::*, Lines};
const LINES: &str = "This\nis \nan \nexample \nof \nsome \nlines \n.";
#[test]
fn lines_can_delete() {
let mut l = Lines::from_slice(LINES.as_ref());
l.update(1, 7, Vec::new());
assert_eq!(l.0[0], OriginalLine(b"This"));
assert_eq!(l.0[1], OriginalLine(b"."));
assert_eq!(l.len(), 2);
}
#[test]
fn lines_can_insert() {
let mut l = Lines::from_slice(LINES.as_ref());
let newlines = vec![
"haaargl".to_string(),
"waaarglll".to_string(),
"blaaargl".to_string(),
];
l.update(2, 2, newlines);
assert_eq!(l.0[1], OriginalLine(b"is "));
assert_eq!(l.0[2], ChangedLine("haaargl".to_string()));
assert_eq!(l.0[5], OriginalLine(b"an "));
assert_eq!(l.len(), 11);
}
#[test]
fn lines_can_update() {
let mut l = Lines::from_slice(LINES.as_ref());
let newlines = vec![
"haaargl".to_string(),
"waaarglll".to_string(),
"blaaargl".to_string(),
];
l.update(1, 7, newlines);
assert_eq!(l.0[0], OriginalLine(b"This"));
assert_eq!(l.0[3], ChangedLine("blaaargl".to_string()));
assert_eq!(l.0[4], OriginalLine(b"."));
assert_eq!(l.len(), 5);
}
#[test]
fn lines_from_file() {
let v = Lines::read_file(file!()).unwrap();
let l = Lines::from_slice(&v);
let f = OriginalLine(
b"//! This module holds the datastructure for the Lines of the \
buffer.",
);
assert_eq!(f, l.0[0]);
}
}