use error::*;
use error::ErrorKind as EK;
use git2::Commit;
use std;
pub mod block;
pub mod line_processor;
use self::line_processor::{Quoted, StrippingIter};
pub trait LineIteratorExt<S>
where S: AsRef<str>
{
type Iter : Iterator<Item = S>;
fn check_message_format(self) -> Result<()>;
fn stripped(self) -> StrippingIter<Self::Iter, S>;
fn quoted(self) -> Quoted<Self::Iter, S>;
fn line_blocks(self) -> block::Blocks<Self::Iter, S>;
fn trailers(self) -> block::Trailers<Self::Iter, S>;
fn collect_string(self) -> String;
}
impl<L, S> LineIteratorExt<S> for L
where L: Iterator<Item = S>,
S: AsRef<str>
{
type Iter = L;
fn check_message_format(mut self) -> Result<()> {
if self.next().ok_or(Error::from_kind(EK::EmptyMessage))?.as_ref().is_empty() {
return Err(Error::from_kind(EK::EmptySubject))
}
if !self.next().map(|line| line.as_ref().is_empty()).unwrap_or(true) {
return Err(Error::from_kind(EK::MalformedMessage));
}
Ok(())
}
fn stripped(self) -> StrippingIter<Self::Iter, S> {
line_processor::TrailingBlankTrimmer::from(
line_processor::StripWhiteSpaceRightIter::from(
line_processor::WithoutCommentsIter::from(self)
)
)
}
fn quoted(self) -> Quoted<Self::Iter, S> {
Quoted::from(self)
}
fn line_blocks(self) -> block::Blocks<Self::Iter, S> {
block::Blocks::from(self)
}
fn trailers(self) -> block::Trailers<Self::Iter, S> {
self.into()
}
fn collect_string(self) -> String {
self.fold(String::new(), |mut res, line| {
res.push_str(line.as_ref());
res.push('\n');
res
})
}
}
pub type BodyLines = std::iter::Skip<std::vec::IntoIter<String>>;
pub trait Message {
fn message_lines(&self) -> std::vec::IntoIter<String>;
fn body_lines(&self) -> BodyLines;
fn body_blocks(&self) -> block::Blocks<BodyLines, String>;
fn trailers(&self) -> block::Trailers<BodyLines, String>;
fn reply_subject(&mut self) -> Option<String>;
}
impl<'c> Message for Commit<'c> {
fn message_lines(&self) -> std::vec::IntoIter<String> {
let lines : Vec<String> = self.message()
.unwrap_or("")
.lines()
.map(String::from)
.collect();
lines.into_iter()
}
fn body_lines(&self) -> BodyLines {
self.message_lines().skip(2)
}
fn body_blocks(&self) -> block::Blocks<BodyLines, String> {
self.body_lines().line_blocks()
}
fn trailers(&self) -> block::Trailers<BodyLines, String> {
self.body_lines().trailers()
}
fn reply_subject(&mut self) -> Option<String> {
self.summary().map(|s| {
if s.starts_with("Re: ") {
s.to_owned()
} else {
format!("Re: {}", s)
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_message_format_check() {
let vec : Vec<&str> = Vec::new();
assert!(vec.into_iter().check_message_format().is_err());
}
#[test]
fn empty_message_format_check2() {
assert!(vec![""].into_iter().check_message_format().is_err());
}
#[test]
fn oneline_message_format_check() {
vec!["Foo bar"].into_iter().check_message_format().unwrap();
}
#[test]
fn malformed_message_format_check() {
assert!(vec!["Foo bar", "Baz"].into_iter().check_message_format().is_err());
}
#[test]
fn multiline_message_format_check() {
vec!["Foo bar", "", "Baz"].into_iter().check_message_format().unwrap();
}
}