diff --git a/docs/sdks/index.md b/docs/sdks/index.md index 6683675b..81dab1fb 100644 --- a/docs/sdks/index.md +++ b/docs/sdks/index.md @@ -133,7 +133,7 @@ The read more about our specific SDKs, check out the SDK-specific docs below: - [Java](server-sdks/java) - [Python](server-sdks/python) - [Go](server-sdks/go) -- [Rust](server-sdks/rust) +- [Rust](server-sdks/rust/intro) - [Ruby](server-sdks/ruby/intro) - [.NET](server-sdks/dotnet) - [PHP](server-sdks/php) diff --git a/docs/sdks/server-sdks/index.md b/docs/sdks/server-sdks/index.md index e20b29bb..ea32b54e 100644 --- a/docs/sdks/server-sdks/index.md +++ b/docs/sdks/server-sdks/index.md @@ -19,4 +19,4 @@ Eppo's server-side SDKs may be used to implement flags and run experiments in yo - [Go](/sdks/server-sdks/go) - [Ruby](/sdks/server-sdks/ruby/intro) - [PHP](/sdks/server-sdks/php) -- [Rust](/sdks/server-sdks/rust) +- [Rust](/sdks/server-sdks/rust/intro) diff --git a/docs/sdks/server-sdks/rust.md b/docs/sdks/server-sdks/rust.md deleted file mode 100644 index d1301ab3..00000000 --- a/docs/sdks/server-sdks/rust.md +++ /dev/null @@ -1,121 +0,0 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Rust - -Eppo's Rust SDK can be used for feature flagging and experiment assignment in Rust applications. - -- [GitHub repository](https://github.com/Eppo-exp/eppo-multiplatform) -- [Crate](https://crates.io/crates/eppo) -- [Crate documentation](https://docs.rs/eppo/latest/eppo/) - -### Getting started - -#### Install the SDK - -First, add the SDK as a dependency using Cargo: - -```sh -cargo add eppo -``` - -#### Initialize the SDK - -To initialize the SDK, you will need an SDK key. You can generate one in [the Eppo interface](https://eppo.cloud/feature-flags/keys). - -```rust -let mut client = eppo::ClientConfig::from_api_key("") - .to_client(); - -client.start_poller_thread(); -``` - -This creates a client instance that can be reused throughout the application lifecycle, and starts a poller thread to periodically fetch latest feature flag configuration from Eppo server. - -Flags and other features become available to the SDK after initialization. - -#### Assign variations - -Assign users to flags or experiments using the appropriate `get_*_assignment` function, depending on the type of the flag. For example, to get a string value from a flag, use `get_string_assignment`: - -```rust -let variation = client - .get_string_assignment( - "", - "", - , - ) - .inspect_err(|err| eprintln!("error assigning a flag: {:?}", err)) - .unwrap_or_default() - .unwrap_or("".to_owned()); - -if variation == "fast_checkout" { - // Handle fast checkout -} else { - // Handle other variations -} -``` - -- `` is the key that you chose when creating a flag. You can find it in [the Eppo interface](https://eppo.cloud/feature-flags). -- `` is a value that identifies each entity in your experiment, typically a user ID. -- `` is a `HashMap` of metadata about the subject used for targeting. If targeting rules are based on attributes, they must be passed in on every assignment call. If no attributes are needed, pass an empty `HashMap`. - -That’s it: You can start changing the feature flag on the page and see how it controls your code! - -However, if you want to run experiments, there’s a little extra work to configure it properly. - -### Assignment Logging for Experiments - -If you are using the Eppo SDK for **experiment** assignment (i.e., randomization), we will need to know which subjects were exposed to experiment. For that, you will need to log the assignment information. - -Eppo encourages centralizing application event logging as much as possible. Accordingly, instead of implementing a new event logging framework, Eppo's SDK integrates with your existing logging system via a logging callback function defined at SDK initialization. - -The code below illustrates an example implementation of a logging callback to the console and other event platforms. You could also use your own logging system, the only requirement is that you should implement `AssignmentLogger` trait and pass the logger when initializing the SDK. - -```rust -let mut client = ClientConfig::from_api_key("") - .assignment_logger(MyAssignmentLogger) - .to_client(); - -client.start_poller_thread(); -``` - - - - -```rust -use eppo::AssignmentLogger; - -struct LocalAssignmentLogger; - -impl AssignmentLogger for LocalAssignmentLogger { - fn log_assignment(&self, assignment: AssignmentEvent) { - println!("{:?}", assignment); - } -} -``` - - - - -### Typed Assignment Calls - -The SDK provides typed assignment functions for different types of values: - -- `get_boolean_assignment` -- `get_integer_assignment` -- `get_numeric_assignment` -- `get_string_assignment` -- `get_json_assignment` - -All of these functions have the same input parameters as mentioned in the "Assign variations" section. - -Example usage for a boolean assignment: - -```rust -if client.get_boolean_assignment("", "", SUBJECT_ATTRIBUTES).unwrap_or_default().unwrap_or(false) { - // Handle true assignment -} else { - // Handle false assignment -} -``` diff --git a/docs/sdks/server-sdks/rust/_category_.json b/docs/sdks/server-sdks/rust/_category_.json new file mode 100644 index 00000000..455619a1 --- /dev/null +++ b/docs/sdks/server-sdks/rust/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Rust", + "position": 4 +} \ No newline at end of file diff --git a/docs/sdks/server-sdks/rust/assignments.mdx b/docs/sdks/server-sdks/rust/assignments.mdx new file mode 100644 index 00000000..39b91cd1 --- /dev/null +++ b/docs/sdks/server-sdks/rust/assignments.mdx @@ -0,0 +1,299 @@ +--- +title: Assignments +sidebar_position: 4 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import ApiOptionRef from '@site/src/components/ApiOptionRef'; + +Assignments are the mechanism through which a given [Subject](/sdks/sdk-features/subjects) is assigned to a variation for a feature flag or experiment. + +Currently, the Eppo SDK supports the following assignment types: + +- String +- Boolean +- JSON +- Numeric (Integer and Float) + +## String Assignments + +String assignments return a string value that is set as the variation for the experiment. String flags are the most common type of flags. They are useful for both A/B/n tests and advanced targeting use cases. + +```rust +use std::collections::HashMap; + +let flag_key = "flag-key-123"; +let subject_key = "user-123"; +let default_assignment = "version-a"; +let mut subject_attributes = HashMap::new(); +subject_attributes.insert("country".into(), "US".into()); +subject_attributes.insert("age".into(), 30.into()); +subject_attributes.insert("isReturningUser".into(), true.into()); + +let variant = client + .get_string_assignment( + flag_key, + subject_key, + subject_attributes, + ) + .inspect_err(|err| eprintln!("error assigning a flag: {:?}", err)) + .unwrap_or_default() + .unwrap_or(default_assignment.into()); + +// Use the variant value to determine which path to take +if variant == "version-a" { + handle_version_a(); +} else if variant == "version-b" { + handle_version_b(); +} +``` + +## Boolean Assignments + +Boolean flags support simple on/off toggles. They're useful for simple, binary feature switches like blue/green deployments or enabling/disabling a new feature. + +```rust +use std::collections::HashMap; + +let flag_key = "flag-key-123"; +let subject_key = "user-123"; +let default_assignment = false; +let mut subject_attributes = HashMap::new(); +subject_attributes.insert("country".into(), "US".into()); +subject_attributes.insert("age".into(), 30.into()); +subject_attributes.insert("isReturningUser".into(), true.into()); + +let variant = client + .get_boolean_assignment( + flag_key, + subject_key, + subject_attributes, + ) + .inspect_err(|err| eprintln!("error assigning a flag: {:?}", err)) + .unwrap_or_default() + .unwrap_or(default_assignment); + +if variant { + handle_feature_enabled(); +} else { + handle_feature_disabled(); +} +``` + +## JSON Assignments + +JSON flags work best for advanced configuration use cases. The JSON flag can include structured information such as: + +- The text of marketing copy for a promotional campaign +- The address of a different hero image + +Using this pattern, a team can make minor changes to the copy and design of a website without having to go through an entire code release process. + +```rust +use std::collections::HashMap; +use serde_json::Value; + +let flag_key = "flag-key-123"; +let subject_key = "user-123"; +let default_assignment = serde_json::json!({ + "hero": false, + "heroImage": "placeholder.png", + "heroTitle": "Placeholder Hero Title", + "heroDescription": "Placeholder Hero Description" +}); +let mut subject_attributes = HashMap::new(); +subject_attributes.insert("country".into(), "US".into()); +subject_attributes.insert("age".into(), 30.into()); +subject_attributes.insert("isReturningUser".into(), true.into()); + +let campaign_json = client + .get_json_assignment( + flag_key, + subject_key, + subject_attributes, + ) + .inspect_err(|err| eprintln!("error assigning a flag: {:?}", err)) + .unwrap_or_default() + .unwrap_or(default_assignment); + +if let Some(campaign) = campaign_json.as_object() { + let hero_image = campaign.get("heroImage").and_then(Value::as_str).unwrap_or("default.png"); + let hero_title = campaign.get("heroTitle").and_then(Value::as_str).unwrap_or(""); + let hero_description = campaign.get("heroDescription").and_then(Value::as_str).unwrap_or(""); + + render_hero(hero_image, hero_title, hero_description); +} +``` + +## Integer and Numeric Assignments + +Integer and numeric assignments work the same way but return either an integer or a floating point number. These assignments are useful where you want to use a numeric value to drive business logic such as pricing on an item or a number of items to display in a list. + +```rust +use std::collections::HashMap; + +let flag_key = "flag-key-123"; +let subject_key = "user-123"; +let default_integer = 0; +let default_numeric = 0.0; +let mut subject_attributes = HashMap::new(); +subject_attributes.insert("country".into(), "US".into()); +subject_attributes.insert("age".into(), 30.into()); +subject_attributes.insert("isReturningUser".into(), true.into()); + +// Example of getting an integer assignment +let number_of_items = client + .get_integer_assignment( + flag_key, + subject_key, + subject_attributes.clone(), + ) + .inspect_err(|err| eprintln!("error assigning a flag: {:?}", err)) + .unwrap_or_default() + .unwrap_or(default_integer); + +// Example of getting a numeric assignment +let price = client + .get_numeric_assignment( + flag_key, + subject_key, + subject_attributes, + ) + .inspect_err(|err| eprintln!("error assigning a flag: {:?}", err)) + .unwrap_or_default() + .unwrap_or(default_numeric); + +// Use the assignments to drive business logic +display_items(number_of_items); +set_item_price(price); +``` + +## Assignment Logger Schema + +The SDK will invoke the `log_assignment` function with an `AssignmentEvent` struct that contains the following fields: + + + +The time when the subject was assigned to the variation. Example: `"2021-06-22T17:35:12.000Z"` + + + + +An Eppo feature flag key. Example: `"recommendation-algo"` + + + + +An Eppo allocation key. Example: `"allocation-17"` + + + + +An Eppo experiment key. Example: `"recommendation-algo-allocation-17"` + + + + +An identifier of the subject or user assigned to the experiment variation. Example: UUID + + + + +A free-form map of metadata about the subject. These attributes are only logged if passed to the SDK assignment function. Example: `{ "country": "US" }` + + + + +The experiment variation the subject was assigned to. Example: `"control"` + + + + +Metadata around the assignment, such as the version of the SDK. Example: `{ "sdkLanguage": "rust", "sdkLibVersion": "0.1.0" }` + + +### Logging data to your data warehouse + +Eppo's unique architecture makes it so Eppo never has access to your data. This means that you can use the assignment logging functions to send data to any data warehouse or logging system you want. + +All you need to do is implement the `AssignmentLogger` trait: + + + + +```rust +use eppo::AssignmentLogger; + +struct MyAssignmentLogger; + +impl AssignmentLogger for MyAssignmentLogger { + fn log_assignment(&self, assignment: AssignmentEvent) { + println!("{:?}", assignment); + } +} +``` +:::note +This example writes to your local machine and is useful for development in your local environment. In production, these logs will need to get written to a table in your data warehouse. +::: + + + + + +```rust +use eppo::AssignmentLogger; + +struct MyCustomLogger; + +impl AssignmentLogger for MyCustomLogger { + fn log_assignment(&self, assignment: AssignmentEvent) { + // Implement your custom logging logic here + // For example, sending to your data warehouse + send_to_data_warehouse(assignment); + } +} + +fn send_to_data_warehouse(assignment: AssignmentEvent) { + // Your implementation here +} +``` + + + + +:::note +The Rust SDK currently has fewer built-in integrations compared to other SDKs. You'll need to implement custom logging logic for specific data warehouses or analytics platforms. +::: diff --git a/docs/sdks/server-sdks/rust/initialization.mdx b/docs/sdks/server-sdks/rust/initialization.mdx new file mode 100644 index 00000000..73ba21a2 --- /dev/null +++ b/docs/sdks/server-sdks/rust/initialization.mdx @@ -0,0 +1,62 @@ +--- +title: Initialization +sidebar_position: 3 +--- + +import ApiOptionRef from '@site/src/components/ApiOptionRef'; + +The Eppo Rust SDK is easy to initialize while offering robust customization options, making it adaptable to various use cases such as offline mode, custom caching requirements, and ultra-low-latency initialization. + +## Initialize the SDK + +To complete basic initialization, you only need to provide an SDK key. [Create an SDK key](/sdks/sdk-keys) if you don't already have one. + +```rust +let mut client = eppo::ClientConfig::from_api_key("") + .to_client(); + +let poller_thread = client.start_poller_thread(); +``` + +## Advanced Configuration + +Basic initialization is great for most use cases, but the SDK provides options that you can use during initialization to customize the behavior of the SDK. + +### Initialization Options + +The SDK configuration can be customized using the `ClientConfig` builder pattern: + + + +The base URL for the Eppo API. + + + + +A callback that sends each assignment to your data warehouse. Required only for experiment analysis. + + +Example using these configuration options: + +```rust +use std::time::Duration; + +let mut client = eppo::ClientConfig::from_api_key("") + .base_url("https://custom-domain.example.com/api") + .assignment_logger(MyAssignmentLogger) + .to_client(); + +client.start_poller_thread(); +``` + +:::note +The Rust SDK is still evolving and some features available in other SDKs (like persistent caching and offline initialization) are not yet implemented. The documentation will be updated as these features become available. +::: diff --git a/docs/sdks/server-sdks/rust/intro.mdx b/docs/sdks/server-sdks/rust/intro.mdx new file mode 100644 index 00000000..c230247e --- /dev/null +++ b/docs/sdks/server-sdks/rust/intro.mdx @@ -0,0 +1,29 @@ +--- +title: SDK Guide +sidebar_position: 1 +--- +import FeatureCard from '/src/components/FeatureCard'; + +The Eppo Rust SDK allows you to manage feature flags and experiments in your Rust applications. + +
+ + + + +
\ No newline at end of file diff --git a/docs/sdks/server-sdks/rust/quickstart.mdx b/docs/sdks/server-sdks/rust/quickstart.mdx new file mode 100644 index 00000000..4ee6724d --- /dev/null +++ b/docs/sdks/server-sdks/rust/quickstart.mdx @@ -0,0 +1,105 @@ +--- +title: Quickstart +sidebar_position: 2 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +The Eppo Rust SDK enables feature flags and experiments in your Rust applications with only a few lines of code. + +The SDK handles all the complexity of feature flag evaluation and experiment assignment locally in your application, with no network calls required after initial setup. + +## Installation + +First, add the SDK as a dependency using Cargo: + +```sh +cargo add eppo +``` + +## Feature Flags + +Feature flags are a way to toggle features on and off without needing to deploy code. + +### Initialize the SDK + +[Create an SDK key](/sdks/sdk-keys) if you don't already have one. + +```rust +let mut client = eppo::ClientConfig::from_api_key("") + .to_client(); + +let poller_thread = client.start_poller_thread(); +``` + +### Assign a variant + +Once initialized, you can use the client to make assignments. Each assignment requires: + +- **Flag Key**: Identifies which set of configuration rules to use +- **Subject Key**: A unique identifier for the subject (usually a user ID) +- **Subject Attributes**: Optional key-value pairs containing additional information used for rule evaluation + +```rust +let variation = client + .get_string_assignment( + "", + "", + , + ) + .inspect_err(|err| eprintln!("error assigning a flag: {:?}", err)) + .unwrap_or_default() + .unwrap_or("".to_owned()); + +if variation == "fast_checkout" { + // Handle fast checkout +} else { + // Handle other variations +} +``` + +### Assignment Types + +The SDK provides different assignment functions based on the type of value you need: + +| Function | Return Type | +|----------|-------------| +| `get_string_assignment()` | `Str` | +| `get_boolean_assignment()` | `bool` | +| `get_json_assignment()` | `serde_json::Value` | +| `get_integer_assignment()` | `i64` | +| `get_numeric_assignment()` | `f64` | + +## Experiments + +For experiments, you'll need to implement assignment logging to track how users interact with different variations. + +```rust +use eppo::AssignmentLogger; + +struct MyAssignmentLogger; + +impl AssignmentLogger for MyAssignmentLogger { + fn log_assignment(&self, assignment: AssignmentEvent) { + println!("{:?}", assignment); + } +} + +let mut client = ClientConfig::from_api_key("") + .assignment_logger(MyAssignmentLogger) + .to_client(); + +let poller_thread = client.start_poller_thread(); +``` + +:::note +In a production application, you would want to replace the `println!()` statement with an actual logging system. +::: + +## Next Steps + +Now that you've seen how to make assignments with the Eppo Rust SDK, we strongly recommend familiarizing yourself with the following topics: + +- [Initialization Configuration](/sdks/server-sdks/rust/initialization) +- [Assignment details](/sdks/server-sdks/rust/assignments) diff --git a/static/_redirects b/static/_redirects index 37aadb65..046337af 100644 --- a/static/_redirects +++ b/static/_redirects @@ -140,3 +140,12 @@ http://adoring-yonath-6ecb9d.netlify.app/* http://docs.geteppo.com/:splat 301! /guides/engineering/nextjs-setup/ /sdks/client-sdks/javascript/nextjs-setup /sdks/server-sdks/ruby/ /sdks/server-sdks/ruby/intro + +/sdks/server-sdks/rust /sdks/server-sdks/rust/intro 301! +/sdks/server-sdks/rust#getting-started /sdks/server-sdks/rust/quickstart +/sdks/server-sdks/rust#install-the-sdk /sdks/server-sdks/rust/quickstart#installation +/sdks/server-sdks/rust#initialize-the-sdk /sdks/server-sdks/rust/initialization +/sdks/server-sdks/rust#assign-variations /sdks/server-sdks/rust/assignments +/sdks/server-sdks/rust#assignment-logging-for-experiments /sdks/server-sdks/rust/assignments#assignment-logger-schema +/sdks/server-sdks/rust#typed-assignment-calls /sdks/server-sdks/rust/assignments#assignment-types +