edgee_sdk/
lib.rs

1use serde::{Deserialize, Serialize};
2
3pub mod token;
4
5#[derive(Debug, Serialize, Deserialize, Clone)]
6pub struct Autocapture {
7    #[serde(default)]
8    pub pageview: bool,
9    #[serde(default)]
10    pub spa_pageview: bool,
11    #[serde(default)]
12    pub engagement: bool,
13    #[serde(default)]
14    pub click: bool,
15    #[serde(default)]
16    pub form: bool,
17    #[serde(default)]
18    pub scroll: bool,
19}
20
21impl Default for Autocapture {
22    fn default() -> Self {
23        Self {
24            pageview: true,
25            spa_pageview: false,
26            engagement: false,
27            click: false,
28            form: false,
29            scroll: false,
30        }
31    }
32}
33
34/// Retrieves and customizes the SDK content.
35///
36/// This function gets the SDK content based on the URL and customizes it for the given host and autocapture settings.
37/// For URLs ending in "sdk.js", it returns the latest version. Otherwise, it extracts and returns a specific version.
38///
39/// # Arguments
40///
41/// * `url` - A string slice containing the URL path to get the SDK from (e.g. "/_edgee/sdk.js" or "/_edgee/edgee.v1.5.0.js")
42/// * `host` - A string slice containing the host domain to customize the SDK for
43/// * `autocapture` - An Autocapture struct specifying which events to automatically capture
44/// * `edgee_cookie` - A string slice containing the name of the Edgee cookie
45///
46/// # Returns
47///
48/// * `Ok(String)` - The customized SDK content if successful
49/// * `Err(&'static str)` - An error message if the SDK content could not be retrieved
50///
51/// # Example
52///
53/// ```rust
54/// use edgee_sdk::Autocapture;
55/// let url = "/_edgee/edgee.v1.7.3.js";
56/// let host = "example.com";
57/// let sdk_content = edgee_sdk::get_sdk(url, host, Autocapture::default(), "edgee");
58/// assert!(sdk_content.is_ok());
59/// ```
60pub fn get_sdk(
61    url: &str,
62    host: &str,
63    autocapture: Autocapture,
64    edgee_cookie: &str,
65) -> Result<String, &'static str> {
66    let content = get_sdk_content(url)?;
67    Ok(dynamize_sdk(content, host, autocapture, edgee_cookie))
68}
69
70pub fn get_sdk_for_unsafe_user(url: &str) -> Result<String, &'static str> {
71    let content = get_sdk_content(url)?;
72    Ok(content.replace("{{safe}}", "unsafe"))
73}
74
75fn get_sdk_content(url: &str) -> Result<&str, &'static str> {
76    if url.ends_with("sdk.js") {
77        return Ok(include_str!("../release/edgee.v1.7.3.js").trim());
78    }
79
80    let Some((_, part)) = url.rsplit_once("edgee.v") else {
81        return Err("Failed to read the JS SDK file");
82    };
83    let Some(part) = part.strip_suffix(".js") else {
84        return Err("Failed to read the JS SDK file");
85    };
86
87    let content = match part {
88        "1.5.0" => include_str!("../release/edgee.v1.5.0.js").trim(),
89        "1.6.2" => include_str!("../release/edgee.v1.6.2.js").trim(),
90        "1.7.3" => include_str!("../release/edgee.v1.7.3.js").trim(),
91        // Add more versions as needed
92        _ => return Err("Failed to read the JS SDK file"),
93    };
94
95    Ok(content)
96}
97
98fn dynamize_sdk(sdk: &str, host: &str, autocapture: Autocapture, edgee_cookie: &str) -> String {
99    let token = token::generate(host);
100    let mut sdk = sdk.replace("{{token}}", token.as_str());
101    sdk = sdk.replace("{{ecn}}", edgee_cookie);
102
103    if !autocapture.pageview {
104        sdk = sdk.replace("{{ac_pageview}}", "n");
105    }
106    if autocapture.spa_pageview {
107        sdk = sdk.replace("{{ac_spa_pageview}}", "y");
108    }
109    if autocapture.engagement {
110        sdk = sdk.replace("{{ac_engagement}}", "y");
111    }
112    if autocapture.click {
113        sdk = sdk.replace("{{ac_click}}", "y");
114    }
115    if autocapture.form {
116        sdk = sdk.replace("{{ac_form}}", "y");
117    }
118    if autocapture.scroll {
119        sdk = sdk.replace("{{ac_scroll}}", "y");
120    }
121    sdk.to_string()
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn retrieves_sdk_content_for_valid_url() {
130        let url = "/sdk.js";
131        let host = "example.com";
132        let result = get_sdk(url, host, Autocapture::default(), "edgee");
133        assert!(result.is_ok());
134        assert!(result.unwrap().contains("{{side}}"));
135    }
136
137    #[test]
138    fn retrieves_sdk_with_custom_edgee_cookie_name() {
139        let url = "/sdk.js";
140        let host = "example.com";
141        let result = get_sdk(url, host, Autocapture::default(), "test_edgee_cookie_name");
142        assert!(result.is_ok());
143        assert!(result.unwrap().contains("test_edgee_cookie_name"));
144    }
145
146    #[test]
147    fn retrieves_versioned_sdk_content() {
148        let url = "/edgee.v1.5.0.js";
149        let host = "example.com";
150        let result = get_sdk(url, host, Autocapture::default(), "edgee");
151        assert!(result.is_ok());
152        assert!(result.unwrap().contains("{{side}}"));
153    }
154    #[test]
155    fn returns_error_for_invalid_url_format() {
156        let url = "/invalid.js";
157        let host = "example.com";
158        let result = get_sdk(url, host, Autocapture::default(), "edgee");
159        assert!(result.is_err());
160        assert_eq!(result.unwrap_err(), "Failed to read the JS SDK file");
161    }
162
163    #[test]
164    fn returns_error_for_unsupported_version() {
165        let url = "/edgee.v2.0.0.js";
166        let host = "example.com";
167        let result = get_sdk(url, host, Autocapture::default(), "edgee");
168        assert!(result.is_err());
169        assert_eq!(result.unwrap_err(), "Failed to read the JS SDK file");
170    }
171}