1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use crate::core::{Signal, TelemetryDeck};
use reqwasm::http::Request;
use std::collections::HashMap;
use wasm_bindgen_futures::spawn_local;
impl TelemetryDeck {
/// Send a telemetry signal (fire-and-forget)
///
/// This method spawns an async task using `spawn_local` and never returns errors.
/// The signal is sent in the background without blocking. Use [`send_sync`](Self::send_sync)
/// if you need error handling.
///
/// # Parameters
///
/// * `signal_type` - The type/name of the signal (e.g., "userLogin", "buttonClick")
/// * `client_user` - Optional user identifier. Will be SHA-256 hashed automatically.
/// If `None`, defaults to "rust".
/// * `payload` - Optional key-value parameters to attach to the signal
/// * `is_test_mode` - Whether to mark this as a test signal. Defaults to `false` if `None`.
/// * `float_value` - Optional floating-point value (useful for metrics like revenue, duration, etc.)
///
/// # Examples
///
/// ```no_run
/// use telemetrydeck_wasm::TelemetryDeck;
///
/// let client = TelemetryDeck::new("YOUR-APP-ID");
///
/// // Simple signal
/// client.send("buttonClick", None, None, None, None);
///
/// // With user
/// client.send("userLogin", Some("user@example.com"), None, None, None);
///
/// // With float value for revenue tracking
/// client.send("purchase", Some("user123"), None, None, Some(49.99));
/// ```
///
/// ```no_run
/// use telemetrydeck_wasm::TelemetryDeck;
/// use std::collections::HashMap;
///
/// let client = TelemetryDeck::new("YOUR-APP-ID");
///
/// let mut params = HashMap::new();
/// params.insert("screen".to_string(), "settings".to_string());
/// params.insert("action".to_string(), "toggle".to_string());
///
/// client.send("userAction", Some("user"), Some(params), None, None);
/// ```
///
/// # Platform Note
///
/// On WASM, this uses the browser's event loop via `spawn_local`.
pub fn send(
&self,
signal_type: &str,
client_user: Option<&str>,
payload: Option<HashMap<String, String>>,
is_test_mode: Option<bool>,
float_value: Option<f64>,
) {
let signal =
self.create_signal(signal_type, client_user, payload, is_test_mode, float_value);
self.send_one(signal);
}
/// Send a telemetry signal and return errors if any occur
///
/// This method waits for the HTTP request to complete and returns a `Result`.
/// Use this when you need to know if the signal was sent successfully.
///
/// # Parameters
///
/// * `signal_type` - The type/name of the signal (e.g., "userLogin", "buttonClick")
/// * `client_user` - Optional user identifier. Will be SHA-256 hashed automatically.
/// If `None`, defaults to "rust".
/// * `payload` - Optional key-value parameters to attach to the signal
/// * `is_test_mode` - Whether to mark this as a test signal. Defaults to `false` if `None`.
/// * `float_value` - Optional floating-point value (useful for metrics like revenue, duration, etc.)
///
/// # Returns
///
/// * `Ok(())` if the signal was sent successfully (HTTP 2xx status)
/// * `Err(...)` if sending failed (network error, HTTP error, serialization error, etc.)
///
/// # Examples
///
/// ```no_run
/// use telemetrydeck_wasm::TelemetryDeck;
///
/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let client = TelemetryDeck::new("YOUR-APP-ID");
///
/// // Handle errors explicitly
/// match client.send_sync("criticalEvent", Some("user"), None, None, None).await {
/// Ok(()) => println!("Signal sent successfully"),
/// Err(e) => eprintln!("Failed to send: {}", e),
/// }
///
/// // Or use ? operator
/// client.send_sync("anotherEvent", None, None, None, None).await?;
/// # Ok(())
/// # }
/// ```
pub async fn send_sync(
&self,
signal_type: &str,
client_user: Option<&str>,
payload: Option<HashMap<String, String>>,
is_test_mode: Option<bool>,
float_value: Option<f64>,
) -> Result<(), Box<dyn std::error::Error>> {
let signal =
self.create_signal(signal_type, client_user, payload, is_test_mode, float_value);
self.send_many_sync(vec![signal]).await
}
fn send_one(&self, signal: Signal) {
self.send_many(vec![signal])
}
fn send_many(&self, signals: Vec<Signal>) {
let url = self.build_url();
spawn_local(async move {
let body = serde_json::to_string(&signals).unwrap();
let _resp = Request::post(&url)
.body(body)
.header("Content-Type", "application/json")
.send()
.await;
});
}
async fn send_many_sync(&self, signals: Vec<Signal>) -> Result<(), Box<dyn std::error::Error>> {
let url = self.build_url();
let body = serde_json::to_string(&signals)?;
let resp = Request::post(&url)
.body(body)
.header("Content-Type", "application/json")
.send()
.await?;
if resp.ok() {
Ok(())
} else {
Err(format!("HTTP error: {}", resp.status()).into())
}
}
}