Skip to content

Commit

Permalink
Uniformity in the use of Cow instead of String (#32)
Browse files Browse the repository at this point in the history
* Uniformity in the use of Cow instead of String

* rustfmt

* Fix doctest
  • Loading branch information
nappa85 authored Dec 9, 2021
1 parent 788db78 commit 544ef9b
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 118 deletions.
138 changes: 56 additions & 82 deletions src/dgc.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::borrow::Cow;

use crate::{Recovery, Test, Vaccination};
use serde::{Deserialize, Deserializer, Serialize};

Expand All @@ -6,16 +8,16 @@ use serde::{Deserialize, Deserializer, Serialize};
pub struct DgcName {
/// The forename(s) of the person addressed in the certificate
#[serde(rename = "gn", skip_serializing_if = "Option::is_none")]
pub forename: Option<String>,
pub forename: Option<Cow<'static, str>>,
/// The surname or primary name(s) of the person addressed in the certificate
#[serde(rename = "fn", skip_serializing_if = "Option::is_none")]
pub surname: Option<String>,
pub surname: Option<Cow<'static, str>>,
/// The forename(s) of the person, transliterated ICAO 9303
#[serde(rename = "gnt", skip_serializing_if = "Option::is_none")]
pub forename_standard: Option<String>,
pub forename_standard: Option<Cow<'static, str>>,
/// The surname(s) of the person, transliterated ICAO 9303
#[serde(rename = "fnt")]
pub surname_standard: String,
pub surname_standard: Cow<'static, str>,
}

fn empty_if_null<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
Expand All @@ -32,13 +34,13 @@ where
pub struct Dgc {
/// The certificate version as per the published [schemas](https://github.com/ehn-dcc-development/ehn-dcc-schema).
#[serde(rename = "ver")]
pub version: String,
pub version: Cow<'static, str>,
/// The name of the person addressed in the DGC.
#[serde(rename = "nam")]
pub name: DgcName,
/// Date of Birth of the person addressed in the DGC. ISO 8601 date format restricted to range 1900-2099 or empty
#[serde(rename = "dob")]
pub date_of_birth: String,
pub date_of_birth: Cow<'static, str>,
/// Test Group
#[serde(
rename = "t",
Expand Down Expand Up @@ -79,31 +81,29 @@ impl Dgc {

#[cfg(test)]
mod tests {
use std::borrow::Cow;

use super::*;

#[test]
fn test_json_serialization() {
let expected_json = "{\"ver\":\"1.3.0\",\"nam\":{\"gn\":\"ALSTON\",\"fn\":\"BLAKE\",\"gnt\":\"ALSTON\",\"fnt\":\"BLAKE\"},\"dob\":\"1990-01-01\",\"t\":[{\"tg\":\"840539006\",\"tt\":\"LP6464-4\",\"sc\":\"2021-10-09T12:03:12Z\",\"tr\":\"260415000\",\"tc\":\"Alhosn One Day Surgery\",\"co\":\"AE\",\"is\":\"Ministry of Health & Prevention\",\"ci\":\"URN:UVCI:V1:AE:8KST0RH057HI8XKW3M8K2NAD06\"}]}";
let cert = Dgc {
version: String::from("1.3.0"),
version: "1.3.0".into(),
name: DgcName {
forename: Some(String::from("ALSTON")),
surname: Some(String::from("BLAKE")),
forename_standard: Some(String::from("ALSTON")),
surname_standard: String::from("BLAKE"),
forename: Some("ALSTON".into()),
surname: Some("BLAKE".into()),
forename_standard: Some("ALSTON".into()),
surname_standard: "BLAKE".into(),
},
date_of_birth: String::from("1990-01-01"),
date_of_birth: "1990-01-01".into(),
tests: vec![Test {
targeted_disease: Cow::from("840539006"),
test_type: Cow::from("LP6464-4"),
date_of_collection: String::from("2021-10-09T12:03:12Z"),
result: Cow::from("260415000"),
testing_centre: Some(String::from("Alhosn One Day Surgery")),
country: Cow::from("AE"),
issuer: Cow::from("Ministry of Health & Prevention"),
id: String::from("URN:UVCI:V1:AE:8KST0RH057HI8XKW3M8K2NAD06"),
targeted_disease: "840539006".into(),
test_type: "LP6464-4".into(),
date_of_collection: "2021-10-09T12:03:12Z".into(),
result: "260415000".into(),
testing_centre: Some("Alhosn One Day Surgery".into()),
country: "AE".into(),
issuer: "Ministry of Health & Prevention".into(),
id: "URN:UVCI:V1:AE:8KST0RH057HI8XKW3M8K2NAD06".into(),
name: None,
manufacturer: None,
date_of_result: None,
Expand Down Expand Up @@ -146,41 +146,29 @@ mod tests {
}
"#;
let cert: Dgc = serde_json::from_str(json_data).unwrap();
assert_eq!(cert.version, String::from("1.0.0"));
assert_eq!(cert.name.surname, Some(String::from("Di Caprio")));
assert_eq!(cert.name.surname_standard, String::from("DI<CAPRIO"));
assert_eq!(cert.name.forename, Some(String::from("Marilù Teresa")));
assert_eq!(
cert.name.forename_standard,
Some(String::from("MARILU<TERESA"))
);
assert_eq!(cert.date_of_birth, String::from("1977-06-16"));
assert_eq!(cert.tests[0].targeted_disease, String::from("840539006"));
assert_eq!(cert.tests[0].test_type, String::from("LP6464-4"));
assert_eq!(
cert.tests[0].name,
Some(String::from("Roche LightCycler qPCR"))
);
assert_eq!(cert.tests[0].manufacturer, Some(Cow::from("1232")));
assert_eq!(
cert.tests[0].date_of_collection,
String::from("2021-05-03T10:27:15Z")
);
assert_eq!(cert.version, "1.0.0");
assert_eq!(cert.name.surname, Some("Di Caprio".into()));
assert_eq!(cert.name.surname_standard, "DI<CAPRIO");
assert_eq!(cert.name.forename, Some("Marilù Teresa".into()));
assert_eq!(cert.name.forename_standard, Some("MARILU<TERESA".into()));
assert_eq!(cert.date_of_birth, "1977-06-16");
assert_eq!(cert.tests[0].targeted_disease, "840539006");
assert_eq!(cert.tests[0].test_type, "LP6464-4");
assert_eq!(cert.tests[0].name, Some("Roche LightCycler qPCR".into()));
assert_eq!(cert.tests[0].manufacturer, Some("1232".into()));
assert_eq!(cert.tests[0].date_of_collection, "2021-05-03T10:27:15Z");
assert_eq!(
cert.tests[0].date_of_result,
Some(String::from("2021-05-11T12:27:15Z"))
Some("2021-05-11T12:27:15Z".into())
);
assert_eq!(cert.tests[0].result, String::from("260415000"));
assert_eq!(cert.tests[0].result, "260415000");
assert_eq!(
cert.tests[0].testing_centre,
Some(String::from("Policlinico Umberto I"))
);
assert_eq!(cert.tests[0].country, String::from("IT"));
assert_eq!(cert.tests[0].issuer, String::from("IT"));
assert_eq!(
cert.tests[0].id,
String::from("01IT053059F7676042D9BEE9F874C4901F9B#3")
Some("Policlinico Umberto I".into())
);
assert_eq!(cert.tests[0].country, "IT");
assert_eq!(cert.tests[0].issuer, "IT");
assert_eq!(cert.tests[0].id, "01IT053059F7676042D9BEE9F874C4901F9B#3");
}

#[test]
Expand Down Expand Up @@ -213,48 +201,34 @@ mod tests {
"#;
let mut cert: Dgc = serde_json::from_str(json_data).unwrap();
cert.expand_values();
assert_eq!(cert.version, String::from("1.0.0"));
assert_eq!(cert.name.surname, Some(String::from("Di Caprio")));
assert_eq!(cert.name.surname_standard, String::from("DI<CAPRIO"));
assert_eq!(cert.name.forename, Some(String::from("Marilù Teresa")));
assert_eq!(
cert.name.forename_standard,
Some(String::from("MARILU<TERESA"))
);
assert_eq!(cert.date_of_birth, String::from("1977-06-16"));
assert_eq!(cert.tests[0].targeted_disease, String::from("COVID-19"));
assert_eq!(cert.version, "1.0.0");
assert_eq!(cert.name.surname, Some("Di Caprio".into()));
assert_eq!(cert.name.surname_standard, "DI<CAPRIO");
assert_eq!(cert.name.forename, Some("Marilù Teresa".into()));
assert_eq!(cert.name.forename_standard, Some("MARILU<TERESA".into()));
assert_eq!(cert.date_of_birth, "1977-06-16");
assert_eq!(cert.tests[0].targeted_disease, "COVID-19");
assert_eq!(
cert.tests[0].test_type,
String::from("Nucleic acid amplification with probe detection")
);
assert_eq!(
cert.tests[0].name,
Some(String::from("Roche LightCycler qPCR"))
"Nucleic acid amplification with probe detection"
);
assert_eq!(cert.tests[0].name, Some("Roche LightCycler qPCR".into()));
assert_eq!(
cert.tests[0].manufacturer,
Some(Cow::from(
"Abbott Rapid Diagnostics, Panbio COVID-19 Ag Rapid Test"
))
);
assert_eq!(
cert.tests[0].date_of_collection,
String::from("2021-05-03T10:27:15Z")
Some("Abbott Rapid Diagnostics, Panbio COVID-19 Ag Rapid Test".into())
);
assert_eq!(cert.tests[0].date_of_collection, "2021-05-03T10:27:15Z");
assert_eq!(
cert.tests[0].date_of_result,
Some(String::from("2021-05-11T12:27:15Z"))
Some("2021-05-11T12:27:15Z".into())
);
assert_eq!(cert.tests[0].result, String::from("Not detected"));
assert_eq!(cert.tests[0].result, "Not detected");
assert_eq!(
cert.tests[0].testing_centre,
Some(String::from("Policlinico Umberto I"))
);
assert_eq!(cert.tests[0].country, String::from("Italy"));
assert_eq!(cert.tests[0].issuer, String::from("Italy"));
assert_eq!(
cert.tests[0].id,
String::from("01IT053059F7676042D9BEE9F874C4901F9B#3")
Some("Policlinico Umberto I".into())
);
assert_eq!(cert.tests[0].country, "Italy");
assert_eq!(cert.tests[0].issuer, "Italy");
assert_eq!(cert.tests[0].id, "01IT053059F7676042D9BEE9F874C4901F9B#3");
}
}
4 changes: 2 additions & 2 deletions src/dgc_container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use serde::{
de::{MapAccess, Visitor},
Deserialize, Serialize,
};
use std::collections::HashMap;
use std::{borrow::Cow, collections::HashMap};

const ISSUER: i64 = 1;
const ISSUED_AT: i64 = 6;
Expand All @@ -15,7 +15,7 @@ const CERTS: i64 = -260;
pub struct DgcContainer {
/// The issuer of the data in the container
#[serde(rename = "1")]
pub issuer: String,
pub issuer: Cow<'static, str>,
/// A unix timestamp representing the moment in time when the data in the container was issued
#[serde(rename = "6")]
pub issued_at: IntegerOrFloat,
Expand Down
12 changes: 6 additions & 6 deletions src/recovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,23 @@ pub struct Recovery {
pub country: Cow<'static, str>,
/// Certificate Issuer
#[serde(rename = "is")]
pub issuer: String,
pub issuer: Cow<'static, str>,
/// ISO 8601 complete date: Certificate Valid From
#[serde(rename = "df")]
pub valid_from: String,
pub valid_from: Cow<'static, str>,
/// ISO 8601 complete date: Certificate Valid Until
#[serde(rename = "du")]
pub valid_until: String,
pub valid_until: Cow<'static, str>,
/// Unique Certificate Identifier, UVCI
#[serde(rename = "ci")]
pub id: String,
pub id: Cow<'static, str>,
}

impl Recovery {
/// Updates all the ids in the recovery entry with their descriptive counterparts using
/// the official valueset.
pub fn expand_values(&mut self) {
self.targeted_disease = lookup_value(&self.targeted_disease);
self.country = lookup_value(&self.country);
lookup_value(&mut self.targeted_disease);
lookup_value(&mut self.country);
}
}
24 changes: 12 additions & 12 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@ pub struct Test {
pub test_type: Cow<'static, str>,
/// NAA Test Name
#[serde(rename = "nm", skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
pub name: Option<Cow<'static, str>>,
/// RAT Test name and manufacturer
#[serde(rename = "ma", skip_serializing_if = "Option::is_none")]
pub manufacturer: Option<Cow<'static, str>>,
/// Date/Time of Sample Collection
#[serde(rename = "sc")]
pub date_of_collection: String,
pub date_of_collection: Cow<'static, str>,
/// Date/Time of Test Result
/// Deprecated in v1.3.0 of the schema
#[serde(rename = "dr", skip_serializing_if = "Option::is_none")]
pub date_of_result: Option<String>,
pub date_of_result: Option<Cow<'static, str>>,
/// Test Result
#[serde(rename = "tr")]
pub result: Cow<'static, str>,
/// Testing Centre
#[serde(rename = "tc", skip_serializing_if = "Option::is_none")]
pub testing_centre: Option<String>,
pub testing_centre: Option<Cow<'static, str>>,
/// Country of Test
#[serde(rename = "co")]
pub country: Cow<'static, str>,
Expand All @@ -40,20 +40,20 @@ pub struct Test {
pub issuer: Cow<'static, str>,
/// Unique Certificate Identifier, UVCI
#[serde(rename = "ci")]
pub id: String,
pub id: Cow<'static, str>,
}

impl Test {
/// Updates all the ids in the test entry with their descriptive counterparts using
/// the official valueset.
pub fn expand_values(&mut self) {
self.targeted_disease = lookup_value(&self.targeted_disease);
self.test_type = lookup_value(&self.test_type);
self.result = lookup_value(&self.result);
if let Some(ma) = &mut self.manufacturer {
*ma = lookup_value(ma);
lookup_value(&mut self.targeted_disease);
lookup_value(&mut self.test_type);
lookup_value(&mut self.result);
if let Some(ma) = self.manufacturer.as_mut() {
lookup_value(ma);
}
self.country = lookup_value(&self.country);
self.issuer = lookup_value(&self.issuer);
lookup_value(&mut self.country);
lookup_value(&mut self.issuer);
}
}
16 changes: 8 additions & 8 deletions src/vaccination.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,26 @@ pub struct Vaccination {
pub total_doses: usize,
/// ISO8601 complete date: Date of Vaccination
#[serde(rename = "dt")]
pub date: String,
pub date: Cow<'static, str>,
/// Country of Vaccination
#[serde(rename = "co")]
pub country: Cow<'static, str>,
/// Certificate Issuer
#[serde(rename = "is")]
pub issuer: String,
pub issuer: Cow<'static, str>,
/// Unique Certificate Identifier: UVCI
#[serde(rename = "ci")]
pub id: String,
pub id: Cow<'static, str>,
}

impl Vaccination {
/// Updates all the ids in the vaccination entry with their descriptive counterparts using
/// the official valueset.
pub fn expand_values(&mut self) {
self.targeted_disease = lookup_value(&self.targeted_disease);
self.vaccine_prophylaxis = lookup_value(&self.vaccine_prophylaxis);
self.medicinal_product = lookup_value(&self.medicinal_product);
self.manufacturer = lookup_value(&self.manufacturer);
self.country = lookup_value(&self.country);
lookup_value(&mut self.targeted_disease);
lookup_value(&mut self.vaccine_prophylaxis);
lookup_value(&mut self.medicinal_product);
lookup_value(&mut self.manufacturer);
lookup_value(&mut self.country);
}
}
26 changes: 18 additions & 8 deletions src/valuesets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,27 @@ use std::borrow::Cow;
/// ```
/// # use dgc::lookup_value;
/// #
/// assert_eq!(lookup_value("AQ"), "Antarctica");
/// assert_eq!(lookup_value("840539006"), "COVID-19");
/// assert_eq!(lookup_value("LP217198-3"), "Rapid immunoassay");
/// assert_eq!(lookup_value("EU/1/20/1528"), "Comirnaty");
/// assert_eq!(lookup_value("value not in valueset"), "value not in valueset");
/// let mut value = "AQ".into();
/// lookup_value(&mut value);
/// assert_eq!(value, "Antarctica");
/// let mut value = "840539006".into();
/// lookup_value(&mut value);
/// assert_eq!(value, "COVID-19");
/// let mut value = "LP217198-3".into();
/// lookup_value(&mut value);
/// assert_eq!(value, "Rapid immunoassay");
/// let mut value = "EU/1/20/1528".into();
/// lookup_value(&mut value);
/// assert_eq!(value, "Comirnaty");
/// let mut value = "value not in valueset".into();
/// lookup_value(&mut value);
/// assert_eq!(value, "value not in valueset");
/// ```
pub fn lookup_value(value_id: &str) -> Cow<'static, str> {
pub fn lookup_value(value_id: &mut Cow<'static, str>) {
// Populated from https://github.com/ehn-dcc-development/ehn-dcc-schema/tree/release/1.3.0/valuesets
// List generated with the following Node.js snippet (for every valueset file):
// > for (const [key, val] of Object.entries(fileData.valueSetValues)) { console.log(`"${key}" => Cow::Borrowed("${val.display}"),`) }
match value_id {
*value_id = match value_id.as_ref() {
// https://github.com/ehn-dcc-development/ehn-dcc-schema/blob/release/1.3.0/valuesets/country-2-codes.json
"AD" => Cow::Borrowed("Andorra"),
"AE" => Cow::Borrowed("United Arab Emirates"),
Expand Down Expand Up @@ -387,6 +397,6 @@ pub fn lookup_value(value_id: &str) -> Cow<'static, str> {
"1119305005" => Cow::Borrowed("SARS-CoV-2 antigen vaccine"),
"1119349007" => Cow::Borrowed("SARS-CoV-2 mRNA vaccine"),
"J07BX03" => Cow::Borrowed("covid-19 vaccines"),
_ => Cow::Owned(value_id.to_owned()),
_ => return,
}
}

0 comments on commit 544ef9b

Please sign in to comment.