11use std:: collections:: { HashMap , HashSet } ;
2- use std:: sync:: Mutex ;
2+ use std:: sync:: { Mutex , OnceLock } ;
33
44use crate :: client:: BeforeSendHook ;
55use crate :: client:: CaptureDefaults ;
@@ -14,6 +14,32 @@ pub(super) const MAX_FLAG_CALLED_CACHE_SIZE: usize = 50_000;
1414
1515pub ( super ) type FlagEventDedupCache = Mutex < HashMap < String , HashSet < String > > > ;
1616
17+ struct RuntimeContext {
18+ os : String ,
19+ os_version : String ,
20+ }
21+
22+ static RUNTIME_CONTEXT : OnceLock < RuntimeContext > = OnceLock :: new ( ) ;
23+
24+ fn runtime_context ( ) -> & ' static RuntimeContext {
25+ RUNTIME_CONTEXT . get_or_init ( || {
26+ let info = os_info:: get ( ) ;
27+ RuntimeContext {
28+ os : info. os_type ( ) . to_string ( ) ,
29+ os_version : info. version ( ) . to_string ( ) ,
30+ }
31+ } )
32+ }
33+
34+ pub ( super ) fn apply_runtime_context ( event : & mut Event ) {
35+ let context = runtime_context ( ) ;
36+ event. insert_prop_default ( "$os" , serde_json:: Value :: String ( context. os . clone ( ) ) ) ;
37+ event. insert_prop_default (
38+ "$os_version" ,
39+ serde_json:: Value :: String ( context. os_version . clone ( ) ) ,
40+ ) ;
41+ }
42+
1743pub ( super ) fn flag_event_dedup_cache ( ) -> FlagEventDedupCache {
1844 Mutex :: new ( HashMap :: new ( ) )
1945}
@@ -326,6 +352,32 @@ mod tests {
326352 assert_eq ! ( event. properties( ) . get( "$geoip_disable" ) , Some ( & json!( true ) ) ) ;
327353 }
328354
355+ #[ test]
356+ fn runtime_context_adds_missing_os_properties_only ( ) {
357+ let mut event = Event :: new ( "test" , "user-1" ) ;
358+ event. insert_prop ( "$os" , "custom-os" ) . unwrap ( ) ;
359+
360+ apply_runtime_context ( & mut event) ;
361+
362+ assert_eq ! ( event. properties( ) . get( "$os" ) , Some ( & json!( "custom-os" ) ) ) ;
363+ assert ! ( event. properties( ) . contains_key( "$os_version" ) ) ;
364+ assert ! ( !event. properties( ) . contains_key( "$os_arch" ) ) ;
365+ }
366+
367+ #[ test]
368+ fn flag_called_event_leaves_runtime_context_to_capture_path ( ) {
369+ let event = flag_called_event (
370+ flag_params ( HashMap :: new ( ) , HashMap :: new ( ) , None ) ,
371+ false ,
372+ true ,
373+ )
374+ . expect ( "valid flag-called event" ) ;
375+
376+ assert ! ( !event. properties( ) . contains_key( "$os" ) ) ;
377+ assert ! ( !event. properties( ) . contains_key( "$os_version" ) ) ;
378+ assert ! ( !event. properties( ) . contains_key( "$os_arch" ) ) ;
379+ }
380+
329381 #[ test]
330382 fn before_send_hooks_mutate_and_drop_events ( ) {
331383 let options = crate :: ClientOptionsBuilder :: default ( )
0 commit comments