1mod errors;
2use errors::PollTableError;
3use std::{collections::HashMap, path::Path};
4use chrono::NaiveDate;
5use csv::ReaderBuilder;
6use serde::{Deserialize, Deserializer};
7
8pub struct PollTable {
9 pub polls: Vec<Poll>
10}
11
12#[derive(Debug, Deserialize)]
13pub struct Poll {
14 #[serde(rename = "Polling Firm")]
15 pub polling_firm: String,
16 #[serde(rename = "Commissioners")]
17 pub commissioners: PollOption<String>,
18 #[serde(rename = "Fieldwork Start")]
19 pub fieldwork_start: NaiveDate,
20 #[serde(rename = "Fieldwork End")]
21 pub fieldwork_end: NaiveDate,
22 #[serde(rename = "Scope")]
23 pub scope: Scope,
24 #[serde(rename = "Sample Size")]
25 pub sample_size: PollOption<f32>,
26 #[serde(rename = "Sample Size Qualification")]
27 pub sample_size_qualification: PollOption<SampleSizeQualification>,
28 #[serde(rename = "Participation")]
29 pub participation: PollOption<Percentage>,
30 #[serde(rename = "Precision")]
31 pub precision: PollOption<PercentageOrSeats>,
32 #[serde(flatten)]
33 pub party_names: HashMap<String, PollOption<PercentageOrSeats>>,
34 #[serde(rename = "Other")]
35 pub other: PollOption<PercentageOrSeats>,
36}
37
38impl PollTable {
39 pub fn from_path(path: &Path) -> Result<PollTable, PollTableError> {
40 let mut rdr = ReaderBuilder::new().from_path(path)?;
41 let mut polls: Vec<Poll> = Vec::new();
42 for result in rdr.deserialize() {
43 let record: Poll = result?;
44 polls.push(record);
45 }
46 Ok(PollTable { polls })
47 }
48}
49
50#[derive(Debug)]
51pub struct Percentage(f32);
52#[derive(Debug)]
53pub struct Seats(f32);
54
55#[derive(Debug)]
56pub enum PollOption<T> {
57 NotAvailable,
58 Some(T),
59}
60
61#[derive(Debug)]
62pub enum Scope {
63 National,
64 European,
65}
66
67#[derive(Debug)]
68pub enum SampleSizeQualification {
69 Provided,
70 EstimatedAssumed,
71}
72
73#[derive(Debug)]
74pub enum PercentageOrSeats {
75 Percentage(Percentage),
76 Seats(Seats),
77}
78
79impl<'de> Deserialize<'de> for PollOption<String> {
80 fn deserialize<D>(deserializer: D) -> Result<PollOption<String>, D::Error>
81 where
82 D: Deserializer<'de>,
83 {
84 let val: String = Deserialize::deserialize(deserializer)?;
85 match val.as_str() {
86 "Not Available" | "N/A" => Ok(PollOption::NotAvailable),
87 _ => Ok(PollOption::Some(val.to_string())),
88 }
89 }
90}
91
92impl<'de> Deserialize<'de> for Scope {
93 fn deserialize<D>(deserializer: D) -> Result<Scope, D::Error>
94 where
95 D: Deserializer<'de>,
96 {
97 let val: String = Deserialize::deserialize(deserializer)?;
98 match val.as_str() {
99 "National" => Ok(Scope::National),
100 "European" => Ok(Scope::European),
101 _ => Err(serde::de::Error::custom("Failed to parse Scope")),
102 }
103 }
104}
105
106impl<'de> Deserialize<'de> for PollOption<f32> {
107 fn deserialize<D>(deserializer: D) -> Result<PollOption<f32>, D::Error>
108 where
109 D: Deserializer<'de>,
110 {
111 let val: String = Deserialize::deserialize(deserializer)?;
112 if let Ok(val) = val.parse::<f32>() {
113 Ok(PollOption::Some(val))
114 } else {
115 match val.as_str() {
116 "Not Available" | "N/A" => Ok(PollOption::NotAvailable),
117 _ => Err(serde::de::Error::custom("Failed to parse PollOption<f32>")),
118 }
119 }
120 }
121}
122
123impl<'de> Deserialize<'de> for PollOption<SampleSizeQualification> {
124 fn deserialize<D>(deserializer: D) -> Result<PollOption<SampleSizeQualification>, D::Error>
125 where
126 D: Deserializer<'de>,
127 {
128 let val: String = Deserialize::deserialize(deserializer)?;
129 match val.as_str() {
130 "Provided" => Ok(PollOption::Some(SampleSizeQualification::Provided)),
131 "Estimated/Assumed" => Ok(PollOption::Some(SampleSizeQualification::EstimatedAssumed)),
132 "Not Available" | "N/A" => Ok(PollOption::NotAvailable),
133 _ => Err(serde::de::Error::custom(
134 "Failed to parse SampleSizeQualification",
135 )),
136 }
137 }
138}
139
140impl<'de> Deserialize<'de> for PollOption<Percentage> {
141 fn deserialize<D>(deserializer: D) -> Result<PollOption<Percentage>, D::Error>
142 where
143 D: Deserializer<'de>,
144 {
145 let val: String = Deserialize::deserialize(deserializer)?;
146
147 if val.contains('%') {
148 let val = val
149 .trim_end_matches('%')
150 .parse::<f32>()
151 .expect("Should be able to parse percentage as f32");
152 Ok(PollOption::Some(Percentage(val)))
153 } else if val.contains("Not Available") || val.contains("N/A") {
154 Ok(PollOption::NotAvailable)
155 } else {
156 Err(serde::de::Error::custom(
157 "Failed to parse PollOption<Percentage>",
158 ))
159 }
160 }
161}
162
163impl<'de> Deserialize<'de> for PollOption<PercentageOrSeats> {
164 fn deserialize<D>(deserializer: D) -> Result<PollOption<PercentageOrSeats>, D::Error>
165 where
166 D: Deserializer<'de>,
167 {
168 let val: String = Deserialize::deserialize(deserializer)?;
169
170 match val.as_str() {
171 "Not Available" | "N/A" => Ok(PollOption::NotAvailable),
172 _ => {
173 if val.contains('%') {
174 let val = val
175 .trim_end_matches('%')
176 .parse::<f32>()
177 .expect("Should be able to parse percentage as f32");
178 Ok(PollOption::Some(PercentageOrSeats::Percentage(Percentage(
179 val,
180 ))))
181 } else {
182 match val.parse::<f32>() {
183 Ok(val) => Ok(PollOption::Some(PercentageOrSeats::Seats(Seats(val)))),
184 Err(_) => Err(serde::de::Error::custom("Seats could not be parsed as f32")),
185 }
186 }
187 }
188 }
189 }
190}