1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
use crate::body::Body;
use crate::fragment::Fragment;
use crate::trailer::Trailer;
use std::convert::TryFrom;

#[derive(Debug, PartialEq, Clone)]
pub struct Bodies {
    bodies: Vec<Body>,
}

impl From<Vec<Body>> for Bodies {
    fn from(bodies: Vec<Body>) -> Self {
        Bodies { bodies }
    }
}

impl From<Bodies> for String {
    fn from(bodies: Bodies) -> Self {
        bodies
            .bodies
            .into_iter()
            .map(String::from)
            .collect::<Vec<_>>()
            .join("\n\n")
    }
}

impl From<Vec<Fragment>> for Bodies {
    fn from(bodies: Vec<Fragment>) -> Self {
        let raw_body = bodies
            .iter()
            .filter_map(|values| {
                if let Fragment::Body(body) = values {
                    Some(body.clone())
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();

        let trailer_count = raw_body
            .clone()
            .into_iter()
            .rev()
            .take_while(|body| body.is_empty() || Trailer::try_from(body.clone()).is_ok())
            .count();
        let non_trailer_item_count = raw_body.len() - trailer_count;

        raw_body
            .into_iter()
            .enumerate()
            .skip(1)
            .take(non_trailer_item_count - 1)
            .map(|(_, body)| body)
            .collect::<Vec<Body>>()
            .into()
    }
}

#[cfg(test)]
mod tests {
    use super::Bodies;
    use crate::body::Body;
    use crate::fragment::Fragment;
    use indoc::indoc;
    use pretty_assertions::assert_eq;

    #[test]
    fn it_can_give_me_it_as_a_string() {
        let bodies = Bodies::from(vec![
            Body::from("Message Body"),
            Body::from("Another Message Body"),
        ]);

        assert_eq!(
            String::from(bodies),
            String::from(indoc!(
                "
                Message Body

                Another Message Body"
            ))
        )
    }

    #[test]
    fn it_can_parse_itself_from_an_ast() {
        let bodies = Bodies::from(vec![
            Fragment::Body(Body::from("Subject Line")),
            Fragment::Body(Body::default()),
            Fragment::Body(Body::from("Some content in the body of the message")),
            Fragment::Body(Body::default()),
            Fragment::Body(Body::from(indoc!(
                "
                Co-authored-by: Billie Thomposon <billie@example.com>
                Co-authored-by: Someone Else <someone@example.com>
                "
            ))),
        ]);

        assert_eq!(
            bodies,
            Bodies::from(vec![
                Body::default(),
                Body::from("Some content in the body of the message"),
            ])
        )
    }
}