///|
/// A tracking payload. The nonempty key constraint is a local safety extension
/// to the specification. Public field construction bypasses constructor checks;
/// use new for validated events and copy at callback ownership boundaries.
pub(all) struct TrackingEvent {
key : String
value : Double?
details : EvaluationContext
}
///|
pub fn TrackingEvent::new(
key : String,
value~ : Double? = None,
details~ : EvaluationContext = EvaluationContext::empty(),
) -> Result[TrackingEvent, SdkError] {
let event : TrackingEvent = { key, value, details: details.copy(), }
match event.validate() {
Err(error) => Err(error)
Ok(_) => Ok(event)
}
}
///|
pub fn TrackingEvent::copy(self : TrackingEvent) -> TrackingEvent {
{ ..self, details: self.details.copy(), }
}
///|
/// Serialize a validated payload; absent value is omitted, not null. Context
/// JSON preserves its targeting key and snapshots nested detail containers.
pub fn TrackingEvent::to_json(self : TrackingEvent) -> Json {
let fields : Map[String, Json] = {
"key": Json::string(self.key),
"details": self.details.to_json(),
}
match self.value {
Some(value) => fields["value"] = Json::number(value)
None => ()
}
Json::object(fields)
}
///|
/// Validate raw public construction as well as constructor-created events.
/// Providers must not receive non-finite measures through a bypassed factory.
pub fn TrackingEvent::validate(self : TrackingEvent) -> Result[Unit, SdkError] {
if self.key == "" {
return Err(SdkError::new(General, "tracking event key must not be empty"))
}
match self.value {
Some(number) =>
if number.is_nan() || number.is_inf() {
return Err(
SdkError::new(General, "tracking event value must be finite"),
)
}
None => ()
}
Ok(())
}