1use async_graphql::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
2use qm_mongodb::bson::{self, oid::ObjectId, Bson};
3
4use crate::ids::InfraContext;
5
6use super::{CustomerId, InstitutionId, OrganizationId};
7
8#[derive(
9 Debug,
10 Default,
11 Clone,
12 Copy,
13 PartialEq,
14 Eq,
15 PartialOrd,
16 Ord,
17 Hash,
18 serde::Serialize,
19 serde::Deserialize,
20)]
21#[serde(transparent)]
22pub struct ID(ObjectId);
24
25#[Scalar]
26impl ScalarType for ID {
27 fn parse(value: Value) -> InputValueResult<Self> {
28 match value {
29 Value::String(s) => Ok(ID(ObjectId::parse_str(s)?)),
30 Value::Object(o) => {
31 let json = Value::Object(o).into_json()?;
32 let bson: Bson = Bson::try_from(json)?;
33 bson.as_object_id()
34 .map(ID)
35 .ok_or_else(|| InputValueError::custom("could not parse the value as an ID"))
36 }
37 _ => Err(InputValueError::expected_type(value)),
38 }
39 }
40
41 fn to_value(&self) -> Value {
42 Value::String(self.0.to_string())
43 }
44}
45
46impl std::ops::Deref for ID {
47 type Target = ObjectId;
48
49 fn deref(&self) -> &Self::Target {
50 &self.0
51 }
52}
53
54impl std::ops::DerefMut for ID {
55 fn deref_mut(&mut self) -> &mut Self::Target {
56 &mut self.0
57 }
58}
59
60impl std::str::FromStr for ID {
61 type Err = bson::error::Error;
62
63 fn from_str(s: &str) -> Result<Self, Self::Err> {
64 ObjectId::from_str(s).map(ID)
65 }
66}
67
68impl From<ObjectId> for ID {
69 fn from(value: ObjectId) -> Self {
70 Self(value)
71 }
72}
73
74impl From<ID> for Bson {
75 fn from(val: ID) -> Self {
76 val.0.into()
77 }
78}
79
80#[derive(
81 Debug,
82 Clone,
83 Copy,
84 PartialEq,
85 Eq,
86 PartialOrd,
87 Ord,
88 Hash,
89 Default,
90 serde::Serialize,
91 serde::Deserialize,
92)]
93pub struct OwnerId {
95 #[serde(skip_serializing_if = "Option::is_none")]
97 pub cid: Option<i64>,
98 #[serde(skip_serializing_if = "Option::is_none")]
100 pub oid: Option<i64>,
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub iid: Option<i64>,
104}
105
106impl From<CustomerId> for OwnerId {
107 fn from(value: CustomerId) -> Self {
108 Self {
109 cid: Some(value.unzip()),
110 ..Default::default()
111 }
112 }
113}
114
115impl From<OrganizationId> for OwnerId {
116 fn from(value: OrganizationId) -> Self {
117 let (cid, oid) = value.unzip();
118 Self {
119 cid: Some(cid),
120 oid: Some(oid),
121 ..Default::default()
122 }
123 }
124}
125
126impl From<InstitutionId> for OwnerId {
127 fn from(value: InstitutionId) -> Self {
128 let (cid, oid, iid) = value.unzip();
129 Self {
130 cid: Some(cid),
131 oid: Some(oid),
132 iid: Some(iid),
133 }
134 }
135}
136
137impl From<InfraContext> for OwnerId {
138 fn from(value: InfraContext) -> Self {
139 match value {
140 InfraContext::Customer(v) => v.into(),
141 InfraContext::Organization(v) => v.into(),
142 InfraContext::Institution(v) => v.into(),
143 }
144 }
145}
146
147impl<'a> TryFrom<&'a OwnerId> for InfraContext {
148 type Error = anyhow::Error;
149
150 fn try_from(value: &'a OwnerId) -> Result<Self, Self::Error> {
151 match value {
152 OwnerId {
153 cid: Some(cid),
154 oid: Some(oid),
155 iid: Some(iid),
156 } => Ok(InfraContext::Institution((*cid, *oid, *iid).into())),
157 OwnerId {
158 cid: Some(cid),
159 oid: Some(oid),
160 iid: None,
161 } => Ok(InfraContext::Organization((*cid, *oid).into())),
162 OwnerId {
163 cid: Some(cid),
164 oid: None,
165 iid: None,
166 } => Ok(InfraContext::Customer((*cid).into())),
167 _ => anyhow::bail!("invalid owner id"),
168 }
169 }
170}
171
172#[derive(Default, serde::Deserialize, serde::Serialize, Debug, Clone)]
173#[serde(transparent)]
174pub struct Owner {
176 #[serde(skip_serializing_if = "Owner::is_none")]
177 o: OwnerType,
178}
179
180impl Owner {
181 pub fn new(o: OwnerType) -> Self {
183 Self { o }
184 }
185
186 pub fn as_owner_id(&self) -> Option<&OwnerId> {
188 self.o.as_owner_id()
189 }
190}
191
192impl From<InfraContext> for Owner {
193 fn from(value: InfraContext) -> Self {
194 Self { o: value.into() }
195 }
196}
197
198#[derive(Default, serde::Deserialize, serde::Serialize, Debug, Clone)]
200#[serde(tag = "ty", content = "id")]
201pub enum OwnerType {
202 #[default]
204 None,
205 Customer(OwnerId),
207 Organization(OwnerId),
209 Institution(OwnerId),
211}
212
213impl OwnerType {
214 pub fn is_none(&self) -> bool {
216 matches!(self, Self::None)
217 }
218
219 pub fn as_owner_id(&self) -> Option<&OwnerId> {
221 match self {
222 OwnerType::None => None,
223 OwnerType::Customer(id) | OwnerType::Organization(id) | OwnerType::Institution(id) => {
224 Some(id)
225 }
226 }
227 }
228}
229
230impl From<InfraContext> for OwnerType {
231 fn from(value: InfraContext) -> Self {
232 match value {
233 InfraContext::Customer(v) => OwnerType::Customer(v.into()),
234 InfraContext::Organization(v) => OwnerType::Organization(v.into()),
235 InfraContext::Institution(v) => OwnerType::Institution(v.into()),
236 }
237 }
238}