1use std::collections::HashMap;
2
3use serde::{ser::SerializeSeq, Deserialize, Serialize};
4
5use crate::{
6 models::instances::InstanceId, ApiErrorDetail, Chunkable, FromErrorDetail,
7 IntegerStringOrObject,
8};
9
10#[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq, Clone)]
11#[serde(untagged, rename_all_fields = "camelCase")]
12pub enum Identity {
14 Id {
16 id: i64,
18 },
19 ExternalId {
21 external_id: String,
23 },
24}
25
26impl Identity {
27 pub fn id(id: i64) -> Self {
29 Identity::Id { id }
30 }
31
32 pub fn external_id(external_id: impl Into<String>) -> Self {
34 Identity::ExternalId {
35 external_id: external_id.into(),
36 }
37 }
38
39 pub fn into_external_id(self) -> Option<String> {
41 match self {
42 Identity::ExternalId { external_id } => Some(external_id),
43 _ => None,
44 }
45 }
46
47 pub fn into_id(self) -> Option<i64> {
49 match self {
50 Identity::Id { id } => Some(id),
51 _ => None,
52 }
53 }
54
55 pub fn as_external_id(&self) -> Option<&String> {
57 match self {
58 Identity::ExternalId { external_id } => Some(external_id),
59 _ => None,
60 }
61 }
62
63 pub fn as_id(&self) -> Option<i64> {
65 match self {
66 Identity::Id { id } => Some(*id),
67 _ => None,
68 }
69 }
70}
71
72impl Default for Identity {
73 fn default() -> Self {
74 Identity::Id { id: 0 }
75 }
76}
77
78impl From<i64> for Identity {
79 fn from(id: i64) -> Self {
80 Identity::Id { id }
81 }
82}
83
84impl From<&String> for Identity {
85 fn from(value: &String) -> Self {
86 Self::from(value.clone())
87 }
88}
89
90impl From<String> for Identity {
91 fn from(external_id: String) -> Self {
92 Identity::ExternalId { external_id }
93 }
94}
95
96impl From<&str> for Identity {
97 fn from(external_id: &str) -> Self {
98 Identity::ExternalId {
99 external_id: external_id.to_owned(),
100 }
101 }
102}
103
104impl FromErrorDetail for Identity {
105 fn from_detail(detail: &HashMap<String, Box<IntegerStringOrObject>>) -> Option<Self> {
106 ApiErrorDetail::get_integer(detail, "id")
107 .map(|id| Identity::Id { id })
108 .or_else(|| {
109 ApiErrorDetail::get_string(detail, "externalId").map(|id| Identity::ExternalId {
110 external_id: id.to_owned(),
111 })
112 })
113 }
114}
115
116#[derive(Serialize, Deserialize, Debug, Clone)]
117#[serde(rename_all = "camelCase")]
118pub struct CogniteId {
120 id: i64,
122}
123
124impl FromErrorDetail for CogniteId {
125 fn from_detail(detail: &HashMap<String, Box<IntegerStringOrObject>>) -> Option<Self> {
126 ApiErrorDetail::get_integer(detail, "id").map(|id| CogniteId { id })
127 }
128}
129
130#[derive(Serialize, Deserialize, Debug, Clone, Default)]
131#[serde(rename_all = "camelCase")]
132pub struct CogniteExternalId {
134 pub external_id: String,
136}
137
138impl FromErrorDetail for CogniteExternalId {
139 fn from_detail(detail: &HashMap<String, Box<IntegerStringOrObject>>) -> Option<Self> {
140 ApiErrorDetail::get_string(detail, "externalId").map(|external_id| CogniteExternalId {
141 external_id: external_id.to_owned(),
142 })
143 }
144}
145
146pub trait EqIdentity {
148 fn eq(&self, id: &Identity) -> bool;
150}
151
152impl From<String> for CogniteExternalId {
153 fn from(external_id: String) -> Self {
154 CogniteExternalId { external_id }
155 }
156}
157
158impl<'a> From<&'a str> for CogniteExternalId {
159 fn from(external_id: &'a str) -> Self {
160 CogniteExternalId {
161 external_id: external_id.to_owned(),
162 }
163 }
164}
165
166impl From<i64> for CogniteId {
167 fn from(id: i64) -> Self {
168 CogniteId { id }
169 }
170}
171
172#[derive(Debug, Deserialize, Serialize, Clone, Hash, Eq, PartialEq)]
173#[serde(rename_all_fields = "camelCase")]
174#[serde(untagged)]
175pub enum IdentityOrInstance {
177 Identity(Identity),
179 InstanceId {
181 instance_id: InstanceId,
183 },
184}
185
186impl IdentityOrInstance {
187 pub fn id(id: i64) -> Self {
189 Self::Identity(Identity::id(id))
190 }
191
192 pub fn external_id(external_id: impl Into<String>) -> Self {
194 Self::Identity(Identity::external_id(external_id))
195 }
196
197 pub fn instance_id(instance_id: impl Into<InstanceId>) -> Self {
199 Self::InstanceId {
200 instance_id: instance_id.into(),
201 }
202 }
203
204 pub fn as_id(&self) -> Option<i64> {
206 match self {
207 Self::Identity(i) => i.as_id(),
208 _ => None,
209 }
210 }
211
212 pub fn as_external_id(&self) -> Option<&String> {
214 match self {
215 Self::Identity(i) => i.as_external_id(),
216 _ => None,
217 }
218 }
219
220 pub fn as_identity(&self) -> Option<&Identity> {
222 match self {
223 Self::Identity(i) => Some(i),
224 _ => None,
225 }
226 }
227
228 pub fn as_instance_id(&self) -> Option<&InstanceId> {
230 match self {
231 Self::InstanceId { instance_id: i } => Some(i),
232 _ => None,
233 }
234 }
235}
236
237impl Default for IdentityOrInstance {
239 fn default() -> Self {
240 Self::Identity(Default::default())
241 }
242}
243
244impl From<&str> for IdentityOrInstance {
245 fn from(value: &str) -> Self {
246 Self::Identity(Identity::from(value))
247 }
248}
249
250impl From<&String> for IdentityOrInstance {
251 fn from(value: &String) -> Self {
252 Self::Identity(Identity::from(value))
253 }
254}
255
256impl From<String> for IdentityOrInstance {
257 fn from(value: String) -> Self {
258 Self::Identity(Identity::from(value))
259 }
260}
261
262impl From<i64> for IdentityOrInstance {
263 fn from(value: i64) -> Self {
264 Self::Identity(Identity::from(value))
265 }
266}
267
268impl From<Identity> for IdentityOrInstance {
269 fn from(value: Identity) -> Self {
270 Self::Identity(value)
271 }
272}
273
274impl From<InstanceId> for IdentityOrInstance {
275 fn from(value: InstanceId) -> Self {
276 Self::InstanceId { instance_id: value }
277 }
278}
279
280impl PartialEq<i64> for IdentityOrInstance {
281 fn eq(&self, other: &i64) -> bool {
282 self.as_id().as_ref() == Some(other)
283 }
284}
285
286impl PartialEq<str> for IdentityOrInstance {
287 fn eq(&self, other: &str) -> bool {
288 self.as_external_id().map(|a| a.as_str()) == Some(other)
289 }
290}
291
292impl PartialEq<InstanceId> for IdentityOrInstance {
293 fn eq(&self, other: &InstanceId) -> bool {
294 self.as_instance_id() == Some(other)
295 }
296}
297
298impl PartialEq<Identity> for IdentityOrInstance {
299 fn eq(&self, other: &Identity) -> bool {
300 self.as_identity() == Some(other)
301 }
302}
303
304impl FromErrorDetail for IdentityOrInstance {
305 fn from_detail(detail: &HashMap<String, Box<IntegerStringOrObject>>) -> Option<Self> {
306 if let Some(object) = ApiErrorDetail::get_object(detail, "instanceId") {
307 match (
308 ApiErrorDetail::get_string(object, "space"),
309 ApiErrorDetail::get_string(object, "externalId"),
310 ) {
311 (Some(space), Some(external_id)) => Some(Self::InstanceId {
312 instance_id: InstanceId {
313 space: space.to_owned(),
314 external_id: external_id.to_owned(),
315 },
316 }),
317 _ => None,
318 }
319 } else {
320 Identity::from_detail(detail).map(Self::Identity)
321 }
322 }
323}
324
325pub struct IdentityList<R>(R);
330
331impl<'a, R> Chunkable<'a> for IdentityList<R>
332where
333 R: Chunkable<'a>,
334{
335 type Chunk = IdentityList<R::Chunk>;
336
337 fn as_chunks(&'a self, chunk_size: usize) -> impl Iterator<Item = Self::Chunk> {
338 self.0.as_chunks(chunk_size).map(IdentityList)
339 }
340}
341
342impl<R> From<R> for IdentityList<R> {
343 fn from(value: R) -> Self {
344 IdentityList(value)
345 }
346}
347
348pub struct IdentityOrInstanceList<R>(R);
353
354impl<T> Serialize for IdentityOrInstanceList<T>
355where
356 IdentityList<T>: Serialize,
357 T: Copy,
358{
359 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
360 where
361 S: serde::Serializer,
362 {
363 IdentityList(self.0).serialize(serializer)
364 }
365}
366
367impl<'a, R> Chunkable<'a> for IdentityOrInstanceList<R>
368where
369 R: Chunkable<'a>,
370{
371 type Chunk = IdentityOrInstanceList<R::Chunk>;
372
373 fn as_chunks(&'a self, chunk_size: usize) -> impl Iterator<Item = Self::Chunk> {
374 self.0.as_chunks(chunk_size).map(IdentityOrInstanceList)
375 }
376}
377
378impl<R> From<R> for IdentityOrInstanceList<R> {
379 fn from(value: R) -> Self {
380 IdentityOrInstanceList(value)
381 }
382}
383
384macro_rules! identity_list_ser_directly {
385 ($r:ident, $t:ty) => {
386 impl Serialize for $r<$t> {
387 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
388 where
389 S: serde::Serializer,
390 {
391 self.0.serialize(serializer)
392 }
393 }
394 };
395 ($r:ident, $t:ty, $n:ident) => {
396 impl<const $n: usize> Serialize for $r<$t> {
397 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
398 where
399 S: serde::Serializer,
400 {
401 self.0.serialize(serializer)
402 }
403 }
404 };
405}
406
407identity_list_ser_directly!(IdentityList, &Vec<Identity>);
408identity_list_ser_directly!(IdentityOrInstanceList, &Vec<IdentityOrInstance>);
409identity_list_ser_directly!(IdentityList, &Vec<CogniteExternalId>);
410identity_list_ser_directly!(IdentityList, &Vec<CogniteId>);
411identity_list_ser_directly!(IdentityList, &Vec<&Identity>);
412identity_list_ser_directly!(IdentityOrInstanceList, &Vec<&IdentityOrInstance>);
413identity_list_ser_directly!(IdentityList, &Vec<&CogniteExternalId>);
414identity_list_ser_directly!(IdentityList, &Vec<&CogniteId>);
415identity_list_ser_directly!(IdentityList, &[Identity]);
416identity_list_ser_directly!(IdentityOrInstanceList, &[IdentityOrInstance]);
417identity_list_ser_directly!(IdentityList, &[CogniteExternalId]);
418identity_list_ser_directly!(IdentityList, &[CogniteId]);
419identity_list_ser_directly!(IdentityList, &[&Identity]);
420identity_list_ser_directly!(IdentityOrInstanceList, &[&IdentityOrInstance]);
421identity_list_ser_directly!(IdentityList, &[&CogniteExternalId]);
422identity_list_ser_directly!(IdentityList, &[&CogniteId]);
423identity_list_ser_directly!(IdentityList, &[Identity; N], N);
424identity_list_ser_directly!(IdentityOrInstanceList, &[IdentityOrInstance; N], N);
425identity_list_ser_directly!(IdentityList, &[CogniteExternalId; N], N);
426identity_list_ser_directly!(IdentityList, &[CogniteId; N], N);
427identity_list_ser_directly!(IdentityList, &[&Identity; N], N);
428identity_list_ser_directly!(IdentityOrInstanceList, &[&IdentityOrInstance; N], N);
429identity_list_ser_directly!(IdentityList, &[&CogniteExternalId; N], N);
430identity_list_ser_directly!(IdentityList, &[&CogniteId; N], N);
431
432#[derive(Serialize)]
433#[serde(rename_all = "camelCase")]
434struct ExternalIdRef<'a, T> {
435 external_id: &'a T,
436}
437
438macro_rules! identity_list_ser_external_id {
439 ($t:ty) => {
440 impl Serialize for IdentityList<$t> {
441 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
442 where
443 S: serde::Serializer,
444 {
445 let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
446 for id in self.0.iter() {
447 seq.serialize_element(&ExternalIdRef { external_id: id })?;
448 }
449 seq.end()
450 }
451 }
452 };
453
454 ($t:ty, $n:ident) => {
455 impl<const $n: usize> Serialize for IdentityList<$t> {
456 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
457 where
458 S: serde::Serializer,
459 {
460 let mut seq = serializer.serialize_seq(Some($n))?;
461 for id in self.0.iter() {
462 seq.serialize_element(&ExternalIdRef { external_id: id })?;
463 }
464 seq.end()
465 }
466 }
467 };
468}
469identity_list_ser_external_id!(&Vec<String>);
470identity_list_ser_external_id!(&[String]);
471identity_list_ser_external_id!(&[String; N], N);
472identity_list_ser_external_id!(&Vec<&String>);
473identity_list_ser_external_id!(&[&String]);
474identity_list_ser_external_id!(&[&String; N], N);
475identity_list_ser_external_id!(&Vec<&str>);
476identity_list_ser_external_id!(&[&str]);
477identity_list_ser_external_id!(&[&str; N], N);
478
479macro_rules! identity_list_ser_id {
480 ($t:ty) => {
481 impl Serialize for IdentityList<$t> {
482 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
483 where
484 S: serde::Serializer,
485 {
486 let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
487 for id in self.0.iter() {
488 seq.serialize_element(&CogniteId { id: *id })?;
489 }
490 seq.end()
491 }
492 }
493 };
494
495 ($t:ty, $n:ident) => {
496 impl<const N: usize> Serialize for IdentityList<$t> {
497 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
498 where
499 S: serde::Serializer,
500 {
501 let mut seq = serializer.serialize_seq(Some(N))?;
502 for id in self.0.iter() {
503 seq.serialize_element(&CogniteId { id: *id })?;
504 }
505 seq.end()
506 }
507 }
508 };
509}
510
511identity_list_ser_id!(&Vec<i64>);
512identity_list_ser_id!(&[i64]);
513identity_list_ser_id!(&[i64; N], N);
514
515#[derive(Serialize)]
516#[serde(rename_all = "camelCase")]
517struct InstanceIdRef<'a> {
518 instance_id: &'a InstanceId,
519}
520
521macro_rules! identity_list_ser_instance_id {
522 ($t:ty) => {
523 impl Serialize for IdentityOrInstanceList<$t> {
524 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
525 where
526 S: serde::Serializer,
527 {
528 let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
529 for id in self.0.iter() {
530 seq.serialize_element(&InstanceIdRef { instance_id: id })?;
531 }
532 seq.end()
533 }
534 }
535 };
536
537 ($t:ty, $n:ident) => {
538 impl<const N: usize> Serialize for IdentityOrInstanceList<$t> {
539 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
540 where
541 S: serde::Serializer,
542 {
543 let mut seq = serializer.serialize_seq(Some(N))?;
544 for id in self.0.iter() {
545 seq.serialize_element(&InstanceIdRef { instance_id: id })?;
546 }
547 seq.end()
548 }
549 }
550 };
551}
552
553identity_list_ser_instance_id!(&Vec<InstanceId>);
554identity_list_ser_instance_id!(&[InstanceId]);
555identity_list_ser_instance_id!(&[InstanceId; N], N);
556identity_list_ser_instance_id!(&Vec<&InstanceId>);
557identity_list_ser_instance_id!(&[&InstanceId]);
558identity_list_ser_instance_id!(&[&InstanceId; N], N);
559
560macro_rules! identity_list_ser_single {
561 ($r:ident, $t:ty) => {
562 impl Serialize for $r<$t> {
563 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
564 where
565 S: serde::Serializer,
566 {
567 Serialize::serialize(&$r(&[self.0]), serializer)
568 }
569 }
570 };
571}
572
573macro_rules! impl_chunk_single {
574 ($t:ty) => {
575 impl<'a> Chunkable<'a> for $t {
576 type Chunk = &'a $t;
577 fn as_chunks(&'a self, _chunk_size: usize) -> impl Iterator<Item = Self::Chunk> {
578 std::iter::once(self)
579 }
580 }
581 };
582}
583
584identity_list_ser_single!(IdentityList, i64);
585
586impl Chunkable<'_> for i64 {
587 type Chunk = i64;
588 fn as_chunks(&self, _chunk_size: usize) -> impl Iterator<Item = Self::Chunk> {
589 std::iter::once(*self)
590 }
591}
592
593identity_list_ser_single!(IdentityList, &str);
594impl_chunk_single!(&'a str);
595identity_list_ser_single!(IdentityList, &String);
596impl_chunk_single!(&'a String);
597identity_list_ser_single!(IdentityOrInstanceList, &InstanceId);
598impl_chunk_single!(&'a InstanceId);
599identity_list_ser_single!(IdentityList, &Identity);
600impl_chunk_single!(&'a Identity);
601identity_list_ser_single!(IdentityOrInstanceList, &IdentityOrInstance);
602impl_chunk_single!(&'a IdentityOrInstance);
603identity_list_ser_single!(IdentityList, &CogniteExternalId);
604impl_chunk_single!(&'a CogniteExternalId);
605identity_list_ser_single!(IdentityList, &CogniteId);
606impl_chunk_single!(&'a CogniteId);
607
608#[cfg(test)]
609mod tests {
610 use crate::{
611 models::instances::InstanceId, Chunkable, CogniteExternalId, CogniteId, IdentityOrInstance,
612 IdentityOrInstanceList,
613 };
614
615 use super::{Identity, IdentityList};
616
617 macro_rules! test_identity_list {
618 ($v:expr) => {
619 let v = $v;
620 let list = IdentityList::from(&v);
621 let serialized = serde_json::to_string(&list).unwrap();
622 let deserialized: Vec<Identity> = serde_json::from_str(&serialized).unwrap();
623 let reserialized = serde_json::to_string(&deserialized).unwrap();
624 assert_eq!(serialized, reserialized);
625
626 let chunks = list.as_chunks(2).collect::<Vec<_>>();
627 assert_eq!(chunks.len(), 2);
628 for chunk in chunks {
629 assert_eq!(chunk.0.len(), 2);
630 }
631 };
632 }
633
634 macro_rules! test_identity_or_instance_list {
635 ($v:expr) => {
636 let v = $v;
637 let list = IdentityOrInstanceList::from(&v);
638 let serialized = serde_json::to_string(&list).unwrap();
639 let deserialized: Vec<IdentityOrInstance> = serde_json::from_str(&serialized).unwrap();
640 let reserialized = serde_json::to_string(&deserialized).unwrap();
641 assert_eq!(serialized, reserialized);
642
643 let chunks = list.as_chunks(2).collect::<Vec<_>>();
644 assert_eq!(chunks.len(), 2);
645 for chunk in chunks {
646 assert_eq!(chunk.0.len(), 2);
647 }
648 };
649 }
650
651 #[test]
652 fn test_identity_list() {
653 test_identity_list!(vec![
654 Identity::external_id("ext1"),
655 Identity::id(2),
656 Identity::external_id("ext3"),
657 Identity::id(4)
658 ]);
659 test_identity_list!(vec![
660 CogniteExternalId::from("ext1"),
661 CogniteExternalId::from("ext2"),
662 CogniteExternalId::from("ext3"),
663 CogniteExternalId::from("ext4")
664 ]);
665 test_identity_list!(vec![
666 CogniteId::from(1),
667 CogniteId::from(2),
668 CogniteId::from(3),
669 CogniteId::from(4)
670 ]);
671 test_identity_list!([
672 Identity::external_id("ext1"),
673 Identity::id(2),
674 Identity::external_id("ext3"),
675 Identity::id(4)
676 ]);
677
678 test_identity_list!(vec!["ext1", "ext2", "ext3", "ext4"]);
679 test_identity_list!(vec![1i64, 2, 3, 4]);
680 test_identity_list!(["ext1", "ext2", "ext3", "ext4"]);
681 test_identity_list!([1i64, 2, 3, 4]);
682 }
683
684 #[test]
685 fn test_identity_or_instance_list() {
686 use crate::models::instances::InstanceId;
687 test_identity_or_instance_list!(vec![
688 IdentityOrInstance::external_id("ext1"),
689 IdentityOrInstance::id(2),
690 IdentityOrInstance::instance_id(InstanceId {
691 space: "space1".to_owned(),
692 external_id: "inst1".to_owned()
693 }),
694 IdentityOrInstance::id(4)
695 ]);
696 test_identity_or_instance_list!([
697 IdentityOrInstance::external_id("ext1"),
698 IdentityOrInstance::id(2),
699 IdentityOrInstance::instance_id(InstanceId {
700 space: "space1".to_owned(),
701 external_id: "inst1".to_owned()
702 }),
703 IdentityOrInstance::id(4)
704 ]);
705 test_identity_or_instance_list!(vec![
706 InstanceId {
707 space: "space1".to_owned(),
708 external_id: "inst1".to_owned()
709 },
710 InstanceId {
711 space: "space2".to_owned(),
712 external_id: "inst2".to_owned()
713 },
714 InstanceId {
715 space: "space3".to_owned(),
716 external_id: "inst3".to_owned()
717 },
718 InstanceId {
719 space: "space4".to_owned(),
720 external_id: "inst4".to_owned()
721 }
722 ]);
723 test_identity_or_instance_list!(["ext1", "ext2", "ext3", "ext4"]);
724 test_identity_or_instance_list!([1i64, 2, 3, 4]);
725 }
726
727 #[test]
728 fn test_identity_list_single() {
729 let x = "extId";
730 let list = IdentityList::from(x);
731 let serialized = serde_json::to_string(&list).unwrap();
732 assert_eq!(serialized, r#"[{"externalId":"extId"}]"#);
733
734 let x = 42i64;
735 let list = IdentityList::from(x);
736 let serialized = serde_json::to_string(&list).unwrap();
737 assert_eq!(serialized, r#"[{"id":42}]"#);
738
739 let x = InstanceId {
740 space: "space1".to_owned(),
741 external_id: "inst1".to_owned(),
742 };
743 let list = IdentityOrInstanceList::from(&x);
744 let serialized = serde_json::to_string(&list).unwrap();
745 assert_eq!(
746 serialized,
747 r#"[{"instanceId":{"space":"space1","externalId":"inst1"}}]"#
748 );
749 }
750}