1use std::fmt;
2
3pub const US_INGESTION_ENDPOINT: &str = "https://us.i.posthog.com";
5
6pub const EU_INGESTION_ENDPOINT: &str = "https://eu.i.posthog.com";
8
9pub const DEFAULT_HOST: &str = US_INGESTION_ENDPOINT;
11
12#[derive(Debug, Clone)]
14pub enum Endpoint {
15 Capture,
17 Flags,
19 LocalEvaluation,
21}
22
23impl Endpoint {
24 pub fn path(&self) -> &str {
26 match self {
27 Endpoint::Capture => "/i/v0/e/",
28 Endpoint::Flags => "/flags/?v=2",
29 Endpoint::LocalEvaluation => "/api/feature_flag/local_evaluation/?send_cohorts",
30 }
31 }
32}
33
34impl fmt::Display for Endpoint {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(f, "{}", self.path())
37 }
38}
39
40#[derive(Debug, Clone)]
42pub struct EndpointManager {
43 base_host: String,
44 raw_host: String,
45}
46
47impl EndpointManager {
48 pub fn new(host: Option<String>) -> Self {
50 let raw_host = host.clone().unwrap_or_else(|| DEFAULT_HOST.to_string());
51 let base_host = Self::determine_server_host(host);
52
53 Self {
54 base_host,
55 raw_host,
56 }
57 }
58
59 pub fn determine_server_host(host: Option<String>) -> String {
62 let host_or_default = host.unwrap_or_else(|| DEFAULT_HOST.to_string());
63 let trimmed_host = host_or_default.trim_end_matches('/');
64
65 match trimmed_host {
66 "https://app.posthog.com" | "https://us.posthog.com" => {
67 US_INGESTION_ENDPOINT.to_string()
68 }
69 "https://eu.posthog.com" => EU_INGESTION_ENDPOINT.to_string(),
70 _ => trimmed_host.to_string(),
71 }
72 }
73
74 pub fn base_host(&self) -> &str {
76 &self.base_host
77 }
78
79 pub fn raw_host(&self) -> &str {
81 &self.raw_host
82 }
83
84 pub fn build_url(&self, endpoint: Endpoint) -> String {
86 format!(
87 "{}{}",
88 self.base_host.trim_end_matches('/'),
89 endpoint.path()
90 )
91 }
92
93 pub fn build_custom_url(&self, path: &str) -> String {
95 let normalized_path = if path.starts_with('/') {
96 path.to_string()
97 } else {
98 format!("/{path}")
99 };
100 format!(
101 "{}{}",
102 self.base_host.trim_end_matches('/'),
103 normalized_path
104 )
105 }
106
107 pub fn build_local_eval_url(&self, token: &str) -> String {
109 format!(
110 "{}/api/feature_flag/local_evaluation/?token={}&send_cohorts",
111 self.base_host.trim_end_matches('/'),
112 token
113 )
114 }
115
116 pub fn api_host(&self) -> String {
118 self.base_host.trim_end_matches('/').to_string()
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn test_determine_server_host() {
128 assert_eq!(
129 EndpointManager::determine_server_host(None),
130 US_INGESTION_ENDPOINT
131 );
132
133 assert_eq!(
134 EndpointManager::determine_server_host(Some("https://app.posthog.com".to_string())),
135 US_INGESTION_ENDPOINT
136 );
137
138 assert_eq!(
139 EndpointManager::determine_server_host(Some("https://us.posthog.com".to_string())),
140 US_INGESTION_ENDPOINT
141 );
142
143 assert_eq!(
144 EndpointManager::determine_server_host(Some("https://eu.posthog.com".to_string())),
145 EU_INGESTION_ENDPOINT
146 );
147
148 assert_eq!(
149 EndpointManager::determine_server_host(Some("https://custom.domain.com".to_string())),
150 "https://custom.domain.com"
151 );
152 }
153
154 #[test]
155 fn test_build_url() {
156 let manager = EndpointManager::new(None);
157
158 assert_eq!(
159 manager.build_url(Endpoint::Capture),
160 format!("{}/i/v0/e/", US_INGESTION_ENDPOINT)
161 );
162
163 assert_eq!(
164 manager.build_url(Endpoint::Flags),
165 format!("{}/flags/?v=2", US_INGESTION_ENDPOINT)
166 );
167 }
168
169 #[test]
170 fn test_build_custom_url() {
171 let manager = EndpointManager::new(Some("https://custom.com/".to_string()));
172
173 assert_eq!(
174 manager.build_custom_url("/api/test"),
175 "https://custom.com/api/test"
176 );
177
178 assert_eq!(
179 manager.build_custom_url("api/test"),
180 "https://custom.com/api/test"
181 );
182 }
183}