0.4.6
This commit is contained in:
266
khbb_lib/src/ws_event.rs
Normal file
266
khbb_lib/src/ws_event.rs
Normal file
@@ -0,0 +1,266 @@
|
||||
// file: khbb_lib/src/ws_event.rs
|
||||
|
||||
//! Normalized WebSocket events produced from Solana PubSub notifications.
|
||||
|
||||
/// Normalized WebSocket event emitted by the listener runtime.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum KhbbWsNormalizedEvent {
|
||||
/// Normalized slot notification.
|
||||
Slot(KhbbWsSlotEvent),
|
||||
/// Normalized logs notification.
|
||||
Logs(KhbbWsLogsEvent),
|
||||
/// Normalized program notification.
|
||||
Program(KhbbWsProgramEvent),
|
||||
}
|
||||
|
||||
/// Normalized slot notification event.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct KhbbWsSlotEvent {
|
||||
/// Subscription identifier.
|
||||
pub subscription_id: u64,
|
||||
/// Subscription kind.
|
||||
pub source_kind: crate::KhbbWsSubscriptionKind,
|
||||
/// Optional subscription label.
|
||||
pub source_label: std::option::Option<std::string::String>,
|
||||
/// Slot.
|
||||
pub slot: u64,
|
||||
/// Parent slot.
|
||||
pub parent: u64,
|
||||
/// Root slot.
|
||||
pub root: u64,
|
||||
}
|
||||
|
||||
/// Normalized logs notification event.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct KhbbWsLogsEvent {
|
||||
/// Subscription identifier.
|
||||
pub subscription_id: u64,
|
||||
/// Subscription kind.
|
||||
pub source_kind: crate::KhbbWsSubscriptionKind,
|
||||
/// Optional subscription label.
|
||||
pub source_label: std::option::Option<std::string::String>,
|
||||
/// Transaction signature.
|
||||
pub signature: std::string::String,
|
||||
/// Whether the transaction errored.
|
||||
pub has_error: bool,
|
||||
/// Raw logs emitted by the transaction.
|
||||
pub logs: std::vec::Vec<std::string::String>,
|
||||
/// Context slot.
|
||||
pub context_slot: u64,
|
||||
}
|
||||
|
||||
/// Normalized program notification event.
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct KhbbWsProgramEvent {
|
||||
/// Subscription identifier.
|
||||
pub subscription_id: u64,
|
||||
/// Subscription kind.
|
||||
pub source_kind: crate::KhbbWsSubscriptionKind,
|
||||
/// Optional subscription label.
|
||||
pub source_label: std::option::Option<std::string::String>,
|
||||
/// Updated account pubkey.
|
||||
pub pubkey: std::string::String,
|
||||
/// Context slot.
|
||||
pub context_slot: u64,
|
||||
}
|
||||
|
||||
/// Normalizes a raw Solana PubSub notification into an internal event.
|
||||
pub(crate) fn normalize_ws_notification(
|
||||
method: &str,
|
||||
raw: &str,
|
||||
source_subscription: std::option::Option<&crate::domain::KhbbActiveWsSubscription>,
|
||||
) -> core::result::Result<std::option::Option<KhbbWsNormalizedEvent>, crate::KhbbError> {
|
||||
match method {
|
||||
"slotNotification" => {
|
||||
let parse_result = crate::solana_rpc_ws::parse_slot_notification(raw);
|
||||
let notification = match parse_result {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
return Err(error);
|
||||
},
|
||||
};
|
||||
let source_kind = match source_subscription {
|
||||
Some(value) => value.kind,
|
||||
None => crate::KhbbWsSubscriptionKind::Slot,
|
||||
};
|
||||
let source_label = match source_subscription {
|
||||
Some(value) => value.label.clone(),
|
||||
None => None,
|
||||
};
|
||||
Ok(Some(KhbbWsNormalizedEvent::Slot(KhbbWsSlotEvent {
|
||||
subscription_id: notification.params.subscription,
|
||||
source_kind,
|
||||
source_label,
|
||||
slot: notification.params.result.slot,
|
||||
parent: notification.params.result.parent,
|
||||
root: notification.params.result.root,
|
||||
})))
|
||||
},
|
||||
"logsNotification" => {
|
||||
let parse_result = crate::solana_rpc_ws::parse_logs_notification(raw);
|
||||
let notification = match parse_result {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
return Err(error);
|
||||
},
|
||||
};
|
||||
let source_kind = match source_subscription {
|
||||
Some(value) => value.kind,
|
||||
None => crate::KhbbWsSubscriptionKind::Logs,
|
||||
};
|
||||
let source_label = match source_subscription {
|
||||
Some(value) => value.label.clone(),
|
||||
None => None,
|
||||
};
|
||||
Ok(Some(KhbbWsNormalizedEvent::Logs(KhbbWsLogsEvent {
|
||||
subscription_id: notification.params.subscription,
|
||||
source_kind,
|
||||
source_label,
|
||||
signature: notification.params.result.value.signature,
|
||||
has_error: notification.params.result.value.err.is_some(),
|
||||
logs: notification.params.result.value.logs,
|
||||
context_slot: notification.params.result.context.slot,
|
||||
})))
|
||||
},
|
||||
"programNotification" => {
|
||||
let parse_result = crate::solana_rpc_ws::parse_program_notification(raw);
|
||||
let notification = match parse_result {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
return Err(error);
|
||||
},
|
||||
};
|
||||
let source_kind = match source_subscription {
|
||||
Some(value) => value.kind,
|
||||
None => crate::KhbbWsSubscriptionKind::Program,
|
||||
};
|
||||
let source_label = match source_subscription {
|
||||
Some(value) => value.label.clone(),
|
||||
None => None,
|
||||
};
|
||||
Ok(Some(KhbbWsNormalizedEvent::Program(KhbbWsProgramEvent {
|
||||
subscription_id: notification.params.subscription,
|
||||
source_kind,
|
||||
source_label,
|
||||
pubkey: notification.params.result.value.pubkey,
|
||||
context_slot: notification.params.result.context.slot,
|
||||
})))
|
||||
},
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn normalize_slot_notification_returns_slot_event() {
|
||||
let raw = r#"{
|
||||
"jsonrpc":"2.0",
|
||||
"method":"slotNotification",
|
||||
"params":{
|
||||
"subscription":7,
|
||||
"result":{
|
||||
"slot":100,
|
||||
"parent":99,
|
||||
"root":90
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
let result = super::normalize_ws_notification("slotNotification", raw, None);
|
||||
assert!(result.is_ok());
|
||||
let event_option = result.expect("normalize slot");
|
||||
assert!(event_option.is_some());
|
||||
match event_option.expect("slot event") {
|
||||
super::KhbbWsNormalizedEvent::Slot(event) => {
|
||||
assert_eq!(event.subscription_id, 7);
|
||||
assert_eq!(event.slot, 100);
|
||||
assert_eq!(event.parent, 99);
|
||||
assert_eq!(event.root, 90);
|
||||
},
|
||||
_ => {
|
||||
panic!("expected slot event");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_logs_notification_returns_logs_event() {
|
||||
let raw = r#"{
|
||||
"jsonrpc":"2.0",
|
||||
"method":"logsNotification",
|
||||
"params":{
|
||||
"subscription":8,
|
||||
"result":{
|
||||
"context":{"slot":123},
|
||||
"value":{
|
||||
"signature":"abc",
|
||||
"err":null,
|
||||
"logs":["log1","log2"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
let result = super::normalize_ws_notification("logsNotification", raw, None);
|
||||
assert!(result.is_ok());
|
||||
let event_option = result.expect("normalize logs");
|
||||
assert!(event_option.is_some());
|
||||
match event_option.expect("logs event") {
|
||||
super::KhbbWsNormalizedEvent::Logs(event) => {
|
||||
assert_eq!(event.subscription_id, 8);
|
||||
assert_eq!(event.signature, "abc");
|
||||
assert!(!event.has_error);
|
||||
assert_eq!(event.logs.len(), 2);
|
||||
assert_eq!(event.context_slot, 123);
|
||||
},
|
||||
_ => {
|
||||
panic!("expected logs event");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_program_notification_returns_program_event() {
|
||||
let raw = r#"{
|
||||
"jsonrpc":"2.0",
|
||||
"method":"programNotification",
|
||||
"params":{
|
||||
"subscription":9,
|
||||
"result":{
|
||||
"context":{"slot":456},
|
||||
"value":{
|
||||
"pubkey":"TokenAccount1111111111111111111111111111111",
|
||||
"account":{
|
||||
"lamports":1,
|
||||
"data":["","base64"],
|
||||
"owner":"11111111111111111111111111111111",
|
||||
"executable":false,
|
||||
"rentEpoch":0,
|
||||
"space":0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
let result = super::normalize_ws_notification("programNotification", raw, None);
|
||||
assert!(result.is_ok());
|
||||
let event_option = result.expect("normalize program");
|
||||
assert!(event_option.is_some());
|
||||
match event_option.expect("program event") {
|
||||
super::KhbbWsNormalizedEvent::Program(event) => {
|
||||
assert_eq!(event.subscription_id, 9);
|
||||
assert_eq!(event.pubkey, "TokenAccount1111111111111111111111111111111");
|
||||
assert_eq!(event.context_slot, 456);
|
||||
},
|
||||
_ => {
|
||||
panic!("expected program event");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn normalize_unknown_method_returns_none() {
|
||||
let result = super::normalize_ws_notification("unknownNotification", "{}", None);
|
||||
assert!(result.is_ok());
|
||||
assert!(result.expect("normalize unknown").is_none());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user