1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
mod any_id;
mod id;
pub(crate) use any_id::*;
use async_trait::async_trait;
use cornucopia_async::GenericClient;
use futures::future::try_join_all;
pub use id::*;
mod external_id;
pub use external_id::*;
use itertools::Itertools;
mod slug;
pub use self::slug::*;
mod ref_;
pub use ref_::*;
mod request;
pub use request::*;

use std::fmt::Display;

use serde::{de::DeserializeOwned, Serialize};

use crate::helpers;
use crate::organizations::Organization;
use crate::Error;
use crate::Result;

#[async_trait]
pub trait RefTarget: core::fmt::Debug + Clone + Send + Sync {
    /// Try to look up the entity's ID from the given reference
    async fn lookup_id(
        client: &impl GenericClient,
        organization_id: Id<Organization>,
        entity_ref: &Ref<Self>,
    ) -> Result<Option<Id<Self>>>;

    /// Try to look up entity's IDs from the given references
    async fn lookup_ids(
        client: &impl GenericClient,
        organization_id: Id<Organization>,
        entity_refs: &[Ref<Self>],
    ) -> Result<Vec<Option<Id<Self>>>> {
        let ret = try_join_all(
            entity_refs
                .iter()
                .map(|ref_| Self::lookup_id(client, organization_id, ref_)),
        )
        .await?;
        Ok(ret.into_iter().collect_vec())
    }

    /// Returns the looked up Id, or default to Id(0)
    ///
    /// Useful for example when you want a database query to return no row if the ref
    /// is not found.
    async fn lookup_id_or_default(
        client: &impl GenericClient,
        organization_id: Id<Organization>,
        entity_ref: &Ref<Self>,
    ) -> Result<Id<Self>> {
        Ok(Self::lookup_id(client, organization_id, entity_ref)
            .await?
            .unwrap_or_else(|| Id::new(0)))
    }

    /// Look up entity's IDs from the given references, default to Id(0)
    ///
    /// Useful for example when you want a database query to return no rows on refs
    /// that are not found.
    async fn lookup_ids_with_default(
        client: &impl GenericClient,
        organization_id: Id<Organization>,
        entity_refs: &[Ref<Self>],
    ) -> Result<Vec<Id<Self>>> {
        Ok(try_join_all(
            entity_refs
                .iter()
                .map(|ref_| Self::lookup_id_or_default(client, organization_id, ref_)),
        )
        .await?)
    }

    /// Return the entity's ID from the given reference, or return a not found error
    async fn get_id(
        client: &impl GenericClient,
        organization_id: Id<Organization>,
        entity_ref: &Ref<Self>,
    ) -> Result<Id<Self>> {
        if let Some(id) = Self::lookup_id(client, organization_id, entity_ref).await? {
            Ok(id)
        } else {
            Err(entity_ref.not_found_error())
        }
    }

    /// Return if the given entity exists
    async fn exists(
        client: &impl GenericClient,
        organization_id: Id<Organization>,
        entity_ref: &Ref<Self>,
    ) -> Result<bool> {
        Ok(Self::lookup_id(client, organization_id, entity_ref)
            .await?
            .is_some())
    }

    fn short_type_name() -> &'static str {
        helpers::short_type_name::<Self>()
    }
}

pub trait RefType<T: RefTarget>:
    Clone
    + core::fmt::Debug
    + Eq
    + PartialEq
    + Serialize
    + DeserializeOwned
    + Display
    + Send
    + Sync
    + From<Self::Inner>
{
    type Inner;
    fn take(self) -> Self::Inner;
    fn inner(&self) -> &Self::Inner;
    fn not_found_error(&self) -> Error;
    fn already_exists_error(&self) -> Error;

    fn short_type_name() -> &'static str {
        T::short_type_name()
    }
}