|
| 1 | +#[macro_use] |
| 2 | +extern crate serde_derive; |
| 3 | + |
| 4 | +use std::{fmt, io}; |
| 5 | + |
| 6 | +use serde::{ |
| 7 | + de::{self, Deserializer, Visitor}, |
| 8 | + ser::Serializer, |
| 9 | +}; |
| 10 | +use time::OffsetDateTime; |
| 11 | + |
| 12 | +#[derive(Debug, Deserialize, Serialize)] |
| 13 | +struct Response { |
| 14 | + #[serde(rename = "Timestamp")] |
| 15 | + #[serde(deserialize_with = "deserialize_timestamp")] |
| 16 | + #[serde(serialize_with = "serialize_timestamp")] |
| 17 | + timestamp: OffsetDateTime, |
| 18 | + |
| 19 | + #[serde( |
| 20 | + rename = "Do you use the standard library’s lock types (Mutex or RwLock) in any of your projects?" |
| 21 | + )] |
| 22 | + use_stdlib_locks: Option<String>, |
| 23 | + |
| 24 | + #[serde( |
| 25 | + rename = "Do you use locks from outside the standard library (such as from antidote or parking_lot) in any of your projects?" |
| 26 | + )] |
| 27 | + use_non_stdlib_locks: Option<String>, |
| 28 | + |
| 29 | + #[serde( |
| 30 | + rename = "Which of these is closest to how you acquire locks? (Select all that apply)" |
| 31 | + )] |
| 32 | + #[serde(deserialize_with = "deserialize_semi_colon_list")] |
| 33 | + #[serde(default)] |
| 34 | + acquire_locks: Vec<String>, |
| 35 | + |
| 36 | + #[serde(rename = "Have you ever thought carefully about lock poisoning in your projects?")] |
| 37 | + thought_carefully_about_locks: Option<String>, |
| 38 | + |
| 39 | + #[serde( |
| 40 | + rename = "If so, what do you want to happen when a lock is poisoned? (Select all that apply)" |
| 41 | + )] |
| 42 | + #[serde(deserialize_with = "deserialize_semi_colon_list")] |
| 43 | + #[serde(default)] |
| 44 | + when_lock_is_poisoned: Vec<String>, |
| 45 | + |
| 46 | + #[serde( |
| 47 | + rename = "Do you think you’ve benefited from the standard library’s lock types providing poisoning by default?" |
| 48 | + )] |
| 49 | + benefitted_from_poisoning: Option<String>, |
| 50 | + |
| 51 | + #[serde( |
| 52 | + rename = "Do you use the standard library’s lock types or their guards in the public API of any of your projects?" |
| 53 | + )] |
| 54 | + stdlib_locks_in_pub: Option<String>, |
| 55 | + |
| 56 | + #[serde( |
| 57 | + rename = "If so, what’s an example of how the standard library’s lock or guard types appear in the public API of any of your projects? (Please keep accidental information leakage in mind here and consider replacing names with placeholders)" |
| 58 | + )] |
| 59 | + example_of_stdlib_locks_in_pub: Option<String>, |
| 60 | + |
| 61 | + #[serde( |
| 62 | + rename = "How much friction do you think would be involved in migrating any of your projects from the standard library’s poisoning lock types to a non-poisoning lock crate like antidote or parking_lot? (That would mean replacing .lock().unwrap() with .lock())" |
| 63 | + )] |
| 64 | + friction_in_migrating_from_poisoning: Option<String>, |
| 65 | + |
| 66 | + #[serde( |
| 67 | + rename = "Why do you use locks from outside the standard library? (Select all that apply)" |
| 68 | + )] |
| 69 | + #[serde(deserialize_with = "deserialize_semi_colon_list")] |
| 70 | + #[serde(default)] |
| 71 | + why_non_stdlib_locks: Vec<String>, |
| 72 | + |
| 73 | + #[serde(rename = "Do you implement poisoning some other way?")] |
| 74 | + alt_poisoning_impl: Option<String>, |
| 75 | + |
| 76 | + #[serde( |
| 77 | + rename = "If so, what do you want to happen when data is poisoned? (Select all that apply)" |
| 78 | + )] |
| 79 | + #[serde(deserialize_with = "deserialize_semi_colon_list")] |
| 80 | + #[serde(default)] |
| 81 | + when_alt_poisoning_is_poisoned: Vec<String>, |
| 82 | + |
| 83 | + #[serde( |
| 84 | + rename = "Would you use a poisoning implementation from the standard library if it was independent of Mutex or RwLock?" |
| 85 | + )] |
| 86 | + use_stdlib_alt_poisoning: Option<String>, |
| 87 | +} |
| 88 | + |
| 89 | +fn deserialize_semi_colon_list<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error> |
| 90 | +where |
| 91 | + D: Deserializer<'de>, |
| 92 | +{ |
| 93 | + struct SeparatedListVisitor(char); |
| 94 | + |
| 95 | + impl<'de> Visitor<'de> for SeparatedListVisitor { |
| 96 | + type Value = Vec<String>; |
| 97 | + |
| 98 | + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 99 | + write!(f, "a string of values separated by `{}`", self.0) |
| 100 | + } |
| 101 | + |
| 102 | + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> |
| 103 | + where |
| 104 | + E: de::Error, |
| 105 | + { |
| 106 | + if v.is_empty() { |
| 107 | + return Ok(Vec::new()); |
| 108 | + } |
| 109 | + |
| 110 | + Ok(v.split(self.0).map(|v| v.to_owned()).collect()) |
| 111 | + } |
| 112 | + } |
| 113 | + |
| 114 | + deserializer.deserialize_str(SeparatedListVisitor(';')) |
| 115 | +} |
| 116 | + |
| 117 | +fn deserialize_timestamp<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error> |
| 118 | +where |
| 119 | + D: Deserializer<'de>, |
| 120 | +{ |
| 121 | + struct TimestampVisitor; |
| 122 | + |
| 123 | + impl<'de> Visitor<'de> for TimestampVisitor { |
| 124 | + type Value = OffsetDateTime; |
| 125 | + |
| 126 | + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 127 | + write!(f, "a timestamp string") |
| 128 | + } |
| 129 | + |
| 130 | + fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> |
| 131 | + where |
| 132 | + E: de::Error, |
| 133 | + { |
| 134 | + let mut parts = v.split("GMT"); |
| 135 | + let dt = parts |
| 136 | + .next() |
| 137 | + .ok_or_else(|| E::custom("missing datetime part of timestamp"))? |
| 138 | + .trim(); |
| 139 | + let offset = parts |
| 140 | + .next() |
| 141 | + .ok_or_else(|| E::custom("missing offset part of timestamp"))? |
| 142 | + .trim(); |
| 143 | + |
| 144 | + let dt = time::PrimitiveDateTime::parse(dt, "%Y/%m/%d %-I:%M:%S %P") |
| 145 | + .map_err(|e| E::custom(e))?; |
| 146 | + |
| 147 | + Ok(dt.assume_offset(time::UtcOffset::hours({ |
| 148 | + if offset.starts_with("+") { |
| 149 | + &offset[1..] |
| 150 | + } else { |
| 151 | + offset |
| 152 | + } |
| 153 | + .parse() |
| 154 | + .map_err(|e| E::custom(e)) |
| 155 | + }?))) |
| 156 | + } |
| 157 | + } |
| 158 | + |
| 159 | + deserializer.deserialize_str(TimestampVisitor) |
| 160 | +} |
| 161 | + |
| 162 | +fn serialize_timestamp<S>(value: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error> |
| 163 | +where |
| 164 | + S: Serializer, |
| 165 | +{ |
| 166 | + serializer.collect_str(&value.lazy_format("%Y-%0m-%0dT%0H:%0M:%0S%z")) |
| 167 | +} |
| 168 | + |
| 169 | +fn main() { |
| 170 | + let results = csv::ReaderBuilder::new() |
| 171 | + .from_path("poisoning-survey.csv") |
| 172 | + .expect("failed to create csv reader over results file") |
| 173 | + .deserialize() |
| 174 | + .map(|row| row.expect("failed to deserialize a row")) |
| 175 | + .collect::<Vec<Response>>(); |
| 176 | + |
| 177 | + let stdout = io::stdout(); |
| 178 | + serde_json::to_writer(stdout.lock(), &results).expect("failed to write JSON"); |
| 179 | +} |
0 commit comments