launchdarkly_server_sdk/
feature_requester_builders.rs1use crate::feature_requester::{FeatureRequester, HyperFeatureRequester};
2use crate::LAUNCHDARKLY_TAGS_HEADER;
3use hyper::client::connect::Connection;
4use hyper::service::Service;
5use hyper::Uri;
6use std::collections::HashMap;
7use std::str::FromStr;
8use thiserror::Error;
9use tokio::io::{AsyncRead, AsyncWrite};
10
11#[non_exhaustive]
13#[derive(Debug, Error)]
14pub enum BuildError {
15 #[error("feature requester factory failed to build: {0}")]
17 InvalidConfig(String),
18}
19
20pub trait FeatureRequesterFactory: Send {
25 fn build(&self, tags: Option<String>) -> Result<Box<dyn FeatureRequester>, BuildError>;
27}
28
29pub struct HyperFeatureRequesterBuilder<C> {
30 url: String,
31 sdk_key: String,
32 http: hyper::Client<C>,
33}
34
35impl<C> HyperFeatureRequesterBuilder<C>
36where
37 C: Service<Uri> + Clone + Send + Sync + 'static,
38 C::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin,
39 C::Future: Send + Unpin + 'static,
40 C::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
41{
42 pub fn new(url: &str, sdk_key: &str, connector: C) -> Self {
43 Self {
44 http: hyper::Client::builder().build(connector),
45 url: url.into(),
46 sdk_key: sdk_key.into(),
47 }
48 }
49}
50
51impl<C> FeatureRequesterFactory for HyperFeatureRequesterBuilder<C>
52where
53 C: Service<Uri> + Clone + Send + Sync + 'static,
54 C::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin,
55 C::Future: Send + Unpin + 'static,
56 C::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
57{
58 fn build(&self, tags: Option<String>) -> Result<Box<dyn FeatureRequester>, BuildError> {
59 let url = format!("{}/sdk/latest-all", self.url);
60
61 let mut default_headers = HashMap::<&str, String>::new();
62
63 if let Some(tags) = tags {
64 default_headers.insert(LAUNCHDARKLY_TAGS_HEADER, tags);
65 }
66
67 let url = hyper::Uri::from_str(url.as_str())
68 .map_err(|_| BuildError::InvalidConfig("Invalid base url provided".into()))?;
69
70 Ok(Box::new(HyperFeatureRequester::new(
71 self.http.clone(),
72 url,
73 self.sdk_key.clone(),
74 default_headers,
75 )))
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use hyper::client::HttpConnector;
82
83 use super::*;
84
85 #[test]
86 fn factory_handles_url_parsing_failure() {
87 let builder = HyperFeatureRequesterBuilder::new(
88 "This is clearly not a valid URL",
89 "sdk-key",
90 HttpConnector::new(),
91 );
92 let result = builder.build(None);
93
94 match result {
95 Err(BuildError::InvalidConfig(_)) => (),
96 _ => panic!("Build did not return the right type of error"),
97 };
98 }
99}