132 133 | }
|
133 134 | }
|
134 135 |
|
135 136 | impl std::error::Error for AuthSchemeOptionBuilderError {}
|
136 137 |
|
137 138 | /// New type around an auth scheme ID.
|
138 139 | ///
|
139 140 | /// Each auth scheme must have a unique string identifier associated with it,
|
140 141 | /// which is used to refer to auth schemes by the auth scheme option resolver, and
|
141 142 | /// also used to select an identity resolver to use.
|
142 - | #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
143 + | #[derive(Clone, Debug, Eq)]
|
143 144 | pub struct AuthSchemeId {
|
144 145 | scheme_id: Cow<'static, str>,
|
145 146 | }
|
146 147 |
|
147 148 | // See: https://doc.rust-lang.org/std/convert/trait.AsRef.html#reflexivity
|
148 149 | impl AsRef<AuthSchemeId> for AuthSchemeId {
|
149 150 | fn as_ref(&self) -> &AuthSchemeId {
|
150 151 | self
|
151 152 | }
|
152 153 | }
|
153 154 |
|
154 155 | impl AuthSchemeId {
|
155 156 | /// Creates a new auth scheme ID.
|
156 157 | pub const fn new(scheme_id: &'static str) -> Self {
|
157 158 | Self {
|
158 159 | scheme_id: Cow::Borrowed(scheme_id),
|
159 160 | }
|
160 161 | }
|
161 162 |
|
162 163 | /// Returns the string equivalent of this auth scheme ID.
|
163 164 | #[deprecated(
|
164 165 | note = "This function is no longer functional. Use `inner` instead",
|
165 166 | since = "1.8.0"
|
166 167 | )]
|
167 168 | pub const fn as_str(&self) -> &'static str {
|
168 169 | match self.scheme_id {
|
169 170 | Cow::Borrowed(val) => val,
|
170 171 | Cow::Owned(_) => {
|
171 172 | // cannot obtain `&'static str` from `String` unless we use `Box::leak`
|
172 173 | ""
|
173 174 | }
|
174 175 | }
|
175 176 | }
|
176 177 |
|
177 178 | /// Returns the string equivalent of this auth scheme ID.
|
178 179 | pub fn inner(&self) -> &str {
|
179 180 | &self.scheme_id
|
180 181 | }
|
181 182 | }
|
182 183 |
|
183 184 | impl From<&'static str> for AuthSchemeId {
|
184 185 | fn from(scheme_id: &'static str) -> Self {
|
185 186 | Self::new(scheme_id)
|
186 187 | }
|
187 188 | }
|
188 189 |
|
189 190 | impl From<Cow<'static, str>> for AuthSchemeId {
|
190 191 | fn from(scheme_id: Cow<'static, str>) -> Self {
|
191 192 | Self { scheme_id }
|
192 193 | }
|
193 194 | }
|
194 195 |
|
196 + | impl PartialEq for AuthSchemeId {
|
197 + | fn eq(&self, other: &Self) -> bool {
|
198 + | let self_normalized = normalize_auth_scheme_id(&self.scheme_id);
|
199 + | let other_normalized = normalize_auth_scheme_id(&other.scheme_id);
|
200 + | self_normalized == other_normalized
|
201 + | }
|
202 + | }
|
203 + |
|
204 + | impl std::hash::Hash for AuthSchemeId {
|
205 + | fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
206 + | // Hash the normalized scheme ID to ensure equal `AuthSchemeId`s have equal hashes
|
207 + | normalize_auth_scheme_id(&self.scheme_id).hash(state);
|
208 + | }
|
209 + | }
|
210 + |
|
211 + | impl Ord for AuthSchemeId {
|
212 + | fn cmp(&self, other: &Self) -> Ordering {
|
213 + | let self_normalized = normalize_auth_scheme_id(&self.scheme_id);
|
214 + | let other_normalized = normalize_auth_scheme_id(&other.scheme_id);
|
215 + | self_normalized.cmp(other_normalized)
|
216 + | }
|
217 + | }
|
218 + |
|
219 + | impl PartialOrd for AuthSchemeId {
|
220 + | fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
221 + | Some(self.cmp(other))
|
222 + | }
|
223 + | }
|
224 + |
|
225 + | // Normalizes auth scheme IDs for comparison and hashing by treating "no_auth" and "noAuth" as equivalent
|
226 + | // by converting "no_auth" to "noAuth".
|
227 + | // This is for backward compatibility; "no_auth" was incorrectly used in pre-GA versions of the SDK and
|
228 + | // could be used still in some places.
|
229 + | fn normalize_auth_scheme_id(scheme_id: &str) -> &str {
|
230 + | if scheme_id == "no_auth" {
|
231 + | "noAuth"
|
232 + | } else {
|
233 + | scheme_id
|
234 + | }
|
235 + | }
|
236 + |
|
195 237 | /// Parameters needed to resolve auth scheme options.
|
196 238 | ///
|
197 239 | /// Most generated clients will use the [`StaticAuthSchemeOptionResolver`](static_resolver::StaticAuthSchemeOptionResolver),
|
198 240 | /// which doesn't require any parameters for resolution (and has its own empty params struct).
|
199 241 | ///
|
200 242 | /// However, more complex auth scheme resolvers may need modeled parameters in order to resolve
|
201 243 | /// the auth scheme options. For those, this params struct holds a type erased box so that any
|
202 244 | /// kind of parameters can be contained within, and type casted by the auth scheme option resolver
|
203 245 | /// implementation.
|
204 246 | #[derive(Debug)]
|
447 489 | impl<T> From<T> for AuthSchemePreference
|
448 490 | where
|
449 491 | T: AsRef<[AuthSchemeId]>,
|
450 492 | {
|
451 493 | fn from(slice: T) -> Self {
|
452 494 | AuthSchemePreference {
|
453 495 | preference_list: slice.as_ref().to_vec(),
|
454 496 | }
|
455 497 | }
|
456 498 | }
|
499 + |
|
500 + | #[cfg(test)]
|
501 + | mod tests {
|
502 + | use super::*;
|
503 + |
|
504 + | #[test]
|
505 + | fn test_auth_scheme_id_equality_no_auth_variants() {
|
506 + | let no_auth_legacy = AuthSchemeId::new("no_auth");
|
507 + | let no_auth_camel = AuthSchemeId::new("noAuth");
|
508 + |
|
509 + | // Test that "no_auth" and "noAuth" are considered equal
|
510 + | assert_eq!(no_auth_legacy, no_auth_camel);
|
511 + | assert_eq!(no_auth_camel, no_auth_legacy);
|
512 + | }
|
513 + |
|
514 + | #[test]
|
515 + | fn test_auth_scheme_id_equality_same_schemes() {
|
516 + | let sigv4_1 = AuthSchemeId::new("sigv4");
|
517 + | let sigv4_2 = AuthSchemeId::new("sigv4");
|
518 + |
|
519 + | // Test that identical schemes are equal
|
520 + | assert_eq!(sigv4_1, sigv4_2);
|
521 + | }
|
522 + |
|
523 + | #[test]
|
524 + | fn test_auth_scheme_id_inequality_different_schemes() {
|
525 + | let sigv4 = AuthSchemeId::new("sigv4");
|
526 + | let sigv4a = AuthSchemeId::new("sigv4a");
|
527 + | let bearer = AuthSchemeId::new("httpBearerAuth");
|
528 + |
|
529 + | // Test that different schemes are not equal
|
530 + | assert_ne!(sigv4, sigv4a);
|
531 + | assert_ne!(sigv4, bearer);
|
532 + | assert_ne!(sigv4a, bearer);
|
533 + | }
|
534 + |
|
535 + | #[test]
|
536 + | fn test_auth_scheme_id_no_auth_vs_other_schemes() {
|
537 + | let no_auth_legacy = AuthSchemeId::new("no_auth");
|
538 + | let no_auth_camel = AuthSchemeId::new("noAuth");
|
539 + | let sigv4 = AuthSchemeId::new("sigv4");
|
540 + | let bearer = AuthSchemeId::new("httpBearerAuth");
|
541 + |
|
542 + | // Test that no_auth variants are not equal to other schemes
|
543 + | assert_ne!(no_auth_legacy, sigv4);
|
544 + | assert_ne!(no_auth_camel, sigv4);
|
545 + | assert_ne!(no_auth_legacy, bearer);
|
546 + | assert_ne!(no_auth_camel, bearer);
|
547 + |
|
548 + | // Test symmetry
|
549 + | assert_ne!(sigv4, no_auth_legacy);
|
550 + | assert_ne!(sigv4, no_auth_camel);
|
551 + | assert_ne!(bearer, no_auth_legacy);
|
552 + | assert_ne!(bearer, no_auth_camel);
|
553 + | }
|
554 + |
|
555 + | #[test]
|
556 + | fn test_normalize_auth_scheme_id_function() {
|
557 + | // Test the helper function directly
|
558 + | assert_eq!("noAuth", normalize_auth_scheme_id("no_auth"));
|
559 + | assert_eq!("noAuth", normalize_auth_scheme_id("noAuth"));
|
560 + | assert_eq!("sigv4", normalize_auth_scheme_id("sigv4"));
|
561 + | assert_eq!("httpBearerAuth", normalize_auth_scheme_id("httpBearerAuth"));
|
562 + | assert_eq!("custom_scheme", normalize_auth_scheme_id("custom_scheme"));
|
563 + | }
|
564 + |
|
565 + | #[test]
|
566 + | fn test_auth_scheme_id_reflexivity() {
|
567 + | let no_auth_legacy = AuthSchemeId::new("no_auth");
|
568 + | let no_auth_camel = AuthSchemeId::new("noAuth");
|
569 + | let sigv4 = AuthSchemeId::new("sigv4");
|
570 + |
|
571 + | // Test reflexivity: x == x
|
572 + | assert_eq!(no_auth_legacy, no_auth_legacy);
|
573 + | assert_eq!(no_auth_camel, no_auth_camel);
|
574 + | assert_eq!(sigv4, sigv4);
|
575 + | }
|
576 + |
|
577 + | #[test]
|
578 + | fn test_auth_scheme_id_transitivity() {
|
579 + | let no_auth_1 = AuthSchemeId::new("no_auth");
|
580 + | let no_auth_2 = AuthSchemeId::new("noAuth");
|
581 + | let no_auth_3 = AuthSchemeId::new("no_auth");
|
582 + |
|
583 + | // Test transitivity: if a == b and b == c, then a == c
|
584 + | assert_eq!(no_auth_1, no_auth_2);
|
585 + | assert_eq!(no_auth_2, no_auth_3);
|
586 + | assert_eq!(no_auth_1, no_auth_3);
|
587 + | }
|
588 + |
|
589 + | #[test]
|
590 + | fn test_auth_scheme_id_hash_consistency() {
|
591 + | use std::collections::hash_map::DefaultHasher;
|
592 + | use std::hash::{Hash, Hasher};
|
593 + |
|
594 + | fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
595 + | let mut s = DefaultHasher::new();
|
596 + | t.hash(&mut s);
|
597 + | s.finish()
|
598 + | }
|
599 + |
|
600 + | // Test that equal AuthSchemeIds have the same hash
|
601 + | let no_auth_legacy = AuthSchemeId::new("no_auth");
|
602 + | let no_auth_camel = AuthSchemeId::new("noAuth");
|
603 + |
|
604 + | // Since these are equal, they must have the same hash
|
605 + | assert_eq!(no_auth_legacy, no_auth_camel);
|
606 + | assert_eq!(
|
607 + | calculate_hash(&no_auth_legacy),
|
608 + | calculate_hash(&no_auth_camel)
|
609 + | );
|
610 + |
|
611 + | // Test that identical schemes have the same hash
|
612 + | let sigv4_1 = AuthSchemeId::new("sigv4");
|
613 + | let sigv4_2 = AuthSchemeId::new("sigv4");
|
614 + | assert_eq!(calculate_hash(&sigv4_1), calculate_hash(&sigv4_2));
|
615 + |
|
616 + | // Test that different schemes have different hashes (highly likely but not guaranteed)
|
617 + | let sigv4 = AuthSchemeId::new("sigv4");
|
618 + | let sigv4a = AuthSchemeId::new("sigv4a");
|
619 + | let bearer = AuthSchemeId::new("httpBearerAuth");
|
620 + |
|
621 + | // These should be different (though hash collisions are theoretically possible)
|
622 + | assert_ne!(calculate_hash(&sigv4), calculate_hash(&sigv4a));
|
623 + | assert_ne!(calculate_hash(&sigv4), calculate_hash(&bearer));
|
624 + | assert_ne!(calculate_hash(&sigv4a), calculate_hash(&bearer));
|
625 + | }
|
626 + |
|
627 + | #[test]
|
628 + | fn test_auth_scheme_id_hash_in_collections() {
|
629 + | use std::collections::{HashMap, HashSet};
|
630 + |
|
631 + | let no_auth_legacy = AuthSchemeId::new("no_auth");
|
632 + | let no_auth_camel = AuthSchemeId::new("noAuth");
|
633 + | let sigv4 = AuthSchemeId::new("sigv4");
|
634 + | let bearer = AuthSchemeId::new("httpBearerAuth");
|
635 + |
|
636 + | // Test HashSet behavior - equal items should be treated as the same
|
637 + | let mut set = HashSet::new();
|
638 + | set.insert(no_auth_legacy.clone());
|
639 + | set.insert(no_auth_camel.clone());
|
640 + | set.insert(sigv4.clone());
|
641 + | set.insert(bearer.clone());
|
642 + |
|
643 + | // Should only have 3 items since no_auth_legacy and no_auth_camel are equal
|
644 + | assert_eq!(set.len(), 3);
|
645 + | assert!(set.contains(&no_auth_legacy));
|
646 + | assert!(set.contains(&no_auth_camel));
|
647 + | assert!(set.contains(&sigv4));
|
648 + | assert!(set.contains(&bearer));
|
649 + |
|
650 + | // Test HashMap behavior
|
651 + | let mut map = HashMap::new();
|
652 + | map.insert(no_auth_legacy.clone(), "legacy");
|
653 + | map.insert(no_auth_camel.clone(), "camel");
|
654 + | map.insert(sigv4.clone(), "v4");
|
655 + |
|
656 + | // Should only have 2 entries since no_auth_legacy and no_auth_camel are equal
|
657 + | assert_eq!(map.len(), 2);
|
658 + | // The value should be "camel" since it was inserted last
|
659 + | assert_eq!(map.get(&no_auth_legacy), Some(&"camel"));
|
660 + | assert_eq!(map.get(&no_auth_camel), Some(&"camel"));
|
661 + | assert_eq!(map.get(&sigv4), Some(&"v4"));
|
662 + | }
|
663 + |
|
664 + | #[test]
|
665 + | fn test_auth_scheme_id_ord_consistency() {
|
666 + | let no_auth_legacy = AuthSchemeId::new("no_auth");
|
667 + | let no_auth_camel = AuthSchemeId::new("noAuth");
|
668 + | let sigv4 = AuthSchemeId::new("sigv4");
|
669 + | let sigv4a = AuthSchemeId::new("sigv4a");
|
670 + | let bearer = AuthSchemeId::new("httpBearerAuth");
|
671 + |
|
672 + | // Test that equal items compare as equal
|
673 + | assert_eq!(no_auth_legacy.cmp(&no_auth_camel), Ordering::Equal);
|
674 + | assert_eq!(no_auth_camel.cmp(&no_auth_legacy), Ordering::Equal);
|
675 + |
|
676 + | // Test reflexivity: x.cmp(&x) == Equal
|
677 + | assert_eq!(sigv4.cmp(&sigv4), Ordering::Equal);
|
678 + | assert_eq!(bearer.cmp(&bearer), Ordering::Equal);
|
679 + |
|
680 + | // Test that ordering is consistent with string ordering of normalized values
|
681 + | // "sigv4" < "sigv4a" lexicographically
|
682 + | assert_eq!(sigv4.cmp(&sigv4a), Ordering::Less);
|
683 + | assert_eq!(sigv4a.cmp(&sigv4), Ordering::Greater);
|
684 + |
|
685 + | // Test transitivity with a chain of comparisons
|
686 + | let schemes = vec![
|
687 + | AuthSchemeId::new("a_scheme"),
|
688 + | AuthSchemeId::new("b_scheme"),
|
689 + | AuthSchemeId::new("c_scheme"),
|
690 + | ];
|
691 + |
|
692 + | // a < b < c should hold
|
693 + | assert_eq!(schemes[0].cmp(&schemes[1]), Ordering::Less);
|
694 + | assert_eq!(schemes[1].cmp(&schemes[2]), Ordering::Less);
|
695 + | assert_eq!(schemes[0].cmp(&schemes[2]), Ordering::Less);
|
696 + | }
|
697 + |
|
698 + | #[test]
|
699 + | fn test_auth_scheme_id_ord_sorting() {
|
700 + | let mut schemes = vec![
|
701 + | AuthSchemeId::new("z_last"),
|
702 + | AuthSchemeId::new("no_auth"), // Should be normalized to "noAuth"
|
703 + | AuthSchemeId::new("sigv4a"),
|
704 + | AuthSchemeId::new("noAuth"),
|
705 + | AuthSchemeId::new("sigv4"),
|
706 + | AuthSchemeId::new("a_first"),
|
707 + | ];
|
708 + |
|
709 + | schemes.sort();
|
710 + | dbg!(&schemes);
|
711 + |
|
712 + | // Expected order after sorting (considering normalization):
|
713 + | // "a_first", "sigv4", "sigv4a", "no_auth", "noAuth", "z_last"
|
714 + | // Note: "no_auth" gets normalized to "noAuth" for comparison
|
715 + | let expected_inner_values =
|
716 + | vec!["a_first", "no_auth", "noAuth", "sigv4", "sigv4a", "z_last"];
|
717 + |
|
718 + | assert_eq!(schemes.len(), expected_inner_values.len());
|
719 + | for (scheme, expected) in schemes.iter().zip(expected_inner_values.iter()) {
|
720 + | assert_eq!(scheme.inner(), *expected);
|
721 + | }
|
722 + | }
|
723 + |
|
724 + | #[test]
|
725 + | fn test_auth_scheme_id_ord_with_cow_variants() {
|
726 + | use std::borrow::Cow;
|
727 + |
|
728 + | // Test ordering with different Cow variants
|
729 + | let borrowed = AuthSchemeId::new("test_scheme");
|
730 + | let owned = AuthSchemeId::from(Cow::Owned("test_scheme".to_string()));
|
731 + | let borrowed2 = AuthSchemeId::from(Cow::Borrowed("test_scheme"));
|
732 + |
|
733 + | // All should be equal
|
734 + | assert_eq!(borrowed, owned);
|
735 + | assert_eq!(borrowed, borrowed2);
|
736 + | assert_eq!(owned, borrowed2);
|
737 + |
|
738 + | // All should have the same ordering
|
739 + | assert_eq!(borrowed.cmp(&owned), Ordering::Equal);
|
740 + | assert_eq!(borrowed.cmp(&borrowed2), Ordering::Equal);
|
741 + | assert_eq!(owned.cmp(&borrowed2), Ordering::Equal);
|
742 + |
|
743 + | // Test with different values
|
744 + | let borrowed_a = AuthSchemeId::new("a_scheme");
|
745 + | let owned_b = AuthSchemeId::from(Cow::Owned("b_scheme".to_string()));
|
746 + |
|
747 + | assert_eq!(borrowed_a.cmp(&owned_b), Ordering::Less);
|
748 + | assert_eq!(owned_b.cmp(&borrowed_a), Ordering::Greater);
|
749 + | }
|
750 + | }
|