1use super::*;
2use ofdb_entities as e;
3use thiserror::Error;
4
5impl From<e::time::Timestamp> for UnixTimeMillis {
6 fn from(from: e::time::Timestamp) -> Self {
7 Self(from.as_millis())
8 }
9}
10
11impl From<e::time::Timestamp> for UnixTimeSeconds {
12 fn from(from: e::time::Timestamp) -> Self {
13 Self(from.as_secs())
14 }
15}
16
17impl TryFrom<UnixTimeSeconds> for e::time::Timestamp {
18 type Error = e::time::OutOfRangeError;
19 fn try_from(from: UnixTimeSeconds) -> Result<Self, Self::Error> {
20 Self::try_from_secs(from.0)
21 }
22}
23
24impl TryFrom<UnixTimeMillis> for e::time::Timestamp {
25 type Error = e::time::OutOfRangeError;
26 fn try_from(from: UnixTimeMillis) -> Result<Self, Self::Error> {
27 Self::try_from_millis(from.0)
28 }
29}
30
31impl From<e::links::CustomLink> for CustomLink {
32 fn from(from: e::links::CustomLink) -> Self {
33 let e::links::CustomLink {
34 url,
35 title,
36 description,
37 } = from;
38 Self {
39 url: url.to_string(),
40 title,
41 description,
42 }
43 }
44}
45
46impl From<CustomLink> for e::links::CustomLink {
48 fn from(from: CustomLink) -> Self {
49 let CustomLink {
50 url,
51 title,
52 description,
53 } = from;
54 Self {
55 url: url.parse().unwrap(),
56 title,
57 description,
58 }
59 }
60}
61
62impl From<e::category::Category> for Category {
63 fn from(from: e::category::Category) -> Self {
64 let name = from.name();
65 Self {
66 id: from.id.into(),
67 name,
68 }
69 }
70}
71
72impl From<e::review::ReviewStatus> for ReviewStatus {
73 fn from(from: e::review::ReviewStatus) -> Self {
74 use e::review::ReviewStatus::*;
75 match from {
76 Archived => ReviewStatus::Archived,
77 Confirmed => ReviewStatus::Confirmed,
78 Created => ReviewStatus::Created,
79 Rejected => ReviewStatus::Rejected,
80 }
81 }
82}
83
84impl From<ReviewStatus> for e::review::ReviewStatus {
85 fn from(from: ReviewStatus) -> Self {
86 use e::review::ReviewStatus::*;
87 match from {
88 ReviewStatus::Archived => Archived,
89 ReviewStatus::Confirmed => Confirmed,
90 ReviewStatus::Created => Created,
91 ReviewStatus::Rejected => Rejected,
92 }
93 }
94}
95
96impl From<e::user::User> for User {
97 fn from(from: e::user::User) -> Self {
98 let e::user::User {
99 email,
100 email_confirmed,
101 role,
102 password: _password,
103 } = from;
104 Self {
105 email: email.to_string(),
106 email_confirmed,
107 role: role.into(),
108 }
109 }
110}
111
112impl From<e::user::Role> for UserRole {
113 fn from(from: e::user::Role) -> Self {
114 use e::user::Role::*;
115 match from {
116 Guest => UserRole::Guest,
117 User => UserRole::User,
118 Scout => UserRole::Scout,
119 Admin => UserRole::Admin,
120 }
121 }
122}
123
124impl From<UserRole> for e::user::Role {
125 fn from(from: UserRole) -> Self {
126 use e::user::Role::*;
127 match from {
128 UserRole::Guest => Guest,
129 UserRole::User => User,
130 UserRole::Scout => Scout,
131 UserRole::Admin => Admin,
132 }
133 }
134}
135
136impl From<Coordinate> for e::geo::MapPoint {
137 fn from(c: Coordinate) -> Self {
138 e::geo::MapPoint::try_from_lat_lng_deg(c.lat, c.lng).unwrap_or_default()
139 }
140}
141
142impl From<e::tag::TagFrequency> for TagFrequency {
143 fn from(from: e::tag::TagFrequency) -> Self {
144 Self(from.0, from.1)
145 }
146}
147
148impl From<e::rating::RatingContext> for RatingContext {
149 fn from(from: e::rating::RatingContext) -> Self {
150 use e::rating::RatingContext as E;
151 use RatingContext as C;
152 match from {
153 E::Diversity => C::Diversity,
154 E::Renewable => C::Renewable,
155 E::Fairness => C::Fairness,
156 E::Humanity => C::Humanity,
157 E::Transparency => C::Transparency,
158 E::Solidarity => C::Solidarity,
159 }
160 }
161}
162
163impl From<RatingContext> for e::rating::RatingContext {
164 fn from(from: RatingContext) -> Self {
165 use e::rating::RatingContext as E;
166 use RatingContext as C;
167 match from {
168 C::Diversity => E::Diversity,
169 C::Renewable => E::Renewable,
170 C::Fairness => E::Fairness,
171 C::Humanity => E::Humanity,
172 C::Transparency => E::Transparency,
173 C::Solidarity => E::Solidarity,
174 }
175 }
176}
177
178impl From<e::rating::AvgRatingValue> for AvgRatingValue {
179 fn from(v: e::rating::AvgRatingValue) -> Self {
180 let v: f64 = v.into();
181 AvgRatingValue::from(v)
182 }
183}
184
185impl From<e::rating::RatingValue> for RatingValue {
186 fn from(v: e::rating::RatingValue) -> Self {
187 let v: i8 = v.into();
188 RatingValue::from(v)
189 }
190}
191
192impl From<RatingValue> for e::rating::RatingValue {
193 fn from(v: RatingValue) -> Self {
194 e::rating::RatingValue::from(v.0)
195 }
196}
197
198impl From<e::event::Event> for Event {
199 fn from(e: e::event::Event) -> Self {
200 let e::event::Event {
201 id,
202 title,
203 description,
204 start,
205 end,
206 location,
207 contact,
208 tags,
209 homepage,
210 registration,
211 image_url,
212 image_link_url,
213 ..
214 } = e;
215
216 let (lat, lng, address) = if let Some(location) = location {
217 if location.pos.is_valid() {
218 let lat = location.pos.lat().to_deg();
219 let lng = location.pos.lng().to_deg();
220 (Some(lat), Some(lng), location.address)
221 } else {
222 (None, None, location.address)
223 }
224 } else {
225 (None, None, None)
226 };
227
228 let e::address::Address {
229 street,
230 zip,
231 city,
232 country,
233 state,
234 } = address.unwrap_or_default();
235
236 let e::contact::Contact {
237 name: organizer,
238 email,
239 phone: telephone,
240 } = contact.unwrap_or_default();
241
242 let registration = registration.map(|r| {
243 match r {
244 e::event::RegistrationType::Email => "email",
245 e::event::RegistrationType::Phone => "telephone",
246 e::event::RegistrationType::Homepage => "homepage",
247 }
248 .to_string()
249 });
250
251 let start = start.into();
252 let end = end.map(e::time::Timestamp::from).map(Into::into);
253
254 Event {
255 id: id.into(),
256 title,
257 description,
258 start,
259 end,
260 lat,
261 lng,
262 street,
263 zip,
264 city,
265 country,
266 state,
267 email: email.map(e::email::EmailAddress::into_string),
268 telephone,
269 homepage: homepage.map(Into::into),
270 tags,
271 registration,
272 organizer,
273 image_url: image_url.map(Into::into),
274 image_link_url: image_link_url.map(Into::into),
275 }
276 }
277}
278
279impl From<e::clearance::PendingClearanceForPlace> for PendingClearanceForPlace {
280 fn from(from: e::clearance::PendingClearanceForPlace) -> Self {
281 let e::clearance::PendingClearanceForPlace {
282 place_id,
283 created_at,
284 last_cleared_revision,
285 } = from;
286 Self {
287 place_id: place_id.into(),
288 created_at: created_at.into(),
289 last_cleared_revision: last_cleared_revision.map(Into::into),
290 }
291 }
292}
293
294impl From<ClearanceForPlace> for e::clearance::ClearanceForPlace {
295 fn from(from: ClearanceForPlace) -> Self {
296 let ClearanceForPlace {
297 place_id,
298 cleared_revision,
299 } = from;
300 Self {
301 place_id: place_id.into(),
302 cleared_revision: cleared_revision.map(Into::into),
303 }
304 }
305}
306
307impl From<e::geo::MapPoint> for LatLonDegrees {
308 fn from(from: e::geo::MapPoint) -> Self {
309 Self(from.lat().to_deg(), from.lng().to_deg())
310 }
311}
312
313impl From<e::geo::MapPoint> for MapPoint {
314 fn from(from: e::geo::MapPoint) -> Self {
315 Self {
316 lat: from.lat().to_deg(),
317 lng: from.lng().to_deg(),
318 }
319 }
320}
321
322impl TryFrom<LatLonDegrees> for e::geo::MapPoint {
323 type Error = e::geo::CoordRangeError;
324
325 fn try_from(from: LatLonDegrees) -> Result<Self, Self::Error> {
326 e::geo::MapPoint::try_from_lat_lng_deg(from.0, from.1)
327 }
328}
329
330impl From<e::geo::MapBbox> for MapBbox {
331 fn from(bbox: e::geo::MapBbox) -> Self {
332 Self {
333 sw: MapPoint::from(bbox.southwest()),
334 ne: MapPoint::from(bbox.northeast()),
335 }
336 }
337}
338
339impl From<e::address::Address> for Address {
340 fn from(from: e::address::Address) -> Self {
341 let e::address::Address {
342 street,
343 zip,
344 city,
345 country,
346 state,
347 } = from;
348 Self {
349 street,
350 zip,
351 city,
352 country,
353 state,
354 }
355 }
356}
357
358impl From<Address> for e::address::Address {
359 fn from(from: Address) -> Self {
360 let Address {
361 street,
362 zip,
363 city,
364 country,
365 state,
366 } = from;
367 Self {
368 street,
369 zip,
370 city,
371 country,
372 state,
373 }
374 }
375}
376
377impl From<e::location::Location> for Location {
378 fn from(from: e::location::Location) -> Self {
379 let e::location::Location { pos, address } = from;
380 Self {
381 latlon: pos.into(),
382 address: address.map(Into::into).unwrap_or_default(),
383 }
384 }
385}
386
387impl From<Location> for e::location::Location {
389 fn from(from: Location) -> Self {
390 let Location { latlon, address } = from;
391 Self {
392 pos: latlon.try_into().unwrap(),
393 address: Some(address.into()),
394 }
395 }
396}
397
398impl From<e::contact::Contact> for Contact {
399 fn from(from: e::contact::Contact) -> Self {
400 let e::contact::Contact { name, email, phone } = from;
401 Self {
402 name,
403 email: email.map(e::email::EmailAddress::into_string),
404 phone,
405 }
406 }
407}
408
409impl From<e::links::Links> for Links {
410 fn from(from: e::links::Links) -> Self {
411 let e::links::Links {
412 homepage,
413 image,
414 image_href,
415 custom,
416 } = from;
417 Self {
418 homepage: homepage.map(Into::into),
419 image: image.map(Into::into),
420 image_href: image_href.map(Into::into),
421 custom: custom.into_iter().map(Into::into).collect(),
422 }
423 }
424}
425
426impl From<Links> for e::links::Links {
427 fn from(from: Links) -> Self {
428 let Links {
429 homepage,
430 image,
431 image_href,
432 custom,
433 } = from;
434 Self {
435 homepage: homepage.and_then(|url| url.parse().ok()),
436 image: image.and_then(|url| url.parse().ok()),
437 image_href: image_href.and_then(|url| url.parse().ok()),
438 custom: custom.into_iter().map(Into::into).collect(),
439 }
440 }
441}
442
443#[derive(Debug, Error)]
444pub enum ContactConversionError {
445 #[error(transparent)]
446 Email(#[from] e::email::EmailAddressParseError),
447}
448
449impl TryFrom<Contact> for e::contact::Contact {
450 type Error = ContactConversionError;
451 fn try_from(from: Contact) -> Result<Self, Self::Error> {
452 let Contact { name, email, phone } = from;
453 let email = email.map(|email| email.parse()).transpose()?;
454 Ok(Self { name, email, phone })
455 }
456}
457
458impl From<e::activity::Activity> for Activity {
459 fn from(from: e::activity::Activity) -> Self {
460 let e::activity::Activity { at, by } = from;
461 Self {
462 at: at.into(),
463 by: by.map(e::email::EmailAddress::into_string),
464 }
465 }
466}
467
468#[derive(Debug, Error)]
469pub enum ActivityConversionError {
470 #[error(transparent)]
471 Email(#[from] e::email::EmailAddressParseError),
472 #[error(transparent)]
473 Time(#[from] e::time::OutOfRangeError),
474}
475
476impl TryFrom<Activity> for e::activity::Activity {
477 type Error = ActivityConversionError;
478 fn try_from(from: Activity) -> Result<Self, Self::Error> {
479 let Activity { at, by } = from;
480 let by = by.map(|email| email.parse()).transpose()?;
481 let at = at.try_into()?;
482 Ok(Self { at, by })
483 }
484}
485
486impl From<e::place::PlaceRoot> for PlaceRoot {
487 fn from(from: e::place::PlaceRoot) -> Self {
488 let e::place::PlaceRoot { id, license } = from;
489 Self {
490 id: id.into(),
491 license,
492 }
493 }
494}
495
496impl From<PlaceRoot> for e::place::PlaceRoot {
497 fn from(from: PlaceRoot) -> Self {
498 let PlaceRoot { id, license } = from;
499 Self {
500 id: id.into(),
501 license,
502 }
503 }
504}
505
506impl From<e::place::PlaceRevision> for PlaceRevision {
507 fn from(from: e::place::PlaceRevision) -> Self {
508 let e::place::PlaceRevision {
509 revision,
510 created,
511 title,
512 description,
513 location,
514 contact,
515 opening_hours,
516 founded_on,
517 links,
518 tags,
519 } = from;
520 Self {
521 revision: revision.into(),
522 created: created.into(),
523 title,
524 description,
525 location: location.into(),
526 contact: contact.map(Into::into).unwrap_or_default(),
527 opening_hours: opening_hours.map(Into::into),
528 founded_on: founded_on.map(Into::into),
529 links: links.map(Into::into).unwrap_or_default(),
530 tags,
531 }
532 }
533}
534
535#[derive(Debug, Error)]
536pub enum PlaceRevisionConversionError {
537 #[error(transparent)]
538 Contact(#[from] ContactConversionError),
539 #[error(transparent)]
540 Activity(#[from] ActivityConversionError),
541 #[error(transparent)]
542 Email(#[from] e::email::EmailAddressParseError),
543}
544
545impl TryFrom<PlaceRevision> for e::place::PlaceRevision {
546 type Error = PlaceRevisionConversionError;
547 fn try_from(from: PlaceRevision) -> Result<Self, Self::Error> {
548 let PlaceRevision {
549 revision,
550 created,
551 title,
552 description,
553 location,
554 contact,
555 opening_hours,
556 founded_on,
557 links,
558 tags,
559 } = from;
560
561 Ok(Self {
562 revision: revision.into(),
563 created: created.try_into()?,
564 title,
565 description,
566 location: location.into(),
567 contact: Some(contact.try_into()?),
568 opening_hours: opening_hours.map(Into::into),
569 founded_on: founded_on.map(Into::into),
570 links: Some(links.into()),
571 tags,
572 })
573 }
574}
575
576impl From<e::place::PlaceHistory> for PlaceHistory {
577 fn from(from: e::place::PlaceHistory) -> Self {
578 let e::place::PlaceHistory { place, revisions } = from;
579 Self {
580 place: place.into(),
581 revisions: revisions
582 .into_iter()
583 .map(|(place_revision, reviews)| {
584 (
585 place_revision.into(),
586 reviews.into_iter().map(Into::into).collect(),
587 )
588 })
589 .collect(),
590 }
591 }
592}
593
594#[derive(Debug, Error)]
595pub enum PlaceHistoryConversionError {
596 #[error(transparent)]
597 PlaceRevision(#[from] PlaceRevisionConversionError),
598 #[error(transparent)]
599 ReviewStatusLog(#[from] ReviewStatusLogConversionError),
600}
601
602impl TryFrom<PlaceHistory> for e::place::PlaceHistory {
603 type Error = PlaceHistoryConversionError;
604 fn try_from(from: PlaceHistory) -> Result<Self, Self::Error> {
605 let PlaceHistory { place, revisions } = from;
606 let place = place.into();
607 let revisions = revisions
608 .into_iter()
609 .map(|(place_revision, reviews)| {
610 place_revision
611 .try_into()
612 .map_err(Self::Error::PlaceRevision)
613 .and_then(|place_revision| {
614 reviews
615 .into_iter()
616 .map(TryInto::try_into)
617 .collect::<Result<_, _>>()
618 .map_err(Self::Error::ReviewStatusLog)
619 .map(|reviews| (place_revision, reviews))
620 })
621 })
622 .collect::<Result<_, _>>()?;
623 Ok(Self { place, revisions })
624 }
625}
626
627impl From<e::activity::ActivityLog> for ActivityLog {
628 fn from(from: e::activity::ActivityLog) -> Self {
629 let e::activity::ActivityLog {
630 activity: e::activity::Activity { at, by },
631 context: ctx,
632 comment,
633 } = from;
634 Self {
635 at: at.into(),
636 by: by.map(e::email::EmailAddress::into_string),
637 ctx,
638 comment,
639 }
640 }
641}
642
643#[derive(Debug, Error)]
644pub enum ActivityLogConversionError {
645 #[error(transparent)]
646 Activity(#[from] ActivityConversionError),
647}
648
649impl TryFrom<ActivityLog> for e::activity::ActivityLog {
650 type Error = ActivityLogConversionError;
651 fn try_from(from: ActivityLog) -> Result<Self, Self::Error> {
652 let ActivityLog {
653 at,
654 by,
655 ctx: context,
656 comment,
657 } = from;
658 let activity = Activity { at, by }.try_into()?;
659 Ok(Self {
660 activity,
661 context,
662 comment,
663 })
664 }
665}
666
667impl From<e::review::ReviewStatusLog> for ReviewStatusLog {
668 fn from(from: e::review::ReviewStatusLog) -> Self {
669 let e::review::ReviewStatusLog {
670 revision,
671 activity,
672 status,
673 } = from;
674 Self {
675 rev: revision.into(),
676 act: activity.into(),
677 status: status.into(),
678 }
679 }
680}
681
682#[derive(Debug, Error)]
683pub enum ReviewStatusLogConversionError {
684 #[error(transparent)]
685 ActivityLog(#[from] ActivityLogConversionError),
686}
687
688impl TryFrom<ReviewStatusLog> for e::review::ReviewStatusLog {
689 type Error = ReviewStatusLogConversionError;
690 fn try_from(from: ReviewStatusLog) -> Result<Self, Self::Error> {
691 let ReviewStatusLog { rev, act, status } = from;
692 Ok(Self {
693 revision: rev.into(),
694 activity: act.try_into()?,
695 status: status.into(),
696 })
697 }
698}