1use futu_core::error::{FutuError, Result};
4use prost::Message;
5
6use crate::proto_internal::{msgcenter, msgdefine, option_move_warn_msg_define};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct MessageCenterHead {
10 pub msg_id: Option<u64>,
11 pub create_time: Option<u64>,
12 pub modify_time: Option<u64>,
13 pub msg_sequence: Option<u64>,
14 pub inner_sequence: Option<u64>,
15 pub status: Option<u32>,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct MessageCenterItem {
20 pub head: Option<MessageCenterHead>,
21 pub body: Option<Vec<u8>>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct MessageCenterPush {
26 pub user_id: Option<u64>,
27 pub msg_category: Option<u32>,
28 pub notify_msg: Option<MessageCenterItem>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct PriceWarningDetail {
33 pub key: Option<u64>,
34 pub warn_type: Option<u32>,
35 pub set_value: Option<i64>,
36 pub present_value: Option<i64>,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct PriceWarningPush {
41 pub stock_id: Option<u64>,
42 pub price: Option<u32>,
43 pub price_change_ratio: Option<i64>,
44 pub msg_content: Option<String>,
45 pub msg_content_tc: Option<String>,
46 pub msg_content_en: Option<String>,
47 pub note: Option<String>,
48 pub market_status: Option<i32>,
49 pub warn_detail: Option<PriceWarningDetail>,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct LocaleTextPush {
54 pub language_tag: Option<String>,
55 pub context: Option<String>,
56 pub language_id: Option<i32>,
57}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct LocaleStringPush {
61 pub entries: Vec<LocaleTextPush>,
62}
63
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct OptionMoveWarningPush {
66 pub underlying_id: Option<u64>,
67 pub option_id: Option<u64>,
68 pub multi_lang_msg_content: Option<LocaleStringPush>,
69}
70
71pub fn decode_message_center_push(body: &[u8]) -> Result<MessageCenterPush> {
72 let wire = msgcenter::MsgCenterNotify::decode(body)
73 .map_err(|error| FutuError::Codec(format!("CMD5300 outer decode: {error}")))?;
74 Ok(MessageCenterPush {
75 user_id: wire.user_id,
76 msg_category: wire.msg_category,
77 notify_msg: wire.notify_msg.map(message_center_item),
78 })
79}
80
81pub fn decode_price_warning_push(body: &[u8]) -> Result<PriceWarningPush> {
82 let wire = msgdefine::PriceWarnMsg::decode(body)
83 .map_err(|error| FutuError::Codec(format!("CMD5300 price decode: {error}")))?;
84 Ok(PriceWarningPush {
85 stock_id: wire.stock_id,
86 price: wire.price,
87 price_change_ratio: wire.price_change_ratio,
88 msg_content: wire.msg_content,
89 msg_content_tc: wire.msg_content_tc,
90 msg_content_en: wire.msg_content_en,
91 note: wire.note,
92 market_status: wire.market_status,
93 warn_detail: wire.warn_detail.map(|detail| PriceWarningDetail {
94 key: detail.key,
95 warn_type: detail.r#type,
96 set_value: detail.set_value,
97 present_value: detail.present_value,
98 }),
99 })
100}
101
102pub fn decode_option_move_warning_push(body: &[u8]) -> Result<OptionMoveWarningPush> {
103 let wire = option_move_warn_msg_define::OptionMoveWarnMsg::decode(body)
104 .map_err(|error| FutuError::Codec(format!("CMD5300 option decode: {error}")))?;
105 Ok(OptionMoveWarningPush {
106 underlying_id: wire.underlying_id,
107 option_id: wire.option_id,
108 multi_lang_msg_content: wire.multi_lang_msg_content.map(|locale| LocaleStringPush {
109 entries: locale
110 .str_context
111 .into_iter()
112 .map(|entry| LocaleTextPush {
113 language_tag: entry.language_tag,
114 context: entry.context,
115 language_id: entry.language_id,
116 })
117 .collect(),
118 }),
119 })
120}
121
122fn message_center_item(wire: msgcenter::MsgItem) -> MessageCenterItem {
123 MessageCenterItem {
124 head: wire.head.map(|head| MessageCenterHead {
125 msg_id: head.msg_id,
126 create_time: head.create_time,
127 modify_time: head.modify_time,
128 msg_sequence: head.msg_sequence,
129 inner_sequence: head.inner_sequence,
130 status: head.status,
131 }),
132 body: wire.body,
133 }
134}