futucli/cmd/analysis/
security.rs1use anyhow::{Result, anyhow, bail};
6use prost::Message;
7use serde::Serialize;
8use tabled::Tabled;
9
10use crate::common::connect_gateway;
11use crate::output::OutputFormat;
12
13#[derive(Tabled)]
14struct UserSecurityRow {
15 #[tabled(rename = "Code")]
16 code: String,
17 #[tabled(rename = "Name")]
18 name: String,
19 #[tabled(rename = "Lot")]
20 lot: i32,
21 #[tabled(rename = "Type")]
22 sec_type: i32,
23}
24
25#[derive(Serialize)]
26struct UserSecurityJson {
27 code: String,
28 name: String,
29 lot_size: i32,
30 sec_type: i32,
31}
32
33pub async fn run_user_security(
34 gateway: &str,
35 group_name: &str,
36 format: OutputFormat,
37) -> Result<()> {
38 let (client, _rx) = connect_gateway(gateway, "futucli-user-security").await?;
39 let req = futu_proto::qot_get_user_security::Request {
40 c2s: futu_proto::qot_get_user_security::C2s {
41 group_name: group_name.to_string(),
42 header: None,
43 },
44 };
45 let body = req.encode_to_vec();
46 let frame = client
47 .request(futu_core::proto_id::QOT_GET_USER_SECURITY, body)
48 .await?;
49 let resp = futu_proto::qot_get_user_security::Response::decode(frame.body.as_ref())
50 .map_err(|e| anyhow!("decode user_security: {e}"))?;
51 if resp.ret_type != 0 {
52 bail!(
53 "user_security ret_type={} msg={:?}",
54 resp.ret_type,
55 resp.ret_msg
56 );
57 }
58 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
59 let mut rows = Vec::new();
60 let mut jsons = Vec::new();
61 for s in &s2c.static_info_list {
62 rows.push(UserSecurityRow {
63 code: s.basic.security.code.clone(),
64 name: s.basic.name.clone(),
65 lot: s.basic.lot_size,
66 sec_type: s.basic.sec_type,
67 });
68 jsons.push(UserSecurityJson {
69 code: s.basic.security.code.clone(),
70 name: s.basic.name.clone(),
71 lot_size: s.basic.lot_size,
72 sec_type: s.basic.sec_type,
73 });
74 }
75 format.print_rows(&rows, &jsons)?;
76 Ok(())
77}
78
79#[derive(Tabled)]
80struct GroupRow {
81 #[tabled(rename = "Name")]
82 name: String,
83 #[tabled(rename = "Type")]
84 gtype: String,
85}
86
87#[derive(Serialize)]
88struct GroupJson {
89 group_name: String,
90 group_type: i32,
91}
92
93pub async fn run_user_security_groups(
94 gateway: &str,
95 group_type: i32,
96 format: OutputFormat,
97) -> Result<()> {
98 let (client, _rx) = connect_gateway(gateway, "futucli-user-security-groups").await?;
99 let req = futu_proto::qot_get_user_security_group::Request {
100 c2s: futu_proto::qot_get_user_security_group::C2s {
101 group_type,
102 header: None, },
104 };
105 let body = req.encode_to_vec();
106 let frame = client
107 .request(futu_core::proto_id::QOT_GET_USER_SECURITY_GROUP, body)
108 .await?;
109 let resp = futu_proto::qot_get_user_security_group::Response::decode(frame.body.as_ref())
110 .map_err(|e| anyhow!("decode user_security_group: {e}"))?;
111 if resp.ret_type != 0 {
112 bail!(
113 "user_security_group ret_type={} msg={:?}",
114 resp.ret_type,
115 resp.ret_msg
116 );
117 }
118 let s2c = resp.s2c.ok_or_else(|| anyhow!("missing s2c"))?;
119 let mut rows = Vec::new();
120 let mut jsons = Vec::new();
121 for g in &s2c.group_list {
122 let label = match g.group_type {
123 1 => "Custom",
124 2 => "System",
125 3 => "All",
126 _ => "?",
127 };
128 rows.push(GroupRow {
129 name: g.group_name.clone(),
130 gtype: format!("{} ({})", label, g.group_type),
131 });
132 jsons.push(GroupJson {
133 group_name: g.group_name.clone(),
134 group_type: g.group_type,
135 });
136 }
137 format.print_rows(&rows, &jsons)?;
138 Ok(())
139}