aws_smithy_types/
endpoint.rs

1/*
2 *  Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *  SPDX-License-Identifier: Apache-2.0
4 */
5//! Smithy Endpoint Types
6
7use crate::config_bag::{Storable, StoreReplace};
8use crate::Document;
9use std::borrow::Cow;
10use std::collections::HashMap;
11
12type MaybeStatic = Cow<'static, str>;
13
14/* ANCHOR: endpoint */
15/// Smithy Endpoint Type
16///
17/// Generally, this type should not be used from user code
18#[derive(Debug, Clone, PartialEq)]
19pub struct Endpoint {
20    url: MaybeStatic,
21    headers: HashMap<MaybeStatic, Vec<MaybeStatic>>,
22    properties: HashMap<MaybeStatic, Document>,
23}
24
25/* ANCHOR_END: endpoint */
26
27#[allow(unused)]
28impl Endpoint {
29    /// Returns the URL of this endpoint
30    pub fn url(&self) -> &str {
31        &self.url
32    }
33
34    /// Returns the headers associated with this endpoint
35    pub fn headers(&self) -> impl Iterator<Item = (&str, impl Iterator<Item = &str>)> {
36        self.headers
37            .iter()
38            .map(|(k, v)| (k.as_ref(), v.iter().map(|v| v.as_ref())))
39    }
40
41    /// Returns the properties associated with this endpoint
42    pub fn properties(&self) -> &HashMap<Cow<'static, str>, Document> {
43        &self.properties
44    }
45
46    /// Converts this endpoint back into a [`Builder`]
47    pub fn into_builder(self) -> Builder {
48        Builder { endpoint: self }
49    }
50
51    /// A builder for [`Endpoint`]
52    pub fn builder() -> Builder {
53        Builder::new()
54    }
55}
56
57impl Storable for Endpoint {
58    type Storer = StoreReplace<Self>;
59}
60
61#[derive(Debug, Clone)]
62/// Builder for [`Endpoint`]
63pub struct Builder {
64    endpoint: Endpoint,
65}
66
67#[allow(unused)]
68impl Builder {
69    pub(crate) fn new() -> Self {
70        Self {
71            endpoint: Endpoint {
72                url: Default::default(),
73                headers: HashMap::new(),
74                properties: HashMap::new(),
75            },
76        }
77    }
78
79    /// Set the URL of the Endpoint
80    ///
81    /// # Examples
82    /// ```rust
83    /// use aws_smithy_types::endpoint::Endpoint;
84    /// let endpoint = Endpoint::builder().url("https://www.example.com").build();
85    /// ```
86    pub fn url(mut self, url: impl Into<MaybeStatic>) -> Self {
87        self.endpoint.url = url.into();
88        self
89    }
90
91    /// Adds a header to the endpoint
92    ///
93    /// If there is already a header for this key, this header will be appended to that key
94    ///
95    /// # Examples
96    /// ```rust
97    /// use aws_smithy_types::endpoint::Endpoint;
98    /// let endpoint = Endpoint::builder().url("https://www.example.com").header("x-my-header", "hello").build();
99    /// ```
100    pub fn header(mut self, name: impl Into<MaybeStatic>, value: impl Into<MaybeStatic>) -> Self {
101        self.endpoint
102            .headers
103            .entry(name.into())
104            .or_default()
105            .push(value.into());
106        self
107    }
108
109    /// Adds a property to the endpoint
110    ///
111    /// If there is already a property for this key, the existing property will be overwritten
112    ///
113    /// # Examples
114    /// ```rust
115    /// use aws_smithy_types::endpoint::Endpoint;
116    /// let endpoint = Endpoint::builder()
117    ///   .url("https://www.example.com")
118    ///   .property("x-my-header", true)
119    ///   .build();
120    /// ```
121    pub fn property(mut self, key: impl Into<MaybeStatic>, value: impl Into<Document>) -> Self {
122        self.endpoint.properties.insert(key.into(), value.into());
123        self
124    }
125
126    /// Constructs an [`Endpoint`] from this builder
127    ///
128    /// # Panics
129    /// Panics if URL is unset or empty
130    pub fn build(self) -> Endpoint {
131        assert_ne!(self.endpoint.url(), "", "URL was unset");
132        self.endpoint
133    }
134}
135
136#[cfg(test)]
137mod test {
138    use crate::endpoint::Endpoint;
139    use crate::Document;
140    use std::borrow::Cow;
141    use std::collections::HashMap;
142
143    #[test]
144    fn endpoint_builder() {
145        let endpoint = Endpoint::builder()
146            .url("https://www.amazon.com")
147            .header("x-amz-test", "header-value")
148            .property("custom", Document::Bool(true))
149            .build();
150        assert_eq!(endpoint.url, Cow::Borrowed("https://www.amazon.com"));
151        assert_eq!(
152            endpoint.headers,
153            HashMap::from([(
154                Cow::Borrowed("x-amz-test"),
155                vec![Cow::Borrowed("header-value")]
156            )])
157        );
158        assert_eq!(
159            endpoint.properties,
160            HashMap::from([(Cow::Borrowed("custom"), Document::Bool(true))])
161        );
162
163        assert_eq!(endpoint.url(), "https://www.amazon.com");
164        assert_eq!(
165            endpoint
166                .headers()
167                .map(|(k, v)| (k, v.collect::<Vec<_>>()))
168                .collect::<Vec<_>>(),
169            vec![("x-amz-test", vec!["header-value"])]
170        );
171    }
172
173    #[test]
174    fn borrowed_values() {
175        fn foo(a: &str) {
176            // borrowed values without a static lifetime need to be converted into owned values
177            let endpoint = Endpoint::builder().url(a.to_string()).build();
178            assert_eq!(endpoint.url(), a);
179        }
180
181        foo("asdf");
182    }
183}