Skip to main content

futucli/cmd/analysis/
security.rs

1//! v1.4.110+ split (from cmd/analysis.rs): security domain.
2//!
3//! pub items: run_user_security, run_user_security_groups.
4
5use 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, // v1.4.110 codex Slice 1 schema 占位
103        },
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}