1use serde::{Deserialize, Serialize};
6
7use crate::common::{ContactId, GrantId};
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
41pub struct Contact {
42 pub id: ContactId,
44
45 pub grant_id: GrantId,
47
48 #[serde(skip_serializing_if = "Option::is_none")]
50 pub given_name: Option<String>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub middle_name: Option<String>,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
58 pub family_name: Option<String>,
59
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub suffix: Option<String>,
63
64 #[serde(skip_serializing_if = "Option::is_none")]
66 pub nickname: Option<String>,
67
68 #[serde(skip_serializing_if = "Option::is_none")]
70 pub birthday: Option<String>,
71
72 #[serde(skip_serializing_if = "Option::is_none")]
74 pub company_name: Option<String>,
75
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub job_title: Option<String>,
79
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub manager_name: Option<String>,
83
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub office_location: Option<String>,
87
88 #[serde(skip_serializing_if = "Option::is_none")]
90 pub notes: Option<String>,
91
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub picture_url: Option<String>,
95
96 #[serde(skip_serializing_if = "Option::is_none")]
98 pub picture: Option<String>,
99
100 #[serde(default)]
102 pub emails: Vec<ContactEmail>,
103
104 #[serde(default)]
106 pub phone_numbers: Vec<ContactPhone>,
107
108 #[serde(default)]
110 pub physical_addresses: Vec<ContactAddress>,
111
112 #[serde(default)]
114 pub web_pages: Vec<ContactWebPage>,
115
116 #[serde(default)]
118 pub groups: Vec<ContactGroup>,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
123pub struct ContactEmail {
124 #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
126 pub email_type: Option<String>,
127
128 pub email: String,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub struct ContactPhone {
135 #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
137 pub phone_type: Option<String>,
138
139 pub number: String,
141}
142
143#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
145pub struct ContactAddress {
146 #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
148 pub address_type: Option<String>,
149
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub street_address: Option<String>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
156 pub city: Option<String>,
157
158 #[serde(skip_serializing_if = "Option::is_none")]
160 pub state: Option<String>,
161
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub postal_code: Option<String>,
165
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub country: Option<String>,
169}
170
171#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
173pub struct ContactWebPage {
174 #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
176 pub page_type: Option<String>,
177
178 pub url: String,
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
184pub struct ContactGroup {
185 pub id: String,
187
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub name: Option<String>,
191
192 #[serde(skip_serializing_if = "Option::is_none")]
194 pub path: Option<String>,
195}
196
197#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
199pub struct CreateContactRequest {
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub given_name: Option<String>,
203
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub middle_name: Option<String>,
207
208 #[serde(skip_serializing_if = "Option::is_none")]
210 pub family_name: Option<String>,
211
212 #[serde(skip_serializing_if = "Option::is_none")]
214 pub suffix: Option<String>,
215
216 #[serde(skip_serializing_if = "Option::is_none")]
218 pub nickname: Option<String>,
219
220 #[serde(skip_serializing_if = "Option::is_none")]
222 pub birthday: Option<String>,
223
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub company_name: Option<String>,
227
228 #[serde(skip_serializing_if = "Option::is_none")]
230 pub job_title: Option<String>,
231
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub manager_name: Option<String>,
235
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub office_location: Option<String>,
239
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub notes: Option<String>,
243
244 #[serde(skip_serializing_if = "Option::is_none")]
246 pub picture_url: Option<String>,
247
248 #[serde(skip_serializing_if = "Option::is_none")]
250 pub picture: Option<String>,
251
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub emails: Option<Vec<ContactEmail>>,
255
256 #[serde(skip_serializing_if = "Option::is_none")]
258 pub phone_numbers: Option<Vec<ContactPhone>>,
259
260 #[serde(skip_serializing_if = "Option::is_none")]
262 pub physical_addresses: Option<Vec<ContactAddress>>,
263
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub web_pages: Option<Vec<ContactWebPage>>,
267
268 #[serde(skip_serializing_if = "Option::is_none")]
270 pub groups: Option<Vec<ContactGroup>>,
271}
272
273impl CreateContactRequest {
274 pub fn builder() -> CreateContactRequestBuilder {
276 CreateContactRequestBuilder::default()
277 }
278}
279
280#[derive(Debug, Clone, Default)]
282pub struct CreateContactRequestBuilder {
283 given_name: Option<String>,
284 middle_name: Option<String>,
285 family_name: Option<String>,
286 suffix: Option<String>,
287 nickname: Option<String>,
288 birthday: Option<String>,
289 company_name: Option<String>,
290 job_title: Option<String>,
291 manager_name: Option<String>,
292 office_location: Option<String>,
293 notes: Option<String>,
294 picture_url: Option<String>,
295 picture: Option<String>,
296 emails: Option<Vec<ContactEmail>>,
297 phone_numbers: Option<Vec<ContactPhone>>,
298 physical_addresses: Option<Vec<ContactAddress>>,
299 web_pages: Option<Vec<ContactWebPage>>,
300 groups: Option<Vec<ContactGroup>>,
301}
302
303impl CreateContactRequestBuilder {
304 pub fn given_name(mut self, name: impl Into<String>) -> Self {
306 self.given_name = Some(name.into());
307 self
308 }
309
310 pub fn middle_name(mut self, name: impl Into<String>) -> Self {
312 self.middle_name = Some(name.into());
313 self
314 }
315
316 pub fn family_name(mut self, name: impl Into<String>) -> Self {
318 self.family_name = Some(name.into());
319 self
320 }
321
322 pub fn suffix(mut self, suffix: impl Into<String>) -> Self {
324 self.suffix = Some(suffix.into());
325 self
326 }
327
328 pub fn nickname(mut self, nickname: impl Into<String>) -> Self {
330 self.nickname = Some(nickname.into());
331 self
332 }
333
334 pub fn birthday(mut self, birthday: impl Into<String>) -> Self {
336 self.birthday = Some(birthday.into());
337 self
338 }
339
340 pub fn company_name(mut self, name: impl Into<String>) -> Self {
342 self.company_name = Some(name.into());
343 self
344 }
345
346 pub fn job_title(mut self, title: impl Into<String>) -> Self {
348 self.job_title = Some(title.into());
349 self
350 }
351
352 pub fn manager_name(mut self, name: impl Into<String>) -> Self {
354 self.manager_name = Some(name.into());
355 self
356 }
357
358 pub fn office_location(mut self, location: impl Into<String>) -> Self {
360 self.office_location = Some(location.into());
361 self
362 }
363
364 pub fn notes(mut self, notes: impl Into<String>) -> Self {
366 self.notes = Some(notes.into());
367 self
368 }
369
370 pub fn picture_url(mut self, url: impl Into<String>) -> Self {
372 self.picture_url = Some(url.into());
373 self
374 }
375
376 pub fn picture(mut self, picture: impl Into<String>) -> Self {
378 self.picture = Some(picture.into());
379 self
380 }
381
382 pub fn emails(mut self, emails: Vec<ContactEmail>) -> Self {
384 self.emails = Some(emails);
385 self
386 }
387
388 pub fn phone_numbers(mut self, phones: Vec<ContactPhone>) -> Self {
390 self.phone_numbers = Some(phones);
391 self
392 }
393
394 pub fn physical_addresses(mut self, addresses: Vec<ContactAddress>) -> Self {
396 self.physical_addresses = Some(addresses);
397 self
398 }
399
400 pub fn web_pages(mut self, pages: Vec<ContactWebPage>) -> Self {
402 self.web_pages = Some(pages);
403 self
404 }
405
406 pub fn groups(mut self, groups: Vec<ContactGroup>) -> Self {
408 self.groups = Some(groups);
409 self
410 }
411
412 pub fn build(self) -> CreateContactRequest {
414 CreateContactRequest {
415 given_name: self.given_name,
416 middle_name: self.middle_name,
417 family_name: self.family_name,
418 suffix: self.suffix,
419 nickname: self.nickname,
420 birthday: self.birthday,
421 company_name: self.company_name,
422 job_title: self.job_title,
423 manager_name: self.manager_name,
424 office_location: self.office_location,
425 notes: self.notes,
426 picture_url: self.picture_url,
427 picture: self.picture,
428 emails: self.emails,
429 phone_numbers: self.phone_numbers,
430 physical_addresses: self.physical_addresses,
431 web_pages: self.web_pages,
432 groups: self.groups,
433 }
434 }
435}
436
437#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
439pub struct UpdateContactRequest {
440 #[serde(skip_serializing_if = "Option::is_none")]
442 pub given_name: Option<String>,
443
444 #[serde(skip_serializing_if = "Option::is_none")]
446 pub middle_name: Option<String>,
447
448 #[serde(skip_serializing_if = "Option::is_none")]
450 pub family_name: Option<String>,
451
452 #[serde(skip_serializing_if = "Option::is_none")]
454 pub suffix: Option<String>,
455
456 #[serde(skip_serializing_if = "Option::is_none")]
458 pub nickname: Option<String>,
459
460 #[serde(skip_serializing_if = "Option::is_none")]
462 pub birthday: Option<String>,
463
464 #[serde(skip_serializing_if = "Option::is_none")]
466 pub company_name: Option<String>,
467
468 #[serde(skip_serializing_if = "Option::is_none")]
470 pub job_title: Option<String>,
471
472 #[serde(skip_serializing_if = "Option::is_none")]
474 pub manager_name: Option<String>,
475
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub office_location: Option<String>,
479
480 #[serde(skip_serializing_if = "Option::is_none")]
482 pub notes: Option<String>,
483
484 #[serde(skip_serializing_if = "Option::is_none")]
486 pub picture_url: Option<String>,
487
488 #[serde(skip_serializing_if = "Option::is_none")]
490 pub picture: Option<String>,
491
492 #[serde(skip_serializing_if = "Option::is_none")]
494 pub emails: Option<Vec<ContactEmail>>,
495
496 #[serde(skip_serializing_if = "Option::is_none")]
498 pub phone_numbers: Option<Vec<ContactPhone>>,
499
500 #[serde(skip_serializing_if = "Option::is_none")]
502 pub physical_addresses: Option<Vec<ContactAddress>>,
503
504 #[serde(skip_serializing_if = "Option::is_none")]
506 pub web_pages: Option<Vec<ContactWebPage>>,
507
508 #[serde(skip_serializing_if = "Option::is_none")]
510 pub groups: Option<Vec<ContactGroup>>,
511}
512
513impl UpdateContactRequest {
514 pub fn builder() -> UpdateContactRequestBuilder {
516 UpdateContactRequestBuilder::default()
517 }
518}
519
520#[derive(Debug, Clone, Default)]
522pub struct UpdateContactRequestBuilder {
523 given_name: Option<String>,
524 middle_name: Option<String>,
525 family_name: Option<String>,
526 suffix: Option<String>,
527 nickname: Option<String>,
528 birthday: Option<String>,
529 company_name: Option<String>,
530 job_title: Option<String>,
531 manager_name: Option<String>,
532 office_location: Option<String>,
533 notes: Option<String>,
534 picture_url: Option<String>,
535 picture: Option<String>,
536 emails: Option<Vec<ContactEmail>>,
537 phone_numbers: Option<Vec<ContactPhone>>,
538 physical_addresses: Option<Vec<ContactAddress>>,
539 web_pages: Option<Vec<ContactWebPage>>,
540 groups: Option<Vec<ContactGroup>>,
541}
542
543impl UpdateContactRequestBuilder {
544 pub fn given_name(mut self, name: impl Into<String>) -> Self {
546 self.given_name = Some(name.into());
547 self
548 }
549
550 pub fn middle_name(mut self, name: impl Into<String>) -> Self {
552 self.middle_name = Some(name.into());
553 self
554 }
555
556 pub fn family_name(mut self, name: impl Into<String>) -> Self {
558 self.family_name = Some(name.into());
559 self
560 }
561
562 pub fn suffix(mut self, suffix: impl Into<String>) -> Self {
564 self.suffix = Some(suffix.into());
565 self
566 }
567
568 pub fn nickname(mut self, nickname: impl Into<String>) -> Self {
570 self.nickname = Some(nickname.into());
571 self
572 }
573
574 pub fn birthday(mut self, birthday: impl Into<String>) -> Self {
576 self.birthday = Some(birthday.into());
577 self
578 }
579
580 pub fn company_name(mut self, name: impl Into<String>) -> Self {
582 self.company_name = Some(name.into());
583 self
584 }
585
586 pub fn job_title(mut self, title: impl Into<String>) -> Self {
588 self.job_title = Some(title.into());
589 self
590 }
591
592 pub fn manager_name(mut self, name: impl Into<String>) -> Self {
594 self.manager_name = Some(name.into());
595 self
596 }
597
598 pub fn office_location(mut self, location: impl Into<String>) -> Self {
600 self.office_location = Some(location.into());
601 self
602 }
603
604 pub fn notes(mut self, notes: impl Into<String>) -> Self {
606 self.notes = Some(notes.into());
607 self
608 }
609
610 pub fn picture_url(mut self, url: impl Into<String>) -> Self {
612 self.picture_url = Some(url.into());
613 self
614 }
615
616 pub fn picture(mut self, picture: impl Into<String>) -> Self {
618 self.picture = Some(picture.into());
619 self
620 }
621
622 pub fn emails(mut self, emails: Vec<ContactEmail>) -> Self {
624 self.emails = Some(emails);
625 self
626 }
627
628 pub fn phone_numbers(mut self, phones: Vec<ContactPhone>) -> Self {
630 self.phone_numbers = Some(phones);
631 self
632 }
633
634 pub fn physical_addresses(mut self, addresses: Vec<ContactAddress>) -> Self {
636 self.physical_addresses = Some(addresses);
637 self
638 }
639
640 pub fn web_pages(mut self, pages: Vec<ContactWebPage>) -> Self {
642 self.web_pages = Some(pages);
643 self
644 }
645
646 pub fn groups(mut self, groups: Vec<ContactGroup>) -> Self {
648 self.groups = Some(groups);
649 self
650 }
651
652 pub fn build(self) -> UpdateContactRequest {
654 UpdateContactRequest {
655 given_name: self.given_name,
656 middle_name: self.middle_name,
657 family_name: self.family_name,
658 suffix: self.suffix,
659 nickname: self.nickname,
660 birthday: self.birthday,
661 company_name: self.company_name,
662 job_title: self.job_title,
663 manager_name: self.manager_name,
664 office_location: self.office_location,
665 notes: self.notes,
666 picture_url: self.picture_url,
667 picture: self.picture,
668 emails: self.emails,
669 phone_numbers: self.phone_numbers,
670 physical_addresses: self.physical_addresses,
671 web_pages: self.web_pages,
672 groups: self.groups,
673 }
674 }
675}
676
677#[cfg(test)]
678mod tests {
679 use super::*;
680
681 #[test]
682 fn test_contact_creation() {
683 let contact = Contact {
684 id: ContactId::new("contact_123"),
685 grant_id: GrantId::new("grant_456"),
686 given_name: Some("John".to_string()),
687 family_name: Some("Doe".to_string()),
688 emails: vec![ContactEmail {
689 email_type: Some("work".to_string()),
690 email: "john.doe@example.com".to_string(),
691 }],
692 phone_numbers: vec![],
693 physical_addresses: vec![],
694 web_pages: vec![],
695 groups: vec![],
696 middle_name: None,
697 suffix: None,
698 nickname: None,
699 birthday: None,
700 company_name: None,
701 job_title: None,
702 manager_name: None,
703 office_location: None,
704 notes: None,
705 picture_url: None,
706 picture: None,
707 };
708
709 assert_eq!(contact.id.as_str(), "contact_123");
710 assert_eq!(contact.given_name.as_ref().unwrap(), "John");
711 assert_eq!(contact.family_name.as_ref().unwrap(), "Doe");
712 assert_eq!(contact.emails.len(), 1);
713 }
714
715 #[test]
716 fn test_contact_serialization() {
717 let contact = Contact {
718 id: ContactId::new("contact_123"),
719 grant_id: GrantId::new("grant_456"),
720 given_name: Some("Jane".to_string()),
721 family_name: Some("Smith".to_string()),
722 emails: vec![],
723 phone_numbers: vec![],
724 physical_addresses: vec![],
725 web_pages: vec![],
726 groups: vec![],
727 middle_name: None,
728 suffix: None,
729 nickname: None,
730 birthday: None,
731 company_name: Some("Acme Corp".to_string()),
732 job_title: Some("Engineer".to_string()),
733 manager_name: None,
734 office_location: None,
735 notes: None,
736 picture_url: None,
737 picture: None,
738 };
739
740 let json = serde_json::to_string(&contact).unwrap();
741 assert!(json.contains("contact_123"));
742 assert!(json.contains("Jane"));
743 assert!(json.contains("Acme Corp"));
744 }
745
746 #[test]
747 fn test_contact_deserialization() {
748 let json = r#"{
749 "id": "contact_123",
750 "grant_id": "grant_456",
751 "given_name": "Bob",
752 "family_name": "Johnson",
753 "company_name": "Tech Inc",
754 "emails": [],
755 "phone_numbers": [],
756 "physical_addresses": [],
757 "web_pages": [],
758 "groups": []
759 }"#;
760
761 let contact: Contact = serde_json::from_str(json).unwrap();
762 assert_eq!(contact.id.as_str(), "contact_123");
763 assert_eq!(contact.given_name.as_ref().unwrap(), "Bob");
764 assert_eq!(contact.company_name.as_ref().unwrap(), "Tech Inc");
765 }
766
767 #[test]
768 fn test_create_contact_request_builder() {
769 let request = CreateContactRequest::builder()
770 .given_name("Alice")
771 .family_name("Williams")
772 .company_name("Example LLC")
773 .job_title("Manager")
774 .build();
775
776 assert_eq!(request.given_name.as_ref().unwrap(), "Alice");
777 assert_eq!(request.family_name.as_ref().unwrap(), "Williams");
778 assert_eq!(request.company_name.as_ref().unwrap(), "Example LLC");
779 assert_eq!(request.job_title.as_ref().unwrap(), "Manager");
780 }
781
782 #[test]
783 fn test_update_contact_request_builder() {
784 let update = UpdateContactRequest::builder()
785 .given_name("Updated Name")
786 .job_title("Senior Engineer")
787 .build();
788
789 assert_eq!(update.given_name.as_ref().unwrap(), "Updated Name");
790 assert_eq!(update.job_title.as_ref().unwrap(), "Senior Engineer");
791 assert!(update.company_name.is_none());
792 }
793
794 #[test]
795 fn test_contact_email() {
796 let email = ContactEmail {
797 email_type: Some("work".to_string()),
798 email: "test@example.com".to_string(),
799 };
800
801 assert_eq!(email.email_type.as_ref().unwrap(), "work");
802 assert_eq!(email.email, "test@example.com");
803 }
804
805 #[test]
806 fn test_contact_phone() {
807 let phone = ContactPhone {
808 phone_type: Some("mobile".to_string()),
809 number: "+1234567890".to_string(),
810 };
811
812 assert_eq!(phone.phone_type.as_ref().unwrap(), "mobile");
813 assert_eq!(phone.number, "+1234567890");
814 }
815
816 #[test]
817 fn test_contact_address() {
818 let address = ContactAddress {
819 address_type: Some("home".to_string()),
820 street_address: Some("123 Main St".to_string()),
821 city: Some("Springfield".to_string()),
822 state: Some("IL".to_string()),
823 postal_code: Some("62701".to_string()),
824 country: Some("USA".to_string()),
825 };
826
827 assert_eq!(address.address_type.as_ref().unwrap(), "home");
828 assert_eq!(address.city.as_ref().unwrap(), "Springfield");
829 }
830}