ofdb_entities/
place.rs

1use std::str::FromStr;
2use time::Date;
3
4use crate::{
5    activity::*, contact::*, email::EmailAddress, id::*, links::*, location::*, review::*,
6    revision::*,
7};
8
9// Immutable part of a place.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct PlaceRoot {
12    pub id: Id,
13    pub license: String,
14}
15
16#[derive(Debug, Clone, Eq, PartialEq)]
17pub struct OpeningHours(String);
18
19#[derive(Debug, Clone, Eq, PartialEq)]
20pub struct OpeningHoursParseError;
21
22impl OpeningHours {
23    pub const fn min_len() -> usize {
24        4
25    }
26}
27
28impl FromStr for OpeningHours {
29    type Err = OpeningHoursParseError;
30
31    fn from_str(s: &str) -> Result<Self, Self::Err> {
32        let trimmed = s.trim();
33        if trimmed.len() < Self::min_len() {
34            return Err(OpeningHoursParseError);
35        }
36        Ok(Self(trimmed.to_string()))
37    }
38}
39
40impl From<String> for OpeningHours {
41    fn from(from: String) -> Self {
42        let res = Self(from);
43        debug_assert_eq!(Ok(&res), res.0.as_str().parse().as_ref());
44        res
45    }
46}
47
48impl From<OpeningHours> for String {
49    fn from(from: OpeningHours) -> Self {
50        from.0
51    }
52}
53
54// Mutable part of a place.
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct PlaceRevision {
57    pub revision: Revision,
58    pub created: Activity,
59    pub title: String,
60    pub description: String,
61    pub location: Location,
62    pub contact: Option<Contact>,
63    pub opening_hours: Option<OpeningHours>,
64    pub founded_on: Option<Date>,
65    pub links: Option<Links>,
66    pub tags: Vec<String>,
67}
68
69// Convenience type that merges the tuple (PlaceRoot, PlaceRevision)
70// into a single, flat struct.
71#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct Place {
73    pub id: Id,
74    pub license: String,
75    pub revision: Revision,
76    pub created: Activity,
77    pub title: String,
78    pub description: String,
79    pub location: Location,
80    pub contact: Option<Contact>,
81    pub opening_hours: Option<OpeningHours>,
82    pub founded_on: Option<Date>,
83    pub links: Option<Links>,
84    pub tags: Vec<String>,
85}
86
87impl Place {
88    pub fn strip_activity_details(self) -> Self {
89        Self {
90            created: self.created.anonymize(),
91            ..self
92        }
93    }
94
95    pub fn strip_contact_details(self) -> Self {
96        Self {
97            contact: None,
98            ..self
99        }
100    }
101
102    pub fn contact_email(&self) -> Option<&EmailAddress> {
103        self.contact.as_ref().and_then(|c| c.email.as_ref())
104    }
105
106    pub fn is_owned<'a>(&self, moderated_tags: impl IntoIterator<Item = &'a str>) -> bool {
107        // Exclusive ownership of events is determined by the associated tags
108        moderated_tags
109            .into_iter()
110            .any(|moderated_tag| self.tags.iter().any(|tag| tag == moderated_tag))
111    }
112}
113
114impl From<(PlaceRoot, PlaceRevision)> for Place {
115    fn from(from: (PlaceRoot, PlaceRevision)) -> Self {
116        let (
117            PlaceRoot { id, license },
118            PlaceRevision {
119                revision,
120                created,
121                title,
122                description,
123                location,
124                contact,
125                opening_hours,
126                founded_on,
127                links,
128                tags,
129            },
130        ) = from;
131        Self {
132            id,
133            license,
134            revision,
135            created,
136            title,
137            description,
138            location,
139            contact,
140            opening_hours,
141            founded_on,
142            links,
143            tags,
144        }
145    }
146}
147
148impl From<Place> for (PlaceRoot, PlaceRevision) {
149    fn from(from: Place) -> Self {
150        let Place {
151            id,
152            license,
153            revision,
154            created,
155            title,
156            description,
157            location,
158            contact,
159            opening_hours,
160            founded_on,
161            links,
162            tags,
163        } = from;
164        (
165            PlaceRoot { id, license },
166            PlaceRevision {
167                revision,
168                created,
169                title,
170                description,
171                location,
172                contact,
173                opening_hours,
174                founded_on,
175                links,
176                tags,
177            },
178        )
179    }
180}
181
182#[derive(Debug, Clone, PartialEq, Eq)]
183pub struct PlaceHistory {
184    pub place: PlaceRoot,
185    pub revisions: Vec<(PlaceRevision, Vec<ReviewStatusLog>)>,
186}