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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use std::collections::BTreeMap;

use crate::external::Vcs;
use crate::mit::{Author, Authors, VcsError};

/// # Errors
///
/// On IO failure
///
/// # Panics
///
/// Does not panic
pub fn get_config_authors(vcs: &dyn Vcs) -> Result<Authors, VcsError> {
    let raw_entries: BTreeMap<String, BTreeMap<String, String>> = vcs
        .entries(Some("mit.author.config.*"))?
        .iter()
        .map(|x| (x, x.trim_start_matches("mit.author.config.")))
        .map(|(x, y)| (x, y.split_terminator('.').collect::<Vec<_>>()))
        .try_fold::<_, _, Result<_, VcsError>>(BTreeMap::new(), |mut acc, (key, fragments)| {
            let mut fragment_iterator = fragments.iter();
            let initial = String::from(*fragment_iterator.next().unwrap());
            let part = String::from(*fragment_iterator.next().unwrap());

            let mut exisiting: BTreeMap<String, String> =
                acc.get(&initial).map(BTreeMap::clone).unwrap_or_default();
            exisiting.insert(part, String::from(vcs.get_str(key)?.unwrap()));

            acc.insert(initial, exisiting);
            Ok(acc)
        })?;

    Ok(Authors::new(
        raw_entries
            .iter()
            .filter_map(|(key, y)| {
                let name = y.get("name").map(String::clone);
                let email = y.get("email").map(String::clone);
                let signingkey: Option<String> = y.get("signingkey").map(String::clone);

                match (name, email, signingkey) {
                    (Some(name), Some(email), None) => {
                        Some((key, Author::new(&name, &email, None)))
                    }
                    (Some(name), Some(email), Some(signingkey)) => {
                        Some((key, Author::new(&name, &email, Some(&signingkey))))
                    }
                    _ => None,
                }
            })
            .fold(
                BTreeMap::new(),
                |mut acc: BTreeMap<String, Author>, (key, value): (&String, Author)| {
                    acc.insert(key.clone(), value);
                    acc
                },
            ),
    ))
}

#[cfg(test)]
mod tests {
    use std::collections::BTreeMap;

    use pretty_assertions::assert_eq;

    use crate::mit::cmd::get_config_authors::get_config_authors;
    use crate::mit::Authors;
    use crate::{external::InMemory, mit::Author};

    #[test]
    fn it_can_give_me_an_author() {
        let mut strings: BTreeMap<String, String> = BTreeMap::new();
        strings.insert("mit.author.config.zy.email".into(), "zy@example.com".into());
        strings.insert("mit.author.config.zy.name".into(), "Z Y".into());
        let vcs = InMemory::new(&mut strings);

        let actual = get_config_authors(&vcs).expect("Failed to read VCS config");
        let expected_author = Author::new("Z Y", "zy@example.com", None);
        let mut store = BTreeMap::new();
        store.insert("zy".into(), expected_author);
        let expected = Authors::new(store);
        assert_eq!(
            expected, actual,
            "Expected the mit config to be {:?}, instead got {:?}",
            expected, actual
        )
    }

    #[test]
    fn it_can_give_me_multiple_authors() {
        let mut strings: BTreeMap<String, String> = BTreeMap::new();
        strings.insert("mit.author.config.zy.email".into(), "zy@example.com".into());
        strings.insert("mit.author.config.zy.name".into(), "Z Y".into());
        strings.insert(
            "mit.author.config.bt.email".into(),
            "billie@example.com".into(),
        );
        strings.insert("mit.author.config.bt.name".into(), "Billie Thompson".into());
        strings.insert("mit.author.config.bt.signingkey".into(), "ABC".into());
        let vcs = InMemory::new(&mut strings);

        let actual = get_config_authors(&vcs).expect("Failed to read VCS config");
        let mut store = BTreeMap::new();
        store.insert("zy".into(), Author::new("Z Y", "zy@example.com", None));
        store.insert(
            "bt".into(),
            Author::new("Billie Thompson", "billie@example.com", Some("ABC")),
        );
        let expected = Authors::new(store);
        assert_eq!(
            expected, actual,
            "Expected the mit config to be {:?}, instead got {:?}",
            expected, actual
        )
    }

    #[test]
    fn broken_authors_are_skipped() {
        let mut strings: BTreeMap<String, String> = BTreeMap::new();
        strings.insert("mit.author.config.zy.name".into(), "Z Y".into());
        strings.insert(
            "mit.author.config.bt.email".into(),
            "billie@example.com".into(),
        );
        strings.insert("mit.author.config.bt.name".into(), "Billie Thompson".into());
        strings.insert("mit.author.config.bt.signingkey".into(), "ABC".into());
        let vcs = InMemory::new(&mut strings);

        let actual = get_config_authors(&vcs).expect("Failed to read VCS config");
        let mut store = BTreeMap::new();
        store.insert(
            "bt".into(),
            Author::new("Billie Thompson", "billie@example.com", Some("ABC")),
        );
        let expected = Authors::new(store);
        assert_eq!(
            expected, actual,
            "Expected the mit config to be {:?}, instead got {:?}",
            expected, actual
        )
    }
}