use std::collections::VecDeque;
use std::str::FromStr;
use trailer::{self, Trailer};
pub enum Block {
Text(Vec<String>),
Trailer(Vec<Trailer>),
}
#[derive(Debug)]
pub struct Blocks<I, S>(I)
where I: Iterator<Item = S>,
S: AsRef<str>;
impl<I, S> From<I> for Blocks<I, S>
where I: Iterator<Item = S>,
S: AsRef<str>
{
fn from(iter: I) -> Self {
Blocks(iter)
}
}
impl<I, S> Iterator for Blocks<I, S>
where I: Iterator<Item = S>,
S: AsRef<str>
{
type Item = Block;
fn next(&mut self) -> Option<Self::Item> {
let mut lines = Vec::new();
let mut trailers: Vec<Trailer> = Vec::new();
let mut is_trailer = true;
for line in &mut self.0 {
let trimmed = line.as_ref().trim_end();
if trimmed.is_empty() {
if lines.is_empty() {
continue;
} else {
break;
}
}
lines.push(trimmed.to_string());
if !is_trailer {
continue;
}
if trimmed.starts_with(" ") {
if let Some(ref mut trailer) = trailers.last_mut() {
trailer.value.append("\n");
trailer.value.append(trimmed);
} else {
is_trailer = false;
}
} else if let Ok(trailer) = Trailer::from_str(trimmed) {
trailers.push(trailer);
} else {
is_trailer = false;
}
}
if lines.is_empty() {
return None;
}
if is_trailer {
Some(Block::Trailer(trailers))
} else {
Some(Block::Text(lines))
}
}
}
pub struct Trailers<I, S>
where I: Iterator<Item = S>,
S: AsRef<str>
{
blocks: Blocks<I, S>,
buf: VecDeque<Trailer>,
}
impl<I, S> Trailers<I, S>
where I: Iterator<Item = S>,
S: AsRef<str>
{
pub fn only_dit(self) -> trailer::iter::DitTrailers<Self> {
self.into()
}
}
impl<I, S> From<Blocks<I, S>> for Trailers<I, S>
where I: Iterator<Item = S>,
S: AsRef<str>
{
fn from(blocks: Blocks<I, S>) -> Self {
Trailers {
blocks: blocks,
buf: VecDeque::new(),
}
}
}
impl<I, S> From<I> for Trailers<I, S>
where I: Iterator<Item = S>,
S: AsRef<str>
{
fn from(lines: I) -> Self {
Blocks::from(lines).into()
}
}
impl<I, S> Iterator for Trailers<I, S>
where I: Iterator<Item = S>,
S: AsRef<str>
{
type Item = Trailer;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(trailer) = self.buf.pop_front() {
return Some(trailer);
}
match self.blocks.next() {
Some(Block::Trailer(trailers)) => self.buf = VecDeque::from(trailers),
None => return None,
_ => {},
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use trailer::{TrailerKey, TrailerValue};
#[test]
fn trailers() {
let mut blocks = Blocks::from(vec![
"Foo-bar: bar",
"",
"Space: the final frontier.",
"These are the voyages...",
"",
"And then he",
"said: engage!",
"",
"And now",
"for something completely different.",
"",
"",
"Signed-off-by: Spock",
"Dit-status: closed",
"Multi-line-trailer: multi",
" line",
" content"
].into_iter());
match blocks.next().expect("Failed to retrieve block 1") {
Block::Trailer(trailers) => {
let mut iter = trailers.iter();
let trailer = iter.next().expect("Failed to parse trailer 1");
assert_eq!(trailer.key, TrailerKey::from("Foo-bar".to_string()));
assert!(iter.next().is_none());
},
_ => panic!("Wrong type for block 1")
}
match blocks.next().expect("Failed to retrieve block 2") {
Block::Text(lines) => assert_eq!(lines, vec![
"Space: the final frontier.",
"These are the voyages..."
]),
_ => panic!("Wrong type for block 2")
}
match blocks.next().expect("Failed to retrieve block 3") {
Block::Text(lines) => assert_eq!(lines, vec![
"And then he",
"said: engage!",
]),
_ => panic!("Wrong type for block 3")
}
match blocks.next().expect("Failed to retrieve block 4") {
Block::Text(lines) => assert_eq!(lines, vec![
"And now",
"for something completely different.",
]),
_ => panic!("Wrong type for block 4")
}
match blocks.next().expect("Failed to retrieve block 5") {
Block::Trailer(trailers) => {
let mut iter = trailers.iter();
{
let trailer = iter.next().expect("Failed to parse trailer 2");
assert_eq!(trailer.key, TrailerKey::from("Signed-off-by".to_string()));
}
{
let trailer = iter.next().expect("Failed to parse trailer 3");
assert_eq!(trailer.key, TrailerKey::from("Dit-status".to_string()));
}
{
let trailer = iter.next().expect("Failed to parse trailer 4");
assert_eq!(trailer.key, TrailerKey::from("Multi-line-trailer".to_string()));
assert_eq!(trailer.value, TrailerValue::String("multi\n line\n content".to_string()));
}
assert!(iter.next().is_none());
},
_ => panic!("Wrong type for block 5")
}
assert!(!blocks.next().is_some())
}
#[test]
fn trailers_iter() {
let mut trailers = Trailers::from(vec![
"Foo-bar: bar",
"",
"Space: the final frontier.",
"These are the voyages...",
"",
"And then he",
"said: engage!",
"",
"",
"Signed-off-by: Spock",
"Dit-status: closed",
"Multi-line-trailer: multi",
" line",
" content"
].into_iter());
{
let (key, _) = trailers.next().expect("Failed to parse trailer1").into();
assert_eq!(key, "Foo-bar".to_string().into());
}
{
let (key, _) = trailers.next().expect("Failed to parse trailer2").into();
assert_eq!(key, "Signed-off-by".to_string().into());
}
{
let (key, _) = trailers.next().expect("Failed to parse trailer3").into();
assert_eq!(key, "Dit-status".to_string().into());
}
{
let (key, value) = trailers.next().expect("Failed to parse trailer4").into();
assert_eq!(key, "Multi-line-trailer".to_string().into());
assert_eq!(value, TrailerValue::String("multi\n line\n content".to_string()));
}
assert!(!trailers.next().is_some())
}
}