Skip to main content

Mountain/Binary/Build/
PostHogPlugin.rs

1//! # PostHog Plugin Module
2//!
3//! Debug-only PostHog analytics integration for Mountain.
4//! Captures lifecycle events, IPC commands, errors, and performance metrics.
5//! Disabled in release builds (compile-time gated).
6
7use std::sync::OnceLock;
8
9use crate::dev_log;
10
11/// PostHog EU Cloud project token (debug builds only).
12const POSTHOG_API_KEY:&str = "phc_mCwHy7LgvbnEqh6a2DyMiLUJcaZvmmj7JNmmpQzvr7mA";
13
14/// PostHog EU Cloud host.
15const POSTHOG_HOST:&str = "https://eu.i.posthog.com";
16
17/// Global PostHog client instance.
18static CLIENT:OnceLock<posthog_rs::Client> = OnceLock::new();
19
20/// Machine-stable distinct ID for the dev session.
21fn DistinctId() -> String {
22	let User = std::env::var("USER")
23		.or_else(|_| std::env::var("USERNAME"))
24		.unwrap_or_else(|_| "unknown".to_string());
25	format!("land-dev-{}", User)
26}
27
28/// Initialize the PostHog client. Call once during app setup.
29/// No-op in release builds.
30pub async fn Initialize() {
31	if !cfg!(debug_assertions) {
32		return;
33	}
34
35	let Options = posthog_rs::ClientOptionsBuilder::default()
36		.api_key(POSTHOG_API_KEY.to_string())
37		.api_endpoint(POSTHOG_HOST.to_string())
38		.build()
39		.expect("PostHog client options");
40
41	let PostHogClient = posthog_rs::client(Options).await;
42	let _ = CLIENT.set(PostHogClient);
43	dev_log!("lifecycle", "[PostHog] Initialized (EU Cloud, debug mode)");
44	CaptureEvent("mountain:session:start", None);
45}
46
47/// Capture a named event with optional properties.
48pub fn CaptureEvent(EventName:&str, Properties:Option<Vec<(&str, &str)>>) {
49	if !cfg!(debug_assertions) {
50		return;
51	}
52
53	let Some(Client) = CLIENT.get() else { return };
54
55	let mut Event = posthog_rs::Event::new(EventName, &DistinctId());
56
57	let _ = Event.insert_prop("$app", "land-editor");
58	let _ = Event.insert_prop("$app_version", "0.0.1");
59	let _ = Event.insert_prop("$build_mode", "debug");
60	let _ = Event.insert_prop("$component", "mountain");
61
62	if let Some(Props) = Properties {
63		for (Key, Value) in Props {
64			let _ = Event.insert_prop(Key, Value);
65		}
66	}
67
68	let _ = Client.capture(Event);
69}
70
71/// Capture an error event.
72pub fn CaptureError(Tag:&str, Message:&str) {
73	if !cfg!(debug_assertions) {
74		return;
75	}
76
77	CaptureEvent("mountain:error", Some(vec![("error_tag", Tag), ("error_message", Message)]));
78}
79
80/// Capture an IPC command invocation.
81pub fn CaptureIPC(Method:&str) {
82	if !cfg!(debug_assertions) {
83		return;
84	}
85
86	CaptureEvent("mountain:ipc:invoke", Some(vec![("method", Method)]));
87}