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