ofdb_boundary/
conv.rs

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
46// TODO: use TryFrom
47impl 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
387// TODO: use TryFrom here
388impl 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}