oparl-types 0.1.0

Type definitions for the OParl protocol
Documentation
// SPDX-FileCopyrightText: Politik im Blick developers
// SPDX-FileCopyrightText: Wolfgang Silbermayr <wolfgang@silbermayr.at>
//
// SPDX-License-Identifier: AGPL-3.0-or-later OR EUPL-1.2

use url::Url;

use crate::{
    namespace::PersonNamespaceUrl, BodyId, DateTime, EmailAddress, Keyword, Location, LocationId,
    Membership, Name, PersonId,
};

#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Person {
    pub id: PersonId,

    #[serde(rename = "type")]
    pub namespace: PersonNamespaceUrl,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub body: Option<BodyId>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub name: Option<Name>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub family_name: Option<Name>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub given_name: Option<Name>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub form_of_address: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub affix: Option<String>,

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub title: Vec<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub gender: Option<String>,

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub phone: Vec<String>,

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub email: Vec<EmailAddress>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub location: Option<LocationId>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub location_object: Option<Location>,

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub status: Vec<String>,

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub membership: Vec<Membership>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub life: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub life_source: Option<String>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub license: Option<Url>,

    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub keyword: Vec<Keyword>,

    pub created: DateTime,

    pub modified: DateTime,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub web: Option<Url>,

    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub deleted: Option<bool>,
}

#[cfg(test)]
mod serde_tests {
    use super::Person;
    use crate::{
        namespace::{MembershipNamespaceUrl, PersonNamespaceUrl},
        Membership,
    };

    use pretty_assertions::assert_eq;
    use serde_json::json;
    use time::macros::{date, datetime};

    fn example_person() -> Person {
        Person {
            id: "https://oparl.example.org/person/29"
                .parse()
                .expect("value must be parseable as id"),
            namespace: PersonNamespaceUrl::Identifier,
            body: Some(
                "https://oparl.example.org/body/0"
                    .parse()
                    .expect("value must be parseable as url"),
            ),
            name: Some("Prof. Dr. Max Mustermann".into()),
            family_name: Some("Mustermann".into()),
            given_name: Some("Max".into()),
            form_of_address: Some("Ratsfrau".into()),
            affix: None,
            title: vec!["Prof.".into(), "Dr.".into()],
            gender: Some("male".into()),
            email: vec!["max@mustermann.de"
                .parse()
                .expect("value must be parseable as email")],
            phone: vec!["+493012345678".into()],
            location: None,
            location_object: None,
            status: vec!["Bezirksbürgermeister".into()],
            membership: vec![
                Membership {
                    id: "https://oparl.example.org/memberships/385"
                        .parse()
                        .expect("value must be parseable as id"),
                    namespace: MembershipNamespaceUrl::Identifier,
                    person: None,
                    organization: Some(
                        "https://oparl.example.org/organizations/5"
                            .parse()
                            .expect("value must be parseable as url"),
                    ),
                    role: Some("Vorsitzende".into()),
                    voting_right: Some(true),
                    start_date: Some(date!(2013 - 12 - 03).into()),
                    end_date: None,
                    on_behalf_of: None,
                    license: None,
                    keyword: vec![],
                    created: datetime!(2011-11-11 11:11:00 +01:00).into(),
                    modified: datetime!(2012-08-16 14:05:27 +02:00).into(),
                    web: None,
                    deleted: None,
                },
                Membership {
                    id: "https://oparl.example.org/memberships/693"
                        .parse()
                        .expect("value must be parseable as id"),
                    namespace: MembershipNamespaceUrl::Identifier,
                    person: None,
                    organization: Some(
                        "https://oparl.example.org/organizations/9"
                            .parse()
                            .expect("value must be parseable as url"),
                    ),
                    role: Some("Sachkundige Bürgerin".into()),
                    voting_right: Some(false),
                    start_date: Some(date!(2013 - 12 - 03).into()),
                    end_date: Some(date!(2014 - 07 - 28).into()),
                    on_behalf_of: None,
                    license: None,
                    keyword: vec![],
                    created: datetime!(2011-11-11 11:11:00 +01:00).into(),
                    modified: datetime!(2012-08-16 14:05:27 +02:00).into(),
                    web: None,
                    deleted: None,
                },
            ],
            life: None,
            life_source: None,
            license: None,
            keyword: vec![],
            created: datetime!(2011-11-11 11:11:00 +01:00).into(),
            modified: datetime!(2012-08-16 14:05:27 +02:00).into(),
            web: None,
            deleted: None,
        }
    }

    fn example_person_json() -> serde_json::Value {
        json!({
            "id": "https://oparl.example.org/person/29",
            "type": "https://schema.oparl.org/1.1/Person",
            "body": "https://oparl.example.org/body/0",
            "name": "Prof. Dr. Max Mustermann",
            "familyName": "Mustermann",
            "givenName": "Max",
            "title": [
                "Prof.",
                "Dr."
            ],
            "formOfAddress": "Ratsfrau",
            "gender": "male",
            "email": [
                "max@mustermann.de"
            ],
            "phone": [
                "+493012345678"
            ],
            "status": [
                "Bezirksbürgermeister"
            ],
            "membership": [
                {
                    "id": "https://oparl.example.org/memberships/385",
                    "type": "https://schema.oparl.org/1.1/Membership",
                    "organization": "https://oparl.example.org/organizations/5",
                    "role": "Vorsitzende",
                    "votingRight": true,
                    "startDate": "2013-12-03",
                    "created": "2011-11-11T11:11:00+01:00",
                    "modified": "2012-08-16T14:05:27+02:00"
                },
                {
                    "id": "https://oparl.example.org/memberships/693",
                    "type": "https://schema.oparl.org/1.1/Membership",
                    "organization": "https://oparl.example.org/organizations/9",
                    "role": "Sachkundige Bürgerin",
                    "votingRight": false,
                    "startDate": "2013-12-03",
                    "endDate": "2014-07-28",
                    "created": "2011-11-11T11:11:00+01:00",
                    "modified": "2012-08-16T14:05:27+02:00"
                }
            ],
            "created": "2011-11-11T11:11:00+01:00",
            "modified": "2012-08-16T14:05:27+02:00"
        })
    }

    #[test]
    fn serialize() {
        assert_eq!(json!(example_person()), example_person_json());
    }

    #[test]
    fn deserialize_good() {
        let deserialized: Person = serde_json::from_value(example_person_json())
            .expect("value must be deserializable as Person");
        assert_eq!(deserialized, example_person());
    }

    #[test]
    fn deserialize_bad() {
        assert!(serde_json::from_value::<Person>(json!("xyzabcd")).is_err());
        assert!(serde_json::from_value::<Person>(json!(true)).is_err());
        assert!(serde_json::from_value::<Person>(json!(123)).is_err());
    }
}