1use std::time::Duration;
5
6use anyhow::Result;
8use serde_json::Value;
9
10use super::enums::{VerificationError, VerificationStatus};
12use crate::errors::BriteVerifyTypeError;
13
14#[cfg(test)]
16#[doc(hidden)]
17#[allow(unused_imports)]
18pub use self::foundry::*;
19
20#[cfg_attr(any(test, tarpaulin, feature = "ci"), derive(Clone))]
24#[derive(Debug, serde::Serialize, serde::Deserialize)]
25pub struct StreetAddressArray {
26 pub address1: String,
28 #[serde(
31 default,
32 skip_serializing_if = "Option::is_none",
33 deserialize_with = "crate::utils::empty_string_is_none"
34 )]
35 pub address2: Option<String>,
36 pub city: String,
38 pub state: String,
40 pub zip: String,
42}
43
44impl StreetAddressArray {
45 pub fn builder() -> AddressArrayBuilder {
47 AddressArrayBuilder::new()
48 }
49
50 pub fn from_values<Displayable: ToString>(
53 address1: Displayable,
54 address2: Option<Displayable>,
55 city: Displayable,
56 state: Displayable,
57 zip: Displayable,
58 ) -> Self {
59 let (address1, city, state, zip) = (
60 address1.to_string(),
61 city.to_string(),
62 state.to_string(),
63 zip.to_string(),
64 );
65 let address2 = address2.map(|value| value.to_string());
66
67 Self {
68 address1,
69 address2,
70 city,
71 state,
72 zip,
73 }
74 }
75}
76
77#[derive(Debug, Default)]
79pub struct AddressArrayBuilder {
80 _address1: Option<String>,
81 _address2: Option<String>,
82 _city: Option<String>,
83 _state: Option<String>,
84 _zip: Option<String>,
85}
86
87impl AddressArrayBuilder {
88 pub fn new() -> Self {
90 Self::default()
91 }
92
93 pub fn build(self) -> Result<StreetAddressArray, BriteVerifyTypeError> {
95 if !self.buildable() {
96 Err(BriteVerifyTypeError::UnbuildableAddressArray(Box::new(
97 self,
98 )))
99 } else {
100 Ok(StreetAddressArray::from_values(
101 self._address1.unwrap(),
102 self._address2,
103 self._city.unwrap(),
104 self._state.unwrap(),
105 self._zip.unwrap(),
106 ))
107 }
108 }
109
110 pub fn buildable(&self) -> bool {
113 self._address1
114 .as_ref()
115 .is_some_and(|value| !value.trim().is_empty())
116 && self
117 ._city
118 .as_ref()
119 .is_some_and(|value| !value.trim().is_empty())
120 && self
121 ._state
122 .as_ref()
123 .is_some_and(|value| !value.trim().is_empty())
124 && self
125 ._zip
126 .as_ref()
127 .is_some_and(|value| !value.trim().is_empty())
128 }
129
130 pub fn zip<Displayable: ToString>(mut self, value: Displayable) -> Self {
133 self._zip = Some(value.to_string());
134 self
135 }
136
137 pub fn city<Displayable: ToString>(mut self, value: Displayable) -> Self {
140 self._city = Some(value.to_string());
141 self
142 }
143
144 pub fn state<Displayable: ToString>(mut self, value: Displayable) -> Self {
147 self._state = Some(value.to_string());
148 self
149 }
150
151 pub fn address1<Displayable: ToString>(mut self, value: Displayable) -> Self {
154 self._address1 = Some(value.to_string());
155 self
156 }
157
158 pub fn address2<Displayable: ToString>(mut self, value: Displayable) -> Self {
161 self._address2 = Some(value.to_string());
162 self
163 }
164
165 pub fn from_values<
168 AddressLine1: ToString,
169 AddressLine2: ToString,
170 CityName: ToString,
171 StateNameOrAbbr: ToString,
172 ZipCode: ToString,
173 >(
174 address1: Option<AddressLine1>,
175 address2: Option<AddressLine2>,
176 city: Option<CityName>,
177 state: Option<StateNameOrAbbr>,
178 zip: Option<ZipCode>,
179 ) -> Self {
180 let mut instance = Self::new();
181
182 if let Some(value) = zip {
183 instance = instance.zip(value);
184 }
185
186 if let Some(value) = city {
187 instance = instance.city(value);
188 }
189
190 if let Some(value) = state {
191 instance = instance.state(value);
192 }
193
194 if let Some(value) = address1 {
195 instance = instance.address1(value);
196 }
197
198 if let Some(value) = address2 {
199 instance = instance.address2(value);
200 }
201
202 instance
203 }
204}
205
206#[cfg(any(test, tarpaulin, feature = "ci"))]
207impl PartialEq for StreetAddressArray {
208 fn eq(&self, other: &Self) -> bool {
209 if self.address2.is_none() != other.address2.is_none() {
210 return false;
211 }
212
213 let (self_addr2, other_addr2) = (
214 self.address2
215 .as_ref()
216 .map_or(String::new(), |val| val.to_string()),
217 other
218 .address2
219 .as_ref()
220 .map_or(String::new(), |val| val.to_string()),
221 );
222
223 crate::utils::caseless_eq(&self.address1, &other.address1)
224 && crate::utils::caseless_eq(&self_addr2, &other_addr2)
225 && crate::utils::caseless_eq(&self.city, &other.city)
226 && crate::utils::caseless_eq(&self.state, &other.state)
227 && crate::utils::caseless_eq(&self.zip, &other.zip)
228 }
229}
230
231#[cfg_attr(any(test, tarpaulin, feature = "ci"), derive(PartialEq))]
238#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
239pub struct VerificationRequest {
240 #[serde(default, skip_serializing_if = "Option::is_none")]
242 pub email: Option<String>,
243 #[serde(default, skip_serializing_if = "Option::is_none")]
245 pub phone: Option<String>,
246 #[serde(default, skip_serializing_if = "Option::is_none")]
248 pub address: Option<StreetAddressArray>,
249}
250
251impl VerificationRequest {
252 pub fn builder() -> VerificationRequestBuilder {
255 VerificationRequestBuilder::new()
256 }
257
258 pub fn from_values<
261 EmailAddress: ToString,
262 PhoneNumber: ToString,
263 AddressLine1: ToString,
264 AddressLine2: ToString,
265 CityName: ToString,
266 StateNameOrAbbr: ToString,
267 ZipCode: ToString,
268 >(
269 email: Option<EmailAddress>,
270 phone: Option<PhoneNumber>,
271 address1: Option<AddressLine1>,
272 address2: Option<AddressLine2>,
273 city: Option<CityName>,
274 state: Option<StateNameOrAbbr>,
275 zip: Option<ZipCode>,
276 ) -> Result<Self, BriteVerifyTypeError> {
277 VerificationRequestBuilder::from_values(email, phone, address1, address2, city, state, zip)
278 .build()
279 }
280}
281
282impl TryFrom<String> for VerificationRequest {
283 type Error = BriteVerifyTypeError;
284
285 fn try_from(value: String) -> Result<Self, Self::Error> {
286 Self::try_from(value.as_str())
287 }
288}
289
290impl TryFrom<&'_ str> for VerificationRequest {
291 type Error = BriteVerifyTypeError;
292
293 fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
294 if let Ok(request) = serde_json::from_str::<VerificationRequest>(value) {
295 return Ok(request);
296 }
297
298 if value.contains('@') {
299 return Ok(Self {
300 email: Some(value.to_string()),
301 ..Self::default()
302 });
303 }
304
305 const PHONE_CHARS: &str = "0123456789 +().- ext";
306
307 if value
308 .to_ascii_lowercase()
309 .chars()
310 .all(|ch| PHONE_CHARS.contains(ch))
311 {
312 return Ok(Self {
313 phone: Some(value.to_string()),
314 ..Self::default()
315 });
316 }
317
318 Err(BriteVerifyTypeError::AmbiguousTryFromValue(
319 value.to_string(),
320 ))
321 }
322}
323
324#[derive(Debug, Default)]
326pub struct VerificationRequestBuilder {
327 _email: Option<String>,
328 _phone: Option<String>,
329 _address: AddressArrayBuilder,
330}
331
332impl VerificationRequestBuilder {
333 pub fn new() -> VerificationRequestBuilder {
335 Self::default()
336 }
337
338 pub fn build(self) -> Result<VerificationRequest, BriteVerifyTypeError> {
341 if self._email.is_some() || self._phone.is_some() || self._address.buildable() {
342 Ok(VerificationRequest {
343 email: self._email,
344 phone: self._phone,
345 address: self._address.build().ok(),
346 })
347 } else {
348 Err(BriteVerifyTypeError::UnbuildableRequest(Box::new(self)))
349 }
350 }
351
352 pub fn email<Displayable: ToString>(mut self, value: Displayable) -> Self {
355 self._email = Some(value.to_string());
356 self
357 }
358
359 pub fn phone<Displayable: ToString>(mut self, value: Displayable) -> Self {
362 self._phone = Some(value.to_string());
363 self
364 }
365
366 pub fn zip<Displayable: ToString>(mut self, value: Displayable) -> Self {
369 self._address = self._address.zip(value);
370 self
371 }
372
373 pub fn city<Displayable: ToString>(mut self, value: Displayable) -> Self {
376 self._address = self._address.city(value);
377 self
378 }
379
380 pub fn state<Displayable: ToString>(mut self, value: Displayable) -> Self {
383 self._address = self._address.state(value);
384 self
385 }
386
387 pub fn address1<Displayable: ToString>(mut self, value: Displayable) -> Self {
390 self._address = self._address.address1(value);
391 self
392 }
393
394 pub fn address2<Displayable: ToString>(mut self, value: Displayable) -> Self {
397 self._address = self._address.address2(value);
398 self
399 }
400
401 pub fn buildable(&self) -> bool {
404 self._email.is_some() || self._phone.is_some() || self._address.buildable()
405 }
406
407 pub fn from_values<
410 EmailAddress: ToString,
411 PhoneNumber: ToString,
412 AddressLine1: ToString,
413 AddressLine2: ToString,
414 CityName: ToString,
415 StateNameOrAbbr: ToString,
416 ZipCode: ToString,
417 >(
418 email: Option<EmailAddress>,
419 phone: Option<PhoneNumber>,
420 address1: Option<AddressLine1>,
421 address2: Option<AddressLine2>,
422 city: Option<CityName>,
423 state: Option<StateNameOrAbbr>,
424 zip: Option<ZipCode>,
425 ) -> Self {
426 let mut instance = Self {
427 _address: AddressArrayBuilder::from_values(address1, address2, city, state, zip),
428 ..Self::default()
429 };
430
431 if let Some(value) = email {
432 instance = instance.email(value);
433 }
434
435 if let Some(value) = phone {
436 instance = instance.phone(value);
437 }
438
439 instance
440 }
441}
442
443#[cfg_attr(any(test, tarpaulin, feature = "ci"), derive(PartialEq))]
449#[derive(Debug, serde::Serialize, serde::Deserialize)]
450pub struct EmailVerificationArray {
451 pub address: String,
454 pub account: String,
457 pub domain: String,
460 pub status: VerificationStatus,
464 pub connected: Option<Value>,
471 pub disposable: bool,
475 pub role_address: bool,
481 #[serde(default, skip_serializing_if = "Option::is_none")]
486 pub error_code: Option<VerificationError>,
487 #[serde(default, skip_serializing_if = "Option::is_none")]
490 pub error: Option<String>,
491}
492
493#[cfg_attr(any(test, tarpaulin, feature = "ci"), derive(PartialEq))]
495#[derive(Debug, serde::Serialize, serde::Deserialize)]
496pub struct PhoneNumberVerificationArray {
497 pub number: String,
506 pub status: VerificationStatus,
510 #[serde(default)]
513 pub service_type: Option<String>,
514 pub phone_location: Option<Value>,
521 pub errors: Vec<Value>,
524}
525
526#[cfg_attr(any(test, tarpaulin, feature = "ci"), derive(PartialEq))]
528#[derive(Debug, serde::Serialize, serde::Deserialize)]
529pub struct AddressVerificationArray {
530 pub address1: String,
532 #[serde(deserialize_with = "crate::utils::empty_string_is_none")]
556 pub address2: Option<String>,
557 pub city: String,
559 pub state: String,
561 pub zip: String,
563 pub status: VerificationStatus,
567 #[serde(deserialize_with = "crate::utils::deserialize_boolean")]
578 pub corrected: bool,
579 #[serde(default = "Vec::new")]
582 pub errors: Vec<Value>,
583 pub secondary_status: Option<String>,
594}
595
596#[cfg_attr(any(test, tarpaulin, feature = "ci"), derive(PartialEq))]
603#[derive(Debug, serde::Serialize, serde::Deserialize)]
604pub struct VerificationResponse {
605 #[serde(default)]
608 pub email: Option<EmailVerificationArray>,
609 #[serde(default)]
612 pub phone: Option<PhoneNumberVerificationArray>,
613 #[serde(default)]
616 pub address: Option<AddressVerificationArray>,
617 #[serde(
618 serialize_with = "crate::utils::duration_to_float",
619 deserialize_with = "crate::utils::float_to_duration"
620 )]
621 pub duration: Duration,
625}
626
627#[cfg(test)]
632#[doc(hidden)]
633mod foundry {
634 use std::collections::HashMap;
636
637 use serde::de::Error;
639 use serde_json::{Map as JsonMap, Value};
640
641 type RawAddressMap = HashMap<String, Option<String>>;
642 type RawAddressJson = JsonMap<String, Value>;
643
644 impl TryFrom<Value> for super::StreetAddressArray {
645 type Error = serde_json::Error;
646
647 fn try_from(value: Value) -> Result<Self, Self::Error> {
648 (&value).try_into()
649 }
650 }
651
652 impl TryFrom<&Value> for super::StreetAddressArray {
653 type Error = serde_json::Error;
654
655 fn try_from(value: &Value) -> Result<Self, Self::Error> {
656 match value.as_object() {
657 None => Err(Self::Error::custom(format!(
658 "Cannot create a `StreetAddressArray` from: {:#?}",
659 value.as_str()
660 ))),
661 Some(data) => {
662 if let Some(obj) = data.get("address") {
663 return obj.try_into();
664 }
665
666 data.try_into()
667 }
668 }
669 }
670 }
671
672 impl TryFrom<RawAddressMap> for super::StreetAddressArray {
673 type Error = serde_json::Error;
674
675 fn try_from(data: RawAddressMap) -> Result<Self, Self::Error> {
676 (&data).try_into()
677 }
678 }
679
680 impl TryFrom<&RawAddressMap> for super::StreetAddressArray {
681 type Error = serde_json::Error;
682
683 fn try_from(data: &RawAddressMap) -> Result<Self, Self::Error> {
684 let (address1, address2, city, state, zip) = (
685 data.get("address1").unwrap().clone(),
686 data.get("address2").unwrap().clone(),
687 data.get("city").unwrap().clone(),
688 data.get("state").unwrap().clone(),
689 data.get("zip").unwrap().clone(),
690 );
691
692 match super::AddressArrayBuilder::from_values(address1, address2, city, state, zip)
693 .build()
694 {
695 Ok(address) => Ok(address),
696 Err(_) => Err(Self::Error::custom(format!(
697 "One or more required fields missing from: {data:#?}"
698 ))),
699 }
700 }
701 }
702
703 impl TryFrom<RawAddressJson> for super::StreetAddressArray {
704 type Error = serde_json::Error;
705
706 fn try_from(value: RawAddressJson) -> Result<Self, Self::Error> {
707 (&value).try_into()
708 }
709 }
710
711 impl TryFrom<&RawAddressJson> for super::StreetAddressArray {
712 type Error = serde_json::Error;
713
714 fn try_from(data: &RawAddressJson) -> Result<Self, Self::Error> {
715 ["address1", "city", "state", "zip"]
716 .into_iter()
717 .map(|key| (key.to_string(), data.get(key)))
718 .map(|(key, value)| (key, value.map(|value| value.to_string())))
719 .collect::<HashMap<String, Option<String>>>()
720 .try_into()
721 }
722 }
723
724 impl super::AddressArrayBuilder {
725 #[cfg_attr(tarpaulin, coverage(off))]
726 #[cfg_attr(tarpaulin, tarpaulin::skip)]
727 pub fn address1_value(&self) -> Option<&String> {
729 self._address1.as_ref()
730 }
731
732 #[cfg_attr(tarpaulin, coverage(off))]
733 #[cfg_attr(tarpaulin, tarpaulin::skip)]
734 pub fn address2_value(&self) -> Option<&String> {
736 self._address2.as_ref()
737 }
738
739 #[cfg_attr(tarpaulin, coverage(off))]
740 #[cfg_attr(tarpaulin, tarpaulin::skip)]
741 pub fn city_value(&self) -> Option<&String> {
743 self._city.as_ref()
744 }
745
746 #[cfg_attr(tarpaulin, coverage(off))]
747 #[cfg_attr(tarpaulin, tarpaulin::skip)]
748 pub fn state_value(&self) -> Option<&String> {
750 self._state.as_ref()
751 }
752
753 #[cfg_attr(tarpaulin, coverage(off))]
754 #[cfg_attr(tarpaulin, tarpaulin::skip)]
755 pub fn zip_value(&self) -> Option<&String> {
757 self._zip.as_ref()
758 }
759 }
760}
761
762#[cfg(test)]
767mod tests {
768 use std::clone::Clone;
770
771 use anyhow::Result;
773 use pretty_assertions::{assert_eq, assert_ne, assert_str_eq};
774
775 const STATE: &str = "CA";
778 const ZIP: &str = "90210";
779 const CITY: &str = "Any Town";
780 const ADDRESS1: &str = "123 Main St.";
781 const ADDRESS2: Option<&str> = Some("P.O. Box 456");
782 const EMAIL: &str = "test@example.com";
783 const PHONE: &str = "+1 (954) 555-1234 ext. 6789";
784
785 #[rstest::rstest]
790 fn test_address_from_values() {
791 let instance = super::AddressArrayBuilder::from_values(
792 Some(ADDRESS1),
793 ADDRESS2,
794 Some(CITY),
795 Some(STATE),
796 Some(ZIP),
797 )
798 .build();
799
800 assert!(instance.is_ok(), "{:#?}", instance.unwrap_err());
801
802 let instance = instance.unwrap();
803
804 assert_str_eq!(ZIP, instance.zip);
805 assert_str_eq!(CITY, instance.city);
806 assert_str_eq!(STATE, instance.state);
807 assert_str_eq!(ADDRESS1, instance.address1);
808 assert_str_eq!(format!("{ADDRESS2:?}"), format!("{:?}", instance.address2));
809 }
810
811 #[rstest::rstest]
814 fn test_address_equality() -> Result<()> {
815 let left = super::StreetAddressArray::from_values(ADDRESS1, ADDRESS2, CITY, STATE, ZIP);
816
817 #[allow(clippy::redundant_clone)]
818 let mut right = left.clone();
819
820 assert_eq!(left, right);
821
822 right.address2 = None;
823
824 Ok(assert_ne!(left, right))
825 }
826
827 #[rstest::rstest]
830 fn test_address_buildability() {
831 let builder = super::StreetAddressArray::builder();
832
833 assert_eq!(
834 !builder.buildable(),
835 builder.address1_value().is_none()
836 && builder.address2_value().is_none()
837 && builder.city_value().is_none()
838 && builder.state_value().is_none()
839 && builder.zip_value().is_none()
840 );
841
842 let builder = builder
845 .address2(ADDRESS2.unwrap())
846 .city(CITY)
847 .state(STATE)
848 .zip(ZIP)
849 .build();
850
851 assert!(builder.is_err());
852
853 let builder = match builder.unwrap_err() {
854 super::BriteVerifyTypeError::UnbuildableAddressArray(inner) => inner,
855 _ => panic!(),
856 };
857
858 let builder = builder.address1(ADDRESS1).build();
859
860 assert!(builder.is_ok());
861 }
862
863 #[rstest::rstest]
866 fn test_try_into_verification_request() {
867 assert!(super::VerificationRequest::try_from(EMAIL)
868 .is_ok_and(|req| req.email.is_some_and(|email| email == EMAIL)));
869 assert!(super::VerificationRequest::try_from(PHONE)
870 .is_ok_and(|req| req.phone.is_some_and(|phone| phone == PHONE)));
871
872 let address_data = format!(
873 r#"{{"address":{{"address1":"{ADDRESS1}","city":"{CITY}","state":"{STATE}","zip":"{ZIP}"}}}}"#
874 );
875 assert!(
876 super::VerificationRequest::try_from(address_data).is_ok_and(|req| req.email.is_none()
877 && req.phone.is_none()
878 && req.address.is_some())
879 );
880
881 assert!(super::VerificationRequest::try_from(format!(
882 r#"{ADDRESS1}, {CITY}, {STATE} {ZIP}"#
883 ))
884 .is_err_and(|error| {
885 matches!(error, super::BriteVerifyTypeError::AmbiguousTryFromValue(_))
886 }));
887 }
888
889 #[rstest::rstest]
893 fn test_verification_request_buildability() {
894 let builder = super::VerificationRequest::builder();
895
896 assert!(!builder.buildable());
897
898 let builder = builder
899 .address2(ADDRESS2.unwrap())
900 .city(CITY)
901 .state(STATE)
902 .zip(ZIP);
903
904 assert!(!builder.buildable());
905
906 let builder = builder.email(EMAIL).phone(PHONE).address1(ADDRESS1);
907
908 assert!(builder.buildable());
909 assert!(builder.build().is_ok());
910
911 let mut build_result = super::VerificationRequest::builder().build();
913
914 assert!(
915 build_result.as_ref().is_err_and(|error| matches!(
916 error,
917 super::BriteVerifyTypeError::UnbuildableRequest(_)
918 )),
919 "Expected Err(UnbuildableRequest), got: {:#?}",
920 build_result.as_ref(),
921 );
922
923 build_result = super::VerificationRequest::builder().email(EMAIL).build();
925
926 assert!(
927 build_result.as_ref().is_ok_and(|req| req.email.is_some()
928 && req.phone.is_none()
929 && req.address.is_none()),
930 "Expected Ok(VerificationRequest) w/ Some(email), got: {:#?}",
931 build_result.as_ref(),
932 );
933
934 build_result = super::VerificationRequest::builder().phone(PHONE).build();
936
937 assert!(
938 build_result.as_ref().is_ok_and(|req| req.email.is_none()
939 && req.phone.is_some()
940 && req.address.is_none()),
941 "Expected Ok(VerificationRequest) w/ Some(phone), got: {:#?}",
942 build_result.as_ref(),
943 );
944
945 build_result = super::VerificationRequest::builder()
947 .address1(ADDRESS1)
948 .address2(ADDRESS2.unwrap())
949 .city(CITY)
950 .state(STATE)
951 .zip(ZIP)
952 .build();
953
954 assert!(
955 build_result.as_ref().is_ok_and(|req| req.email.is_none()
956 && req.phone.is_none()
957 && req.address.is_some()),
958 "Expected Ok(VerificationRequest) w/ Some(address), got: {:#?}",
959 build_result.as_ref(),
960 );
961
962 build_result = super::VerificationRequest::builder()
964 .email(EMAIL)
965 .phone(PHONE)
966 .build();
967
968 assert!(
969 build_result.as_ref().is_ok_and(|req| req.email.is_some()
970 && req.phone.is_some()
971 && req.address.is_none()),
972 "Expected Ok(VerificationRequest) w/ Some(email) & Some(phone), got: {:#?}",
973 build_result.as_ref(),
974 );
975
976 build_result = super::VerificationRequest::builder()
978 .email(EMAIL)
979 .address1(ADDRESS1)
980 .address2(ADDRESS2.unwrap())
981 .city(CITY)
982 .state(STATE)
983 .zip(ZIP)
984 .build();
985
986 assert!(
987 build_result.as_ref().is_ok_and(|req| req.email.is_some()
988 && req.phone.is_none()
989 && req.address.is_some()),
990 "Expected Ok(VerificationRequest) w/ Some(email) & Some(address), got: {:#?}",
991 build_result.as_ref(),
992 );
993
994 build_result = super::VerificationRequest::builder()
996 .phone(PHONE)
997 .address1(ADDRESS1)
998 .address2(ADDRESS2.unwrap())
999 .city(CITY)
1000 .state(STATE)
1001 .zip(ZIP)
1002 .build();
1003
1004 assert!(
1005 build_result.as_ref().is_ok_and(|req| req.email.is_none()
1006 && req.phone.is_some()
1007 && req.address.is_some()),
1008 "Expected Ok(VerificationRequest) w/ Some(phone) & Some(address), got: {:#?}",
1009 build_result.as_ref(),
1010 );
1011
1012 build_result = super::VerificationRequest::from_values(
1014 Some(EMAIL),
1015 Some(PHONE),
1016 Some(ADDRESS1),
1017 ADDRESS2,
1018 Some(CITY),
1019 Some(STATE),
1020 Some(ZIP),
1021 );
1022
1023 assert!(
1024 build_result.as_ref().is_ok_and(|req| req.email.is_some()
1025 && req.phone.is_some()
1026 && req.address.is_some()),
1027 "Expected Ok(VerificationRequest) w/ all fields populated, got: {:#?}",
1028 build_result.as_ref(),
1029 );
1030 }
1031}
1032
1033