Packages

statsig_elixir

0.10.2
0.20.3-beta.2607250300 0.20.3-beta.2607240300 0.20.2 0.20.2-rc.2607201629 0.20.2-beta.2607230301 0.20.2-beta.2607220301 0.20.2-beta.2607180300 0.20.2-beta.2607170300 0.20.1 0.20.1-rc.2607140134 0.20.1-rc.2607132133 0.20.1-beta.2607150300 0.20.0 0.19.9-rc.2607090505 0.19.9-rc.2607010924 0.19.9-beta.2607110300 0.19.9-beta.2607100302 0.19.9-beta.2607090302 0.19.9-beta.2607080301 0.19.9-beta.2607020303 0.19.9-beta.2607010304 0.19.9-beta.2606260304 0.19.8 0.19.7-rc.2606232103 0.19.7-beta.2606240303 0.19.7-beta.2606190306 0.19.7-beta.2606180304 0.19.6 0.19.6-rc.2606161624 0.19.6-rc.2606151903 0.19.6-beta.2606170305 0.19.6-beta.2606100304 0.19.5 0.19.5-rc.2606050349 0.19.5-beta.2606060303 0.19.4 0.19.4-rc.2605212136 0.19.3 0.19.3-beta.2604211739 0.19.2 0.19.2-rc.2604140138 0.19.2-beta.2604150312 0.19.1 0.19.1-rc.2604121707 0.19.1-beta.2604130314 0.19.1-beta.2604110309 0.19.1-beta.2604100313 0.19.0 0.18.2-rc.2604080018 0.18.2-beta.2604080312 0.18.1 0.18.1-rc.2604070138 0.18.1-beta.2604072116 0.18.0 0.17.3-rc.2603272156 0.17.3-beta.2603290312 0.17.3-beta.2603280308 0.17.3-beta.2603250310 0.17.2 0.17.2-rc.2603240138 0.17.2-beta.2603241852 0.17.1 0.17.1-rc.2603201903 0.17.1-rc.2603192323 0.17.1-beta.2603210301 0.17.1-beta.2603200305 0.17.1-beta.2603181954 0.17.0 0.16.6-rc.2603180529 0.16.6-rc.2603170138 0.16.6-beta.2603181807 0.16.5 0.16.5-rc.2603131810 0.16.5-beta.2603130304 0.16.4 0.16.4-rc.2603092140 0.16.4-beta.2603110302 0.16.4-beta.2603060304 0.16.4-beta.2603051742 0.16.4-beta.2603040814 0.16.4-beta.2603040303 0.16.3 0.16.3-rc.2602271954 0.16.3-rc.2602270324 0.16.3-rc.2602240138 0.16.3-beta.2603040747 0.16.3-beta.2603021916 0.16.3-beta.2602280254 0.16.3-beta.2602262056 0.16.3-beta.2602250307 0.16.3-beta.2602240529 0.16.2 0.16.2-rc.2602202152 0.16.2-rc.2602200042 0.16.2-beta.2602210300 0.16.2-beta.2602200305 0.16.1 0.16.1-rc.2602180406 0.16.1-beta.2602190307 0.16.0 0.15.2-rc.2602172254 0.15.2-rc.2602140111 0.15.2-rc.2602130016 0.15.2-rc.2602100139 0.15.2-rc.2602030138 0.15.2-beta.2602180309 0.15.2-beta.2602150309 0.15.2-beta.2602140304 0.15.2-beta.2602130310 0.15.2-beta.2602122245 0.15.2-beta.2602122035 0.15.2-beta.2602120040 0.15.2-beta.2602110310 0.15.2-beta.2602050305 0.15.2-beta.2602040304 0.15.2-beta.2602031515 0.15.1 0.15.1-rc.2601270136 0.15.1-beta.2601310133 0.15.1-beta.2601301825 0.15.1-beta.2601300156 0.15.1-beta.2601292313 0.15.1-beta.2601280249 0.15.0 0.14.2-rc.2601230113 0.14.2-rc.2601200136 0.14.2-rc.2601130135 0.14.2-rc.2601062017 0.14.2-rc.2512230135 0.14.2-beta.2601262335 0.14.2-beta.2601240243 0.14.2-beta.2601230247 0.14.2-beta.2601220251 0.14.2-beta.2601210246 0.14.2-beta.2601170240 0.14.2-beta.2601160247 0.14.2-beta.2601140250 0.14.2-beta.2601130116 0.14.2-beta.2601100241 0.14.2-beta.2601070246 0.14.2-beta.2512310244 0.14.2-beta.2512240241 0.14.1 0.14.1-rc.2512190311 0.14.1-rc.2512162207 0.14.1-rc.2512160223 0.14.1-beta.2512200239 0.14.1-beta.2512190242 0.14.1-beta.2512170241 0.14.0 0.13.1-beta.2512140246 0.13.1-beta.2512130201 0.13.1-beta.2512120242 0.13.1-beta.2512110242 0.13.1-beta.2512110007 0.13.1-beta.2512102250 0.13.1-beta.2512102139 0.13.1-beta.2512100241 0.13.1-beta.2512051931 0.13.1-beta.2512050240 0.12.1 0.12.1-rc.2511121816 0.12.1-rc.2511110014 0.12.1-beta.2511110238 0.12.0 0.11.2-rc.2511040014 0.11.2-beta.2511050238 0.11.2-beta.2511032240 0.11.2-beta.2511032212 0.11.2-beta.2511030239 0.11.2-beta.2510310237 0.11.1 0.11.1-rc.2510292219 0.11.1-rc.2510280013 0.11.1-beta.2510300237 0.11.1-beta.2510290239 0.11.0 0.10.3-rc.2510210409 0.10.3-rc.2510210014 0.10.3-rc.6 0.10.3-beta.2510250234 0.10.3-beta.2510240234 0.10.3-beta.2510230235 0.10.3-beta.2510220237 0.10.2 0.10.2-rc.2510141845 0.10.2-beta.2510180231 0.10.2-beta.2510172041 0.10.2-beta.2510170235 0.10.2-beta.2510160235 0.10.2-beta.2510152259 0.10.2-beta.2510152125 0.10.2-beta.2510151849 0.10.2-beta.2510150236 0.10.2-beta.2510142155 0.10.2-beta.2510142023 0.10.1 0.10.0 0.10.0-rc.2510090432 0.10.0-rc.2510070136 0.10.0-beta.2510120235 0.10.0-beta.2510102126 0.10.0-beta.2510100234 0.10.0-beta.2510090234 0.10.0-beta.2510072144 0.9.6-beta.2510020232 0.9.5-rc.2510030411 0.9.5-rc.2510020138 0.9.5-beta.2510010238 0.9.4-rc.2509300113 0.9.4-beta.2509252256 0.9.3 0.9.3-beta.2509250234 0.9.3-beta.2509240232 0.9.3-beta.2509231844 0.9.2-rc.2509221315 0.9.2-beta.2509190233 0.9.2-beta.2509180233 0.9.2-beta.2509180231 0.9.2-beta.2509170231 0.9.1 0.9.1-rc.2509190115 0.9.1-rc.2509181925 0.9.1-rc.2509171909 0.9.0-rc.1 0.8.9-beta.2509161804 0.8.9-beta.2509160231 0.8.8-beta.2509130225 0.8.8-beta.2509112152 0.8.8-beta.2509112049 0.8.8-beta.2509110233 0.8.7 0.8.7-rc.2509102057 0.8.7-rc.2509102017 0.8.7-rc.2509100003 0.8.7-rc.2509092342 0.8.7-beta.2509092208 0.8.6 0.8.6-rc.2509082013 0.8.6-beta.2509040231 0.8.6-beta.2509020235 0.8.5 0.8.5-beta.2508300230 0.8.4 0.8.3 0.8.3-beta.2508280233 0.8.2 0.8.2-rc.2508282224 0.8.1 0.8.1-rc.2508271913 0.8.1-rc.2508262341 0.8.1-rc.2508251314 0.8.0 0.7.4-rc.2508222145 0.7.4-rc.2508220107 0.7.4-rc.2508220104 0.7.4-rc.2508220101 0.7.4-rc.2508220057 0.7.4-rc.2508220049 0.7.4-rc.2508220046 0.7.4-rc.2508220041 0.7.4-rc.2508220033 0.7.4-rc.2508220032 0.7.4-rc.2508220027 0.7.4-rc.2508220022 0.7.4-rc.2508220017 0.7.4-rc.2508220010 0.7.4-rc.2508220006 0.7.4-rc.2508220000 0.7.4-rc.2508212357 0.7.4-rc.2508212349 0.7.4-rc.2508202203 0.7.4-rc.2508202034 0.7.4-rc.2508191829 0.7.4-beta.2508220235 0.7.4-beta.2508210235 0.7.4-beta.2508200235 0.7.4-beta.2508190236 0.7.4-beta.2508160236 0.7.4-beta.2508150240 0.0.6-beta.7 0.0.6-beta.6 0.0.2 retired 0.0.1 retired

A performant elixir SDK for Statsig feature gates and experiments using Rustler

Current section

Files

Jump to
statsig_elixir native statsig_elixir src statsig_nfi.rs
Raw

native/statsig_elixir/src/statsig_nfi.rs

use rustler::{Env, Error, ResourceArc, Term};
use statsig_rust::{
statsig_metadata::StatsigMetadata, statsig_types::Layer as LayerActual, Statsig,
};
use std::{
collections::HashMap,
sync::{Arc, RwLock},
};
use crate::{
statsig_options_nfi::StatsigOptions,
statsig_types_nfi::{
AllowedPrimitive, ClientInitResponseOptions, DynamicConfig, DynamicConfigEvaluationOptions,
Experiment, ExperimentEvaluationOptions, FeatureGate, FeatureGateEvaluationOptions,
LayerEvaluationOptions,
},
statsig_user_nfi::StatsigUser,
};
use serde_json::Value;
struct StatsigResource {
pub statsig_core: RwLock<Arc<Statsig>>,
}
#[allow(non_local_definitions)]
fn load(env: Env, _: Term) -> bool {
_ = rustler::resource!(StatsigResource, env);
_ = rustler::resource!(LayerResource, env);
true
}
pub struct LayerResource {
pub core: RwLock<Arc<LayerActual>>,
}
impl LayerResource {
pub fn new(layer: LayerActual) -> Self {
LayerResource {
core: RwLock::new(Arc::new(layer)),
}
}
}
#[rustler::nif(schedule = "DirtyCpu")]
pub fn new(
sdk_key: String,
options: Option<StatsigOptions>,
system_metadata: HashMap<String, String>,
) -> Result<ResourceArc<StatsigResource>, Error> {
update_metadata(system_metadata);
let statsig = Statsig::new(&sdk_key, options.map(|op| Arc::new(op.into())));
Ok(ResourceArc::new(StatsigResource {
statsig_core: RwLock::new(Arc::new(statsig)),
}))
}
#[rustler::nif]
pub fn initialize(statsig: ResourceArc<StatsigResource>) -> Result<(), Error> {
match statsig.statsig_core.read() {
Ok(read) => {
let statsig: Arc<Statsig> = Arc::clone(&read);
let rt_handle = match statsig.statsig_runtime.get_handle() {
Ok(handle) => handle,
Err(_) => return Err(Error::RaiseAtom("Failed to get Statsig")),
};
match rt_handle.block_on(statsig.initialize()) {
Ok(_) => Ok(()),
Err(_) => Err(Error::RaiseAtom("failed to init")),
}
}
Err(_) => Err(Error::RaiseAtom("failed to init")),
}
}
#[rustler::nif]
pub fn get_feature_gate(
statsig: ResourceArc<StatsigResource>,
gate_name: &str,
statsig_user: StatsigUser,
options: Option<FeatureGateEvaluationOptions>,
) -> Result<FeatureGate, Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => Ok(read_guard
.get_feature_gate_with_options(
&statsig_user.into(),
gate_name,
options.map(|o| o.into()).unwrap_or_default(),
)
.into()),
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif]
pub fn check_gate(
statsig: ResourceArc<StatsigResource>,
gate_name: &str,
statsig_user: StatsigUser,
options: Option<FeatureGateEvaluationOptions>,
) -> Result<bool, Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => Ok(read_guard.check_gate_with_options(
&statsig_user.into(),
gate_name,
options.map(|o| o.into()).unwrap_or_default(),
)),
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif]
pub fn get_dynamic_config(
statsig: ResourceArc<StatsigResource>,
config_name: &str,
statsig_user: StatsigUser,
options: Option<DynamicConfigEvaluationOptions>,
) -> Result<DynamicConfig, Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => Ok(read_guard
.get_dynamic_config_with_options(
&statsig_user.into(),
config_name,
options.map(|o| o.into()).unwrap_or_default(),
)
.into()),
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif]
pub fn get_experiment(
statsig: ResourceArc<StatsigResource>,
experiment_name: &str,
statsig_user: StatsigUser,
options: Option<ExperimentEvaluationOptions>,
) -> Result<Experiment, Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => Ok(read_guard
.get_experiment_with_options(
&statsig_user.into(),
experiment_name,
options.map(|o| o.into()).unwrap_or_default(),
)
.into()),
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif]
pub fn get_layer(
statsig: ResourceArc<StatsigResource>,
layer_name: &str,
statsig_user: StatsigUser,
options: Option<LayerEvaluationOptions>,
) -> Result<ResourceArc<LayerResource>, Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => {
let layer = read_guard.get_layer_with_options(
&statsig_user.into(),
layer_name,
options.map(|o| o.into()).unwrap_or_default(),
);
Ok(ResourceArc::new(LayerResource::new(layer)))
}
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif]
pub fn get_prompt(
statsig: ResourceArc<StatsigResource>,
prompt_name: &str,
statsig_user: StatsigUser,
options: Option<LayerEvaluationOptions>,
) -> Result<ResourceArc<LayerResource>, Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => {
let layer = read_guard.get_prompt_with_options(
&statsig_user.into(),
prompt_name,
options.map(|o| o.into()).unwrap_or_default(),
);
Ok(ResourceArc::new(LayerResource::new(layer)))
}
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif]
pub fn log_event(
statsig: ResourceArc<StatsigResource>,
statsig_user: StatsigUser,
event_name: &str,
value: Option<&str>,
metadata: Option<HashMap<String, String>>,
) -> Result<(), Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => {
read_guard.log_event(
&statsig_user.into(),
event_name,
value.map(|v| v.to_string()),
metadata,
);
Ok(())
}
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif]
pub fn log_event_with_number(
statsig: ResourceArc<StatsigResource>,
statsig_user: StatsigUser,
event_name: &str,
value: Option<f64>,
metadata: Option<HashMap<String, String>>,
) -> Result<(), Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => {
read_guard.log_event_with_number(&statsig_user.into(), event_name, value, metadata);
Ok(())
}
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif(schedule = "DirtyCpu")]
pub fn get_client_init_response_as_string(
statsig: ResourceArc<StatsigResource>,
statsig_user: StatsigUser,
options: Option<ClientInitResponseOptions>,
) -> Result<String, Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => {
let response = match options {
Some(o) => read_guard.get_client_init_response_with_options_as_string(
&statsig_user.into(),
&o.into(),
),
None => read_guard.get_client_init_response_as_string(&statsig_user.into()),
};
Ok(response)
}
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif(schedule = "DirtyIo")]
pub fn flush(statsig: ResourceArc<StatsigResource>) -> Result<(), Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => {
let statsig: Arc<Statsig> = Arc::clone(&read_guard);
let rt_handle = match statsig.statsig_runtime.get_handle() {
Ok(handle) => handle,
Err(_) => return Err(Error::RaiseAtom("Failed to get Statsig")),
};
rt_handle.block_on(async move { statsig.flush_events().await });
Ok(())
}
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif]
pub fn shutdown(statsig: ResourceArc<StatsigResource>) -> Result<(), Error> {
match statsig.statsig_core.read() {
Ok(read_guard) => {
let statsig: Arc<Statsig> = Arc::clone(&read_guard);
let rt_handle = match statsig.statsig_runtime.get_handle() {
Ok(handle) => handle,
Err(_) => return Err(Error::RaiseAtom("Failed to get Statsig")),
};
rt_handle.block_on(async move {
match statsig.shutdown().await {
Ok(_) => Ok(()),
Err(_) => Err(Error::RaiseAtom("Failed to shutdown")),
}
})
}
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
// Layer NFI
#[rustler::nif]
pub fn layer_get(
layer: ResourceArc<LayerResource>,
param_name: &str,
default: AllowedPrimitive,
) -> Result<AllowedPrimitive, Error> {
match layer.core.read() {
Ok(layer) => match default.clone() {
AllowedPrimitive::Int(i) => Ok(AllowedPrimitive::Int(layer.get_i64(param_name, i))),
AllowedPrimitive::Float(f) => Ok(AllowedPrimitive::Float(layer.get_f64(param_name, f))),
AllowedPrimitive::Bool(b) => Ok(AllowedPrimitive::Bool(layer.get_bool(param_name, b))),
AllowedPrimitive::Str(_) => match layer.get_raw_value(param_name) {
Some(json_value) => {
let res = match json_value {
Value::String(s) => s.to_owned(),
_ => json_value.to_string(),
};
Ok(AllowedPrimitive::Str(res))
}
None => Ok(default),
},
},
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif]
pub fn layer_get_rule_id(layer: ResourceArc<LayerResource>) -> Result<String, Error> {
match layer.core.read() {
Ok(read_guard) => Ok(read_guard.rule_id.clone()),
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
#[rustler::nif]
pub fn layer_get_group_name(layer: ResourceArc<LayerResource>) -> Result<Option<String>, Error> {
println!("get group name");
match layer.core.read() {
Ok(read_guard) => Ok(read_guard.group_name.clone()),
Err(_) => Err(Error::RaiseAtom("Failed to get Statsig")),
}
}
// Util Functions
fn update_metadata(system_metadata: HashMap<String, String>) {
let unknown = "unknown".to_string();
let os = system_metadata.get("os").unwrap_or(&unknown);
let arch = system_metadata.get("arch").unwrap_or(&unknown);
let language_version = system_metadata.get("language_version").unwrap_or(&unknown);
StatsigMetadata::update_values(
"statsig-server-core-elixir".to_owned(),
os.to_string(),
arch.to_string(),
language_version.to_string(),
);
}
rustler::init!("Elixir.Statsig.NativeBindings", load = load);