Skip to content

Commit 8c9c747

Browse files
authored
fix(deps): update datalogic_rs to 3.0.x latest version (#39)
Signed-off-by: Harishankar Narayanan <[email protected]>
1 parent eacf2bd commit 8c9c747

File tree

5 files changed

+271
-314
lines changed

5 files changed

+271
-314
lines changed

crates/flagd/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,4 @@ murmurhash3 = "0.0.5"
5757
tower = "0.5"
5858
hyper-util = { version = "0.1", features = ["tokio"] }
5959
thiserror = "2.0"
60-
datalogic-rs = "2.1.15"
60+
datalogic-rs = "3.0.22"
Lines changed: 101 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,119 @@
1-
use anyhow::Result;
1+
use datalogic_rs::DataValue;
22
use murmurhash3::murmurhash3_x86_32;
3-
use serde_json::Value;
4-
use std::collections::HashMap;
53
use tracing::debug;
64

7-
pub struct Fractional;
5+
// Function implementation for the SimpleOperatorFn approach
6+
pub fn fractional_fn<'r>(
7+
args: Vec<DataValue<'r>>,
8+
data: DataValue<'r>,
9+
) -> std::result::Result<DataValue<'r>, String> {
10+
if args.is_empty() {
11+
debug!("No arguments provided for fractional targeting.");
12+
return Ok(DataValue::Null);
13+
}
814

9-
impl Fractional {
10-
pub fn evaluate(args: &[Value], data: &HashMap<String, Value>) -> Result<Value> {
11-
if args.is_empty() {
12-
debug!("No arguments provided for fractional targeting.");
13-
return Ok(Value::Null);
15+
// If the first element is a simple string, use it as the bucketing expression and use remaining elements as buckets.
16+
// Otherwise, compute the bucketing key from provided data and treat the whole array as bucket definitions.
17+
let (bucket_by, distributions) = match &args[0] {
18+
DataValue::String(s) => {
19+
debug!("Using explicit bucketing expression: {:?}", s);
20+
(s.to_string(), args[1..].to_vec())
1421
}
22+
_ => {
23+
// Extract targeting key from context if available
24+
let targeting_key = match &data {
25+
DataValue::Object(obj) => match obj.iter().find(|(k, _)| *k == "targetingKey") {
26+
Some((_, DataValue::String(s))) => s.to_string(),
27+
_ => String::new(),
28+
},
29+
_ => String::new(),
30+
};
1531

16-
// If the first element is a simple string, use it as the bucketing expression and use remaining elements as buckets.
17-
// Otherwise, compute the bucketing key from provided data and treat the whole array as bucket definitions.
18-
let (bucket_by, distributions) = match &args[0] {
19-
Value::String(s) => {
20-
debug!("Using explicit bucketing expression: {:?}", s);
21-
(s.clone(), &args[1..])
22-
}
23-
_ => {
24-
let targeting_key = data
25-
.get("targetingKey")
26-
.and_then(|v| v.as_str())
27-
.unwrap_or_default();
28-
let flag_key = data
29-
.get("$flagd")
30-
.and_then(|v| v.as_object())
31-
.and_then(|o| o.get("flagKey"))
32-
.and_then(|v| v.as_str())
33-
.unwrap_or_default();
34-
let computed = format!("{}{}", flag_key, targeting_key);
35-
debug!(
36-
"No explicit bucketing expression. Computed bucketing key: {:?}",
37-
computed
38-
);
39-
(computed, args)
40-
}
41-
};
32+
// Extract flag key from context if available
33+
let flag_key = match &data {
34+
DataValue::Object(obj) => match obj.iter().find(|(k, _)| *k == "$flagd") {
35+
Some((_, DataValue::Object(flagd_obj))) => {
36+
match flagd_obj.iter().find(|(k, _)| *k == "flagKey") {
37+
Some((_, DataValue::String(s))) => s.to_string(),
38+
_ => String::new(),
39+
}
40+
}
41+
_ => String::new(),
42+
},
43+
_ => String::new(),
44+
};
4245

43-
if distributions.is_empty() {
44-
debug!("No bucket definitions provided.");
45-
return Ok(Value::Null);
46+
let computed = format!("{}{}", flag_key, targeting_key);
47+
debug!("Using computed bucketing key from context: {:?}", computed);
48+
(computed, args)
4649
}
50+
};
51+
52+
if distributions.is_empty() {
53+
debug!("No bucket definitions provided.");
54+
return Ok(DataValue::Null);
55+
}
56+
57+
let mut total_weight = 0;
58+
let mut buckets = Vec::new();
59+
for dist in &distributions {
60+
if let DataValue::Array(arr) = dist {
61+
if arr.len() >= 2 {
62+
let variant = match &arr[0] {
63+
DataValue::String(s) => s.to_string(),
64+
_ => String::new(),
65+
};
66+
67+
let weight = match &arr[1] {
68+
DataValue::Number(n) => {
69+
if let Some(i) = n.as_i64() {
70+
i as i32
71+
} else {
72+
1
73+
}
74+
}
75+
_ => 1,
76+
};
4777

48-
let mut total_weight = 0;
49-
let mut buckets = Vec::new();
50-
for dist in distributions {
51-
if let Value::Array(arr) = dist {
52-
if arr.len() >= 2 {
53-
let variant = arr[0].as_str().unwrap_or_default().to_string();
54-
let weight = arr[1].as_u64().unwrap_or(1) as i32;
55-
total_weight += weight;
56-
buckets.push((variant.clone(), weight));
57-
debug!("Added bucket: variant={} weight={}", variant, weight);
58-
} else {
59-
debug!("Bucket definition incomplete: {:?}", arr);
60-
}
78+
total_weight += weight;
79+
buckets.push((variant.clone(), weight));
80+
debug!("Added bucket: variant={} weight={}", variant, weight);
6181
} else {
62-
debug!("Invalid bucket definition format: {:?}", dist);
82+
debug!("Bucket definition incomplete: {:?}", arr);
6383
}
84+
} else {
85+
debug!("Invalid bucket definition format: {:?}", dist);
6486
}
65-
debug!("Total weight of buckets: {}", total_weight);
87+
}
88+
debug!("Total weight of buckets: {}", total_weight);
89+
90+
let hash: u32 = murmurhash3_x86_32(bucket_by.as_bytes(), 0);
91+
let bucket = (hash as f64 / u32::MAX as f64) * 100.0;
92+
debug!(
93+
"Computed hash: {}, bucket_by: {:?}, resulting bucket value: {:.4}",
94+
hash, bucket_by, bucket
95+
);
6696

67-
let hash: u32 = murmurhash3_x86_32(bucket_by.as_bytes(), 0);
68-
let bucket = (hash as f64 / u32::MAX as f64) * 100.0;
97+
let mut bucket_sum = 0.0;
98+
for (variant, weight) in buckets {
99+
bucket_sum += (weight as f64 * 100.0) / total_weight as f64;
69100
debug!(
70-
"Computed hash: {}, bucket_by: {:?}, resulting bucket value: {:.4}",
71-
hash, bucket_by, bucket
101+
"Checking bucket: variant={} cumulative_weight_threshold={:.4}, current bucket={:.4}",
102+
variant, bucket_sum, bucket
72103
);
73-
74-
let mut bucket_sum = 0.0;
75-
for (variant, weight) in buckets {
76-
bucket_sum += (weight as f64 * 100.0) / total_weight as f64;
77-
debug!("Checking bucket: variant={} cumulative_weight_threshold={:.4}, current bucket={:.4}", variant, bucket_sum, bucket);
78-
if bucket < bucket_sum {
79-
debug!(
80-
"Selected variant: {} for bucket value {:.4}",
81-
variant, bucket
82-
);
83-
return Ok(Value::String(variant));
84-
}
104+
if bucket < bucket_sum {
105+
debug!(
106+
"Selected variant: {} for bucket value {:.4}",
107+
variant, bucket
108+
);
109+
// To return a string from a function, we need to do something unsafe to get a 'static lifetime
110+
// We'll use Box::leak to create a string with 'static lifetime
111+
// This is safe because the DataLogic library will copy this value to the arena
112+
let leaked_str: &'r str = Box::leak(variant.into_boxed_str());
113+
return Ok(DataValue::String(leaked_str));
85114
}
86-
87-
debug!("No bucket matched for bucket value: {:.4}", bucket);
88-
Ok(Value::Null)
89115
}
116+
117+
debug!("No bucket matched for bucket value: {:.4}", bucket);
118+
Ok(DataValue::Null)
90119
}

0 commit comments

Comments
 (0)