1use std::str::FromStr;
2use time::Date;
3
4use crate::{
5 activity::*, contact::*, email::EmailAddress, id::*, links::*, location::*, review::*,
6 revision::*,
7};
8
9#[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#[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#[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 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}