4 4 | */
|
5 5 | //! Smithy Endpoint Types
|
6 6 |
|
7 7 | use crate::config_bag::{Storable, StoreReplace};
|
8 8 | use crate::Document;
|
9 9 | use std::borrow::Cow;
|
10 10 | use std::collections::HashMap;
|
11 11 |
|
12 12 | type MaybeStatic = Cow<'static, str>;
|
13 13 |
|
14 + | /// An authentication scheme configuration for an endpoint.
|
15 + | ///
|
16 + | /// This is a lightweight alternative to storing auth schemes as
|
17 + | /// `Document::Object(HashMap<String, Document>)` in endpoint properties.
|
18 + | /// Properties are stored in a flat `Vec` and looked up via linear scan,
|
19 + | /// which is faster than HashMap for the typical 3-4 entries.
|
20 + | #[derive(Debug, Clone, PartialEq)]
|
21 + | pub struct EndpointAuthScheme {
|
22 + | name: MaybeStatic,
|
23 + | properties: Vec<(MaybeStatic, Document)>,
|
24 + | }
|
25 + |
|
26 + | impl EndpointAuthScheme {
|
27 + | /// Creates a new `EndpointAuthScheme` with pre-allocated capacity for properties.
|
28 + | pub fn with_capacity(name: impl Into<MaybeStatic>, capacity: usize) -> Self {
|
29 + | Self {
|
30 + | name: name.into(),
|
31 + | properties: Vec::with_capacity(capacity),
|
32 + | }
|
33 + | }
|
34 + |
|
35 + | /// Returns the auth scheme name (e.g., "sigv4", "sigv4a").
|
36 + | pub fn name(&self) -> &str {
|
37 + | &self.name
|
38 + | }
|
39 + |
|
40 + | /// Adds a property to this auth scheme. Chainable.
|
41 + | pub fn put(mut self, key: impl Into<MaybeStatic>, value: impl Into<Document>) -> Self {
|
42 + | self.properties.push((key.into(), value.into()));
|
43 + | self
|
44 + | }
|
45 + |
|
46 + | /// Gets a property value by name (linear scan).
|
47 + | pub fn get(&self, key: &str) -> Option<&Document> {
|
48 + | self.properties
|
49 + | .iter()
|
50 + | .find(|(k, _)| k.as_ref() == key)
|
51 + | .map(|(_, v)| v)
|
52 + | }
|
53 + |
|
54 + | /// Converts this auth scheme into a `Document` for backward compatibility.
|
55 + | pub fn as_document(&self) -> Document {
|
56 + | let mut map = HashMap::with_capacity(self.properties.len() + 1);
|
57 + | map.insert("name".to_string(), Document::String(self.name.to_string()));
|
58 + | for (k, v) in &self.properties {
|
59 + | map.insert(k.to_string(), v.clone());
|
60 + | }
|
61 + | Document::Object(map)
|
62 + | }
|
63 + | }
|
64 + |
|
14 65 | /* ANCHOR: endpoint */
|
15 66 | /// Smithy Endpoint Type
|
16 67 | ///
|
17 68 | /// Generally, this type should not be used from user code
|
18 69 | #[derive(Debug, Clone, PartialEq)]
|
19 70 | pub struct Endpoint {
|
20 71 | url: MaybeStatic,
|
21 72 | headers: HashMap<MaybeStatic, Vec<MaybeStatic>>,
|
22 73 | properties: HashMap<MaybeStatic, Document>,
|
74 + | auth_schemes: Vec<EndpointAuthScheme>,
|
23 75 | }
|
24 76 |
|
25 77 | /* ANCHOR_END: endpoint */
|
26 78 |
|
27 79 | #[allow(unused)]
|
28 80 | impl Endpoint {
|
29 81 | /// Returns the URL of this endpoint
|
30 82 | pub fn url(&self) -> &str {
|
31 83 | &self.url
|
32 84 | }
|
33 85 |
|
34 86 | /// Returns the headers associated with this endpoint
|
35 87 | pub fn headers(&self) -> impl Iterator<Item = (&str, impl Iterator<Item = &str>)> {
|
36 88 | self.headers
|
37 89 | .iter()
|
38 90 | .map(|(k, v)| (k.as_ref(), v.iter().map(|v| v.as_ref())))
|
39 91 | }
|
40 92 |
|
41 93 | /// Returns the properties associated with this endpoint
|
42 94 | pub fn properties(&self) -> &HashMap<Cow<'static, str>, Document> {
|
43 95 | &self.properties
|
44 96 | }
|
45 97 |
|
98 + | /// Returns the typed auth schemes associated with this endpoint
|
99 + | pub fn auth_schemes(&self) -> &[EndpointAuthScheme] {
|
100 + | &self.auth_schemes
|
101 + | }
|
102 + |
|
46 103 | /// Converts this endpoint back into a [`Builder`]
|
47 104 | pub fn into_builder(self) -> Builder {
|
48 105 | Builder { endpoint: self }
|
49 106 | }
|
50 107 |
|
51 108 | /// A builder for [`Endpoint`]
|
52 109 | pub fn builder() -> Builder {
|
53 110 | Builder::new()
|
54 111 | }
|
55 112 | }
|
56 113 |
|
57 114 | impl Storable for Endpoint {
|
58 115 | type Storer = StoreReplace<Self>;
|
59 116 | }
|
60 117 |
|
61 118 | #[derive(Debug, Clone)]
|
62 119 | /// Builder for [`Endpoint`]
|
63 120 | pub struct Builder {
|
64 121 | endpoint: Endpoint,
|
65 122 | }
|
66 123 |
|
67 124 | #[allow(unused)]
|
68 125 | impl Builder {
|
69 126 | pub(crate) fn new() -> Self {
|
70 127 | Self {
|
71 128 | endpoint: Endpoint {
|
72 129 | url: Default::default(),
|
73 130 | headers: HashMap::new(),
|
74 131 | properties: HashMap::new(),
|
132 + | auth_schemes: Vec::new(),
|
75 133 | },
|
76 134 | }
|
77 135 | }
|
78 136 |
|
79 137 | /// Set the URL of the Endpoint
|
80 138 | ///
|
81 139 | /// # Examples
|
82 140 | /// ```rust
|
83 141 | /// use aws_smithy_types::endpoint::Endpoint;
|
84 142 | /// let endpoint = Endpoint::builder().url("https://www.example.com").build();
|
85 143 | /// ```
|
86 144 | pub fn url(mut self, url: impl Into<MaybeStatic>) -> Self {
|
87 145 | self.endpoint.url = url.into();
|
88 146 | self
|
89 147 | }
|
90 148 |
|
91 149 | /// Adds a header to the endpoint
|
92 150 | ///
|
93 151 | /// If there is already a header for this key, this header will be appended to that key
|
94 152 | ///
|
95 153 | /// # Examples
|
96 154 | /// ```rust
|
97 155 | /// use aws_smithy_types::endpoint::Endpoint;
|
98 156 | /// let endpoint = Endpoint::builder().url("https://www.example.com").header("x-my-header", "hello").build();
|
99 157 | /// ```
|
100 158 | pub fn header(mut self, name: impl Into<MaybeStatic>, value: impl Into<MaybeStatic>) -> Self {
|
101 159 | self.endpoint
|
102 160 | .headers
|
103 161 | .entry(name.into())
|
104 162 | .or_default()
|
105 163 | .push(value.into());
|
106 164 | self
|
107 165 | }
|
108 166 |
|
109 167 | /// Adds a property to the endpoint
|
110 168 | ///
|
111 169 | /// If there is already a property for this key, the existing property will be overwritten
|
112 170 | ///
|
113 171 | /// # Examples
|
114 172 | /// ```rust
|
115 173 | /// use aws_smithy_types::endpoint::Endpoint;
|
116 174 | /// let endpoint = Endpoint::builder()
|
117 175 | /// .url("https://www.example.com")
|
118 176 | /// .property("x-my-header", true)
|
119 177 | /// .build();
|
120 178 | /// ```
|
121 179 | pub fn property(mut self, key: impl Into<MaybeStatic>, value: impl Into<Document>) -> Self {
|
122 180 | self.endpoint.properties.insert(key.into(), value.into());
|
123 181 | self
|
124 182 | }
|
125 183 |
|
184 + | /// Adds a typed auth scheme to the endpoint
|
185 + | pub fn auth_scheme(mut self, auth_scheme: EndpointAuthScheme) -> Self {
|
186 + | self.endpoint.auth_schemes.push(auth_scheme);
|
187 + | self
|
188 + | }
|
189 + |
|
126 190 | /// Constructs an [`Endpoint`] from this builder
|
127 191 | ///
|
128 192 | /// # Panics
|
129 193 | /// Panics if URL is unset or empty
|
130 194 | pub fn build(self) -> Endpoint {
|
131 195 | assert_ne!(self.endpoint.url(), "", "URL was unset");
|
132 196 | self.endpoint
|
133 197 | }
|
134 198 | }
|
135 199 |
|