europe_elects_csv/
lib.rs

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}