buddy_up_lib/input/
mod.rs1use crate::BuddyError;
2use std::collections::HashMap;
3use std::io::BufReader;
4use std::io::Read;
5use tracing::info;
6
7#[derive(Clone, Debug, Default)]
18pub struct People {
19 people: HashMap<usize, String>,
20 evenizer: bool,
21}
22
23impl People {
24 pub fn from_csv<R: Read>(input: R) -> Result<Self, BuddyError> {
40 let reader = BufReader::new(input);
41 let mut rdr = csv::ReaderBuilder::new()
42 .has_headers(false)
43 .from_reader(reader);
44 let mut people = HashMap::new();
45 let mut tr_input_len = 0;
46 for rec in rdr.records() {
47 tr_input_len += 1;
48 let r = rec?;
49 let id = str::parse::<usize>(r.get(0).ok_or(BuddyError::CsvFormatError)?)
50 .map_err(|_| BuddyError::IdNotANumber)?;
51 let name = r.get(1).ok_or(BuddyError::CsvFormatError)?.to_string();
52
53 people.insert(id, name);
54 }
55
56 if people.len() != tr_input_len {
58 return Err(BuddyError::IdsNotUnique);
59 }
60 let ret = if people.len() % 2 != 0 {
61 people.insert(usize::MAX, "EVENIZER".to_string());
62 tracing::warn!(
63 "Input people are not even in number, so we can't pair everyone. One person will be left unpaired."
64 );
65 People {
66 people,
67 evenizer: true,
68 }
69 } else {
70 People {
71 people,
72 evenizer: false,
73 }
74 };
75
76 info!("Found {} records in input file.", ret.len());
77
78 Ok(ret)
79 }
80
81 pub fn len(&self) -> usize {
82 if self.has_evenizer() {
83 self.people.len() - 1
84 } else {
85 self.people.len()
86 }
87 }
88
89 pub fn has_evenizer(&self) -> bool {
91 self.evenizer
92 }
93
94 pub fn is_empty(&self) -> bool {
95 self.len() == 0
96 }
97
98 pub(crate) fn as_ids(&self) -> Vec<usize> {
99 self.people.keys().copied().collect()
100 }
101
102 pub(crate) fn name_from_id(&self, id: usize) -> Option<String> {
103 Some(self.people.get(&id)?.to_string())
104 }
105}
106
107#[cfg(test)]
108mod test {
109 use super::*;
110
111 #[test]
113 fn not_even() {
114 let csv = "1,Foo".as_bytes();
115 let r = People::from_csv(csv);
116 assert!(r.is_ok());
117 let r = r.unwrap();
118 assert_eq!(r.len(), 1);
119 assert!(r.has_evenizer());
120 }
121 #[test]
122 fn good() {
123 let csv = "1,Foo\n2,Bar".as_bytes();
124 let r = People::from_csv(csv);
125 assert!(r.is_ok());
126 assert_eq!(r.unwrap().len(), 2);
127 }
128 #[test]
129 fn id_not_number() {
130 let csv = "Baz,Foo\n2,Bar".as_bytes();
131 let r = People::from_csv(csv);
132 assert!(matches!(r, Err(BuddyError::IdNotANumber)));
133 }
134 #[test]
135 fn id_not_unique() {
136 let csv = "1,Foo\n1,Bar".as_bytes();
137 let r = People::from_csv(csv);
138 assert!(matches!(r, Err(BuddyError::IdsNotUnique)));
139 }
140 #[test]
141 fn csv_format_wrong() {
142 let csv = "1\n2".as_bytes();
143 let r = People::from_csv(csv);
144 assert!(matches!(r, Err(BuddyError::CsvFormatError)));
145 }
146}