Skip to main content

acuity_index_api_rs/
types.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4use std::fmt;
5
6#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
7#[serde(tag = "type", content = "value")]
8pub enum Key {
9    Variant(u8, u8),
10    Custom(CustomKey),
11}
12
13#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
14pub struct CustomKey {
15    pub name: String,
16    #[serde(flatten)]
17    pub value: CustomValue,
18}
19
20impl CustomKey {
21    pub fn scalar(name: impl Into<String>, value: CustomValue) -> Self {
22        Self {
23            name: name.into(),
24            value,
25        }
26    }
27
28    pub fn composite(
29        name: impl Into<String>,
30        values: impl IntoIterator<Item = CustomScalarValue>,
31    ) -> Self {
32        Self {
33            name: name.into(),
34            value: CustomValue::Composite(values.into_iter().collect()),
35        }
36    }
37}
38
39#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
40pub struct Bytes32(pub [u8; 32]);
41
42impl Serialize for Bytes32 {
43    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
44    where
45        S: serde::Serializer,
46    {
47        serializer.serialize_str(&format!("0x{}", hex::encode(self.0)))
48    }
49}
50
51impl<'de> Deserialize<'de> for Bytes32 {
52    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
53    where
54        D: serde::Deserializer<'de>,
55    {
56        let s = String::deserialize(deserializer)?;
57        let hex_part = s.strip_prefix("0x").unwrap_or(&s);
58        let bytes = hex::decode(hex_part).map_err(serde::de::Error::custom)?;
59        let arr: [u8; 32] = bytes
60            .try_into()
61            .map_err(|_| serde::de::Error::custom("expected 32 bytes"))?;
62        Ok(Self(arr))
63    }
64}
65
66impl From<[u8; 32]> for Bytes32 {
67    fn from(value: [u8; 32]) -> Self {
68        Self(value)
69    }
70}
71
72#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
73pub struct U64Text(pub u64);
74
75impl Serialize for U64Text {
76    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
77    where
78        S: serde::Serializer,
79    {
80        serializer.serialize_str(&self.0.to_string())
81    }
82}
83
84impl<'de> Deserialize<'de> for U64Text {
85    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
86    where
87        D: serde::Deserializer<'de>,
88    {
89        struct Visitor;
90
91        impl serde::de::Visitor<'_> for Visitor {
92            type Value = U64Text;
93
94            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95                f.write_str("a u64 string or integer")
96            }
97
98            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
99            where
100                E: serde::de::Error,
101            {
102                Ok(U64Text(value))
103            }
104
105            fn visit_u128<E>(self, value: u128) -> Result<Self::Value, E>
106            where
107                E: serde::de::Error,
108            {
109                Ok(U64Text(u64::try_from(value).map_err(E::custom)?))
110            }
111
112            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
113            where
114                E: serde::de::Error,
115            {
116                Ok(U64Text(value.parse::<u64>().map_err(E::custom)?))
117            }
118        }
119
120        deserializer.deserialize_any(Visitor)
121    }
122}
123
124#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
125pub struct U128Text(pub u128);
126
127impl Serialize for U128Text {
128    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
129    where
130        S: serde::Serializer,
131    {
132        serializer.serialize_str(&self.0.to_string())
133    }
134}
135
136impl<'de> Deserialize<'de> for U128Text {
137    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138    where
139        D: serde::Deserializer<'de>,
140    {
141        struct Visitor;
142
143        impl serde::de::Visitor<'_> for Visitor {
144            type Value = U128Text;
145
146            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147                f.write_str("a u128 string or integer")
148            }
149
150            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
151            where
152                E: serde::de::Error,
153            {
154                Ok(U128Text(value.into()))
155            }
156
157            fn visit_u128<E>(self, value: u128) -> Result<Self::Value, E>
158            where
159                E: serde::de::Error,
160            {
161                Ok(U128Text(value))
162            }
163
164            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
165            where
166                E: serde::de::Error,
167            {
168                Ok(U128Text(value.parse::<u128>().map_err(E::custom)?))
169            }
170        }
171
172        deserializer.deserialize_any(Visitor)
173    }
174}
175
176#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
177#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
178pub enum CustomValue {
179    Bytes32(Bytes32),
180    U32(u32),
181    U64(U64Text),
182    U128(U128Text),
183    String(String),
184    Bool(bool),
185    Composite(Vec<CustomScalarValue>),
186}
187
188#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
189#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
190pub enum CustomScalarValue {
191    Bytes32(Bytes32),
192    U32(u32),
193    U64(U64Text),
194    U128(U128Text),
195    String(String),
196    Bool(bool),
197}
198
199#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
200#[serde(rename_all = "camelCase")]
201pub struct EventRef {
202    pub block_number: u32,
203    pub event_index: u16,
204}
205
206#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
207#[serde(rename_all = "camelCase")]
208pub struct DecodedEvent {
209    pub block_number: u32,
210    pub event_index: u16,
211    pub event: StoredEvent,
212}
213
214#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
215#[serde(rename_all = "camelCase")]
216pub struct StoredEvent {
217    pub spec_version: u32,
218    pub pallet_name: String,
219    pub event_name: String,
220    pub pallet_index: u8,
221    pub variant_index: u8,
222    pub event_index: u16,
223    pub fields: Value,
224}
225
226#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
227pub struct EventMeta {
228    pub index: u8,
229    pub name: String,
230}
231
232#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
233pub struct PalletMeta {
234    pub index: u8,
235    pub name: String,
236    pub events: Vec<EventMeta>,
237}
238
239#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
240pub struct Span {
241    pub start: u32,
242    pub end: u32,
243}
244
245#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
246#[serde(rename_all = "camelCase")]
247pub struct EventsResponse {
248    pub key: Key,
249    pub events: Vec<EventRef>,
250    #[serde(default)]
251    pub decoded_events: Vec<DecodedEvent>,
252}
253
254#[derive(Debug, Clone, PartialEq)]
255pub struct EventMatch {
256    pub event_ref: EventRef,
257    pub decoded_event: Option<DecodedEvent>,
258}
259
260#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
261#[serde(tag = "type", rename_all = "camelCase")]
262pub enum SubscriptionTarget {
263    Status,
264    Events { key: Key },
265}
266
267#[derive(Debug, Clone, PartialEq, Eq)]
268pub struct StatusUpdate {
269    pub spans: Vec<Span>,
270}
271
272#[derive(Debug, Clone, PartialEq)]
273pub struct EventNotification {
274    pub key: Key,
275    pub event: EventRef,
276    pub decoded_event: Option<DecodedEvent>,
277}
278
279impl StoredEvent {
280    pub fn field(&self, name: &str) -> Option<&Value> {
281        self.fields.get(name)
282    }
283
284    pub fn variant(&self) -> (u8, u8) {
285        (self.pallet_index, self.variant_index)
286    }
287}
288
289impl DecodedEvent {
290    pub fn pallet_name(&self) -> &str {
291        &self.event.pallet_name
292    }
293
294    pub fn event_name(&self) -> &str {
295        &self.event.event_name
296    }
297
298    pub fn variant(&self) -> (u8, u8) {
299        self.event.variant()
300    }
301
302    pub fn field(&self, name: &str) -> Option<&Value> {
303        self.event.field(name)
304    }
305}
306
307impl EventsResponse {
308    pub fn event_matches(&self) -> Vec<EventMatch> {
309        let decoded_by_ref: HashMap<(u32, u16), DecodedEvent> = self
310            .decoded_events
311            .iter()
312            .cloned()
313            .map(|decoded| ((decoded.block_number, decoded.event_index), decoded))
314            .collect();
315
316        self.events
317            .iter()
318            .cloned()
319            .map(|event_ref| EventMatch {
320                decoded_event: decoded_by_ref
321                    .get(&(event_ref.block_number, event_ref.event_index))
322                    .cloned(),
323                event_ref,
324            })
325            .collect()
326    }
327}
328
329#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
330pub struct RequestMessage<T> {
331    pub id: u64,
332    #[serde(rename = "type")]
333    pub message_type: &'static str,
334    #[serde(flatten)]
335    pub payload: T,
336}
337
338#[derive(Serialize, Default, Debug, Clone, PartialEq, Eq)]
339pub struct EmptyPayload {}
340
341#[derive(Serialize, Debug, Clone, PartialEq)]
342pub struct GetEventsPayload {
343    pub key: Key,
344    #[serde(skip_serializing_if = "Option::is_none")]
345    pub limit: Option<u16>,
346    #[serde(skip_serializing_if = "Option::is_none")]
347    pub before: Option<EventRef>,
348}
349
350#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
351pub struct SubscribeEventsPayload {
352    pub key: Key,
353}
354
355#[derive(Deserialize, Debug, Clone, PartialEq)]
356pub struct Envelope {
357    pub id: Option<u64>,
358    #[serde(rename = "type")]
359    pub message_type: String,
360    #[serde(default)]
361    pub data: Option<Value>,
362}
363
364#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
365pub struct ErrorPayload {
366    pub code: String,
367    pub message: String,
368}
369
370#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
371#[serde(rename_all = "camelCase")]
372pub struct SubscriptionStatusPayload {
373    pub action: String,
374    pub target: SubscriptionTarget,
375}
376
377#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
378#[serde(rename_all = "camelCase")]
379pub struct SubscriptionTerminatedPayload {
380    pub reason: String,
381    pub message: String,
382}
383
384#[derive(Deserialize, Debug, Clone, PartialEq)]
385#[serde(rename_all = "camelCase")]
386pub struct EventNotificationPayload {
387    pub key: Key,
388    pub event: EventRef,
389    pub decoded_event: Option<DecodedEvent>,
390}
391
392#[cfg(test)]
393mod tests {
394    use super::*;
395    use serde::de::value::{
396        Error as ValueError, StrDeserializer, U128Deserializer, U64Deserializer,
397    };
398    use serde::Deserialize;
399    use serde_json::json;
400
401    fn bytes32_hex(byte: u8) -> String {
402        format!("0x{}", hex::encode([byte; 32]))
403    }
404
405    #[test]
406    fn bytes32_serializes_as_prefixed_hex() {
407        assert_eq!(
408            serde_json::to_string(&Bytes32([0xAB; 32])).unwrap(),
409            format!("\"{}\"", bytes32_hex(0xAB))
410        );
411    }
412
413    #[test]
414    fn bytes32_deserializes_with_and_without_prefix() {
415        let prefixed =
416            serde_json::from_str::<Bytes32>(&format!("\"{}\"", bytes32_hex(0x11))).unwrap();
417        let unprefixed =
418            serde_json::from_str::<Bytes32>(&format!("\"{}\"", "22".repeat(32))).unwrap();
419
420        assert_eq!(prefixed, Bytes32([0x11; 32]));
421        assert_eq!(unprefixed, Bytes32([0x22; 32]));
422    }
423
424    #[test]
425    fn bytes32_rejects_wrong_length() {
426        let error = serde_json::from_str::<Bytes32>("\"0x1234\"")
427            .unwrap_err()
428            .to_string();
429        assert!(error.contains("expected 32 bytes"));
430    }
431
432    #[test]
433    fn u64_text_serializes_and_deserializes_multiple_input_shapes() {
434        assert_eq!(serde_json::to_string(&U64Text(42)).unwrap(), "\"42\"");
435        assert_eq!(
436            U64Text::deserialize(U64Deserializer::<ValueError>::new(42)).unwrap(),
437            U64Text(42)
438        );
439        assert_eq!(
440            U64Text::deserialize(U128Deserializer::<ValueError>::new(42)).unwrap(),
441            U64Text(42)
442        );
443        assert_eq!(
444            U64Text::deserialize(StrDeserializer::<ValueError>::new("42")).unwrap(),
445            U64Text(42)
446        );
447    }
448
449    #[test]
450    fn u128_text_serializes_and_deserializes_multiple_input_shapes() {
451        assert_eq!(serde_json::to_string(&U128Text(42)).unwrap(), "\"42\"");
452        assert_eq!(
453            U128Text::deserialize(U64Deserializer::<ValueError>::new(42)).unwrap(),
454            U128Text(42)
455        );
456        assert_eq!(
457            U128Text::deserialize(U128Deserializer::<ValueError>::new(42)).unwrap(),
458            U128Text(42)
459        );
460        assert_eq!(
461            U128Text::deserialize(StrDeserializer::<ValueError>::new("42")).unwrap(),
462            U128Text(42)
463        );
464    }
465
466    #[test]
467    fn custom_value_serializes_all_scalar_kinds() {
468        let cases = [
469            (
470                CustomValue::Bytes32(Bytes32([0xAB; 32])),
471                json!({"kind": "bytes32", "value": bytes32_hex(0xAB)}),
472            ),
473            (CustomValue::U32(7), json!({"kind": "u32", "value": 7})),
474            (
475                CustomValue::U64(U64Text(8)),
476                json!({"kind": "u64", "value": "8"}),
477            ),
478            (
479                CustomValue::U128(U128Text(9)),
480                json!({"kind": "u128", "value": "9"}),
481            ),
482            (
483                CustomValue::String("slug".into()),
484                json!({"kind": "string", "value": "slug"}),
485            ),
486            (
487                CustomValue::Bool(true),
488                json!({"kind": "bool", "value": true}),
489            ),
490        ];
491
492        for (value, expected) in cases {
493            assert_eq!(serde_json::to_value(value).unwrap(), expected);
494        }
495    }
496
497    #[test]
498    fn custom_key_scalar_constructor_preserves_scalar_value() {
499        assert_eq!(
500            CustomKey::scalar("ref_index", CustomValue::U32(42)),
501            CustomKey {
502                name: "ref_index".into(),
503                value: CustomValue::U32(42),
504            }
505        );
506    }
507
508    #[test]
509    fn custom_key_composite_constructor_collects_values() {
510        assert_eq!(
511            CustomKey::composite(
512                "item_revision",
513                [
514                    CustomScalarValue::Bytes32([0x11; 32].into()),
515                    CustomScalarValue::U32(7),
516                ],
517            ),
518            CustomKey {
519                name: "item_revision".into(),
520                value: CustomValue::Composite(vec![
521                    CustomScalarValue::Bytes32(Bytes32([0x11; 32])),
522                    CustomScalarValue::U32(7),
523                ]),
524            }
525        );
526    }
527
528    #[test]
529    fn custom_key_composite_constructor_allows_empty_values() {
530        assert_eq!(
531            CustomKey::composite("empty", std::iter::empty()),
532            CustomKey {
533                name: "empty".into(),
534                value: CustomValue::Composite(vec![]),
535            }
536        );
537    }
538
539    #[test]
540    fn custom_value_serializes_flat_composite_values() {
541        let value = CustomValue::Composite(vec![
542            CustomScalarValue::Bytes32(Bytes32([0xAB; 32])),
543            CustomScalarValue::U32(7),
544        ]);
545
546        assert_eq!(
547            serde_json::to_value(value).unwrap(),
548            json!({
549                "kind": "composite",
550                "value": [
551                    {"kind": "bytes32", "value": bytes32_hex(0xAB)},
552                    {"kind": "u32", "value": 7}
553                ]
554            })
555        );
556    }
557
558    #[test]
559    fn custom_value_rejects_nested_composites() {
560        let error = serde_json::from_value::<CustomValue>(json!({
561            "kind": "composite",
562            "value": [
563                {"kind": "u32", "value": 7},
564                {
565                    "kind": "composite",
566                    "value": [
567                        {"kind": "bool", "value": true}
568                    ]
569                }
570            ]
571        }))
572        .unwrap_err()
573        .to_string();
574
575        assert!(error.contains("unknown variant `composite`"));
576    }
577
578    #[test]
579    fn key_deserializes_variant_and_custom_shapes() {
580        let variant =
581            serde_json::from_value::<Key>(json!({"type": "Variant", "value": [5, 3]})).unwrap();
582        let custom = serde_json::from_value::<Key>(json!({
583            "type": "Custom",
584            "value": {"name": "published", "kind": "bool", "value": true}
585        }))
586        .unwrap();
587
588        assert_eq!(variant, Key::Variant(5, 3));
589        assert_eq!(
590            custom,
591            Key::Custom(CustomKey {
592                name: "published".into(),
593                value: CustomValue::Bool(true),
594            })
595        );
596    }
597
598    #[test]
599    fn key_deserializes_composite_custom_shape() {
600        let custom = serde_json::from_value::<Key>(json!({
601            "type": "Custom",
602            "value": {
603                "name": "item_revision",
604                "kind": "composite",
605                "value": [
606                    {"kind": "bytes32", "value": bytes32_hex(0x11)},
607                    {"kind": "u32", "value": 7}
608                ]
609            }
610        }))
611        .unwrap();
612
613        assert_eq!(
614            custom,
615            Key::Custom(CustomKey {
616                name: "item_revision".into(),
617                value: CustomValue::Composite(vec![
618                    CustomScalarValue::Bytes32(Bytes32([0x11; 32])),
619                    CustomScalarValue::U32(7),
620                ]),
621            })
622        );
623    }
624
625    #[test]
626    fn serializes_empty_payload_request_without_extra_fields() {
627        let request = RequestMessage {
628            id: 1,
629            message_type: "Status",
630            payload: EmptyPayload::default(),
631        };
632
633        let json = serde_json::to_value(&request).unwrap();
634        assert_eq!(json, json!({"id": 1, "type": "Status"}));
635    }
636
637    #[test]
638    fn serializes_get_events_request_in_server_shape() {
639        let request = RequestMessage {
640            id: 3,
641            message_type: "GetEvents",
642            payload: GetEventsPayload {
643                key: Key::Custom(CustomKey {
644                    name: "item_id".into(),
645                    value: CustomValue::Bytes32(Bytes32([0x12; 32])),
646                }),
647                limit: Some(25),
648                before: Some(EventRef {
649                    block_number: 50,
650                    event_index: 3,
651                }),
652            },
653        };
654
655        let json = serde_json::to_value(&request).unwrap();
656        assert_eq!(json["id"], 3);
657        assert_eq!(json["type"], "GetEvents");
658        assert_eq!(json["key"]["type"], "Custom");
659        assert_eq!(json["key"]["value"]["name"], "item_id");
660        assert_eq!(json["key"]["value"]["kind"], "bytes32");
661        assert_eq!(json["limit"], 25);
662        assert_eq!(json["before"]["blockNumber"], 50);
663        assert_eq!(json["before"]["eventIndex"], 3);
664    }
665
666    #[test]
667    fn serializes_get_events_request_without_optional_fields() {
668        let request = RequestMessage {
669            id: 4,
670            message_type: "GetEvents",
671            payload: GetEventsPayload {
672                key: Key::Custom(CustomKey {
673                    name: "ref_index".into(),
674                    value: CustomValue::U32(42),
675                }),
676                limit: None,
677                before: None,
678            },
679        };
680
681        let json = serde_json::to_value(&request).unwrap();
682        assert_eq!(
683            json,
684            json!({
685                "id": 4,
686                "type": "GetEvents",
687                "key": {"type": "Custom", "value": {"name": "ref_index", "kind": "u32", "value": 42}}
688            })
689        );
690    }
691
692    #[test]
693    fn serializes_get_events_request_with_composite_custom_key() {
694        let request = RequestMessage {
695            id: 5,
696            message_type: "GetEvents",
697            payload: GetEventsPayload {
698                key: Key::Custom(CustomKey {
699                    name: "item_revision".into(),
700                    value: CustomValue::Composite(vec![
701                        CustomScalarValue::Bytes32(Bytes32([0x12; 32])),
702                        CustomScalarValue::U32(7),
703                    ]),
704                }),
705                limit: None,
706                before: None,
707            },
708        };
709
710        let json = serde_json::to_value(&request).unwrap();
711        assert_eq!(json["key"]["value"]["kind"], "composite");
712        assert_eq!(json["key"]["value"]["value"][0]["kind"], "bytes32");
713        assert_eq!(
714            json["key"]["value"]["value"][1],
715            json!({"kind": "u32", "value": 7})
716        );
717    }
718
719    #[test]
720    fn serializes_subscribe_events_request_in_server_shape() {
721        let request = RequestMessage {
722            id: 7,
723            message_type: "SubscribeEvents",
724            payload: SubscribeEventsPayload {
725                key: Key::Custom(CustomKey {
726                    name: "account_id".into(),
727                    value: CustomValue::Bytes32(Bytes32([0xAB; 32])),
728                }),
729            },
730        };
731
732        let json = serde_json::to_value(&request).unwrap();
733        assert_eq!(json["type"], "SubscribeEvents");
734        assert_eq!(json["key"]["value"]["kind"], "bytes32");
735    }
736
737    #[test]
738    fn serializes_subscribe_events_request_with_composite_custom_key() {
739        let request = RequestMessage {
740            id: 8,
741            message_type: "SubscribeEvents",
742            payload: SubscribeEventsPayload {
743                key: Key::Custom(CustomKey {
744                    name: "item_revision".into(),
745                    value: CustomValue::Composite(vec![
746                        CustomScalarValue::Bytes32(Bytes32([0xAB; 32])),
747                        CustomScalarValue::U32(9),
748                    ]),
749                }),
750            },
751        };
752
753        let json = serde_json::to_value(&request).unwrap();
754        assert_eq!(json["type"], "SubscribeEvents");
755        assert_eq!(json["key"]["value"]["kind"], "composite");
756    }
757
758    #[test]
759    fn deserializes_events_response_payload() {
760        let payload = serde_json::from_value::<EventsResponse>(json!({
761            "key": {"type": "Custom", "value": {"name": "ref_index", "kind": "u32", "value": 42}},
762            "events": [{"blockNumber": 50, "eventIndex": 3}],
763            "decodedEvents": [{
764                "blockNumber": 50,
765                "eventIndex": 3,
766                "event": {
767                    "specVersion": 1234,
768                    "palletName": "Referenda",
769                    "eventName": "Submitted",
770                    "palletIndex": 42,
771                    "variantIndex": 0,
772                    "eventIndex": 3,
773                    "fields": {"index": 42}
774                }
775            }]
776        }))
777        .unwrap();
778
779        assert_eq!(payload.events.len(), 1);
780        assert_eq!(payload.decoded_events.len(), 1);
781    }
782
783    #[test]
784    fn deserializes_events_response_without_decoded_events() {
785        let payload = serde_json::from_value::<EventsResponse>(json!({
786            "key": {"type": "Custom", "value": {"name": "ref_index", "kind": "u32", "value": 42}},
787            "events": [{"blockNumber": 50, "eventIndex": 3}]
788        }))
789        .unwrap();
790
791        assert_eq!(payload.events.len(), 1);
792        assert!(payload.decoded_events.is_empty());
793    }
794
795    #[test]
796    fn deserializes_events_response_with_composite_key() {
797        let payload = serde_json::from_value::<EventsResponse>(json!({
798            "key": {
799                "type": "Custom",
800                "value": {
801                    "name": "item_revision",
802                    "kind": "composite",
803                    "value": [
804                        {"kind": "bytes32", "value": bytes32_hex(0x11)},
805                        {"kind": "u32", "value": 7}
806                    ]
807                }
808            },
809            "events": [{"blockNumber": 50, "eventIndex": 3}]
810        }))
811        .unwrap();
812
813        assert_eq!(
814            payload.key,
815            Key::Custom(CustomKey {
816                name: "item_revision".into(),
817                value: CustomValue::Composite(vec![
818                    CustomScalarValue::Bytes32(Bytes32([0x11; 32])),
819                    CustomScalarValue::U32(7),
820                ]),
821            })
822        );
823    }
824
825    #[test]
826    fn stored_event_and_events_response_helpers_work() {
827        let payload = serde_json::from_value::<EventsResponse>(json!({
828            "key": {"type": "Custom", "value": {"name": "ref_index", "kind": "u32", "value": 42}},
829            "events": [
830                {"blockNumber": 50, "eventIndex": 3},
831                {"blockNumber": 49, "eventIndex": 1}
832            ],
833            "decodedEvents": [{
834                "blockNumber": 50,
835                "eventIndex": 3,
836                "event": {
837                    "specVersion": 1234,
838                    "palletName": "Referenda",
839                    "eventName": "Submitted",
840                    "palletIndex": 42,
841                    "variantIndex": 0,
842                    "eventIndex": 3,
843                    "fields": {"index": 42}
844                }
845            }]
846        }))
847        .unwrap();
848
849        assert_eq!(payload.decoded_events[0].pallet_name(), "Referenda");
850        assert_eq!(payload.decoded_events[0].event_name(), "Submitted");
851        assert_eq!(payload.decoded_events[0].variant(), (42, 0));
852        assert_eq!(payload.decoded_events[0].field("index"), Some(&json!(42)));
853
854        let matches = payload.event_matches();
855        assert_eq!(matches.len(), 2);
856        assert!(matches[0].decoded_event.is_some());
857        assert!(matches[1].decoded_event.is_none());
858    }
859
860    #[test]
861    fn stored_event_field_returns_none_for_non_object_fields() {
862        let event = StoredEvent {
863            spec_version: 1,
864            pallet_name: "Example".into(),
865            event_name: "Positional".into(),
866            pallet_index: 1,
867            variant_index: 2,
868            event_index: 3,
869            fields: json!([1, 2, 3]),
870        };
871
872        assert_eq!(event.variant(), (1, 2));
873        assert!(event.field("0").is_none());
874    }
875
876    #[test]
877    fn deserializes_subscription_status_payload() {
878        let payload = serde_json::from_value::<SubscriptionStatusPayload>(json!({
879            "action": "subscribed",
880            "target": {"type": "events", "key": {"type": "Custom", "value": {"name": "ref_index", "kind": "u32", "value": 42}}}
881        }))
882        .unwrap();
883
884        assert_eq!(payload.action, "subscribed");
885        assert_eq!(
886            payload.target,
887            SubscriptionTarget::Events {
888                key: Key::Custom(CustomKey {
889                    name: "ref_index".into(),
890                    value: CustomValue::U32(42),
891                })
892            }
893        );
894    }
895
896    #[test]
897    fn deserializes_subscription_status_payload_with_composite_key() {
898        let payload = serde_json::from_value::<SubscriptionStatusPayload>(json!({
899            "action": "subscribed",
900            "target": {
901                "type": "events",
902                "key": {
903                    "type": "Custom",
904                    "value": {
905                        "name": "item_revision",
906                        "kind": "composite",
907                        "value": [
908                            {"kind": "bytes32", "value": bytes32_hex(0x11)},
909                            {"kind": "u32", "value": 7}
910                        ]
911                    }
912                }
913            }
914        }))
915        .unwrap();
916
917        assert_eq!(
918            payload.target,
919            SubscriptionTarget::Events {
920                key: Key::Custom(CustomKey {
921                    name: "item_revision".into(),
922                    value: CustomValue::Composite(vec![
923                        CustomScalarValue::Bytes32(Bytes32([0x11; 32])),
924                        CustomScalarValue::U32(7),
925                    ]),
926                })
927            }
928        );
929    }
930
931    #[test]
932    fn deserializes_subscription_terminated_payload() {
933        let payload = serde_json::from_value::<SubscriptionTerminatedPayload>(json!({
934            "reason": "backpressure",
935            "message": "subscriber disconnected due to backpressure"
936        }))
937        .unwrap();
938
939        assert_eq!(payload.reason, "backpressure");
940        assert_eq!(
941            payload.message,
942            "subscriber disconnected due to backpressure"
943        );
944    }
945
946    #[test]
947    fn deserializes_event_notification_payload() {
948        let payload = serde_json::from_value::<EventNotificationPayload>(json!({
949            "key": {"type": "Custom", "value": {"name": "item_id", "kind": "bytes32", "value": bytes32_hex(0x11)}},
950            "event": {"blockNumber": 50, "eventIndex": 3},
951            "decodedEvent": {
952                "blockNumber": 50,
953                "eventIndex": 3,
954                "event": {
955                    "specVersion": 1234,
956                    "palletName": "Content",
957                    "eventName": "PublishRevision",
958                    "palletIndex": 42,
959                    "variantIndex": 1,
960                    "eventIndex": 3,
961                    "fields": {"revision_id": 1}
962                }
963            }
964        }))
965        .unwrap();
966
967        assert_eq!(payload.event.block_number, 50);
968        assert!(payload.decoded_event.is_some());
969    }
970
971    #[test]
972    fn deserializes_event_notification_payload_with_composite_key() {
973        let payload = serde_json::from_value::<EventNotificationPayload>(json!({
974            "key": {
975                "type": "Custom",
976                "value": {
977                    "name": "item_revision",
978                    "kind": "composite",
979                    "value": [
980                        {"kind": "bytes32", "value": bytes32_hex(0x11)},
981                        {"kind": "u32", "value": 7}
982                    ]
983                }
984            },
985            "event": {"blockNumber": 50, "eventIndex": 3},
986            "decodedEvent": null
987        }))
988        .unwrap();
989
990        assert_eq!(
991            payload.key,
992            Key::Custom(CustomKey {
993                name: "item_revision".into(),
994                value: CustomValue::Composite(vec![
995                    CustomScalarValue::Bytes32(Bytes32([0x11; 32])),
996                    CustomScalarValue::U32(7),
997                ]),
998            })
999        );
1000    }
1001
1002    #[test]
1003    fn envelope_defaults_missing_data_to_none() {
1004        let envelope = serde_json::from_value::<Envelope>(json!({
1005            "id": 1,
1006            "type": "subscriptionStatus"
1007        }))
1008        .unwrap();
1009
1010        assert_eq!(envelope.id, Some(1));
1011        assert_eq!(envelope.message_type, "subscriptionStatus");
1012        assert_eq!(envelope.data, None);
1013    }
1014}