futu_backend/valid_brokers/
store.rs1use std::collections::BTreeSet;
2use std::path::{Path, PathBuf};
3
4use futu_core::error::{FutuError, Result};
5
6const VALID_BROKER_SNAPSHOT_SCHEMA_VERSION: u32 = 1;
7
8#[derive(Debug, serde::Serialize, serde::Deserialize)]
9struct ValidBrokerSnapshotFile {
10 schema_version: u32,
11 uid: u64,
12 broker_ids: Vec<u32>,
13}
14
15pub fn load_last_good_valid_broker_ids(uid: u64) -> Result<Option<Vec<u32>>> {
16 load_valid_broker_snapshot_from_path(&valid_broker_snapshot_path(uid)?, uid)
17}
18
19pub fn save_last_good_valid_broker_ids(uid: u64, broker_ids: &[u32]) -> Result<()> {
20 save_valid_broker_snapshot_to_path(&valid_broker_snapshot_path(uid)?, uid, broker_ids)
21}
22
23fn valid_broker_snapshot_path(uid: u64) -> Result<PathBuf> {
24 let dir = crate::auth::try_futu_opend_dir()
25 .map_err(|error| FutuError::Codec(format!("valid broker store dir: {error}")))?;
26 let uid_key = format!("{:x}", md5::compute(uid.to_string().as_bytes()));
27 Ok(dir.join(format!("valid-brokers-{}.json", &uid_key[..16])))
28}
29
30pub(super) fn load_valid_broker_snapshot_from_path(
31 path: &Path,
32 expected_uid: u64,
33) -> Result<Option<Vec<u32>>> {
34 let data = match std::fs::read(path) {
35 Ok(data) => data,
36 Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(None),
37 Err(error) => return Err(FutuError::Network(error)),
38 };
39 let snapshot: ValidBrokerSnapshotFile = serde_json::from_slice(&data)
40 .map_err(|error| FutuError::Codec(format!("valid broker store decode: {error}")))?;
41 if snapshot.schema_version != VALID_BROKER_SNAPSHOT_SCHEMA_VERSION {
42 return Err(FutuError::Codec(format!(
43 "valid broker store schema {} unsupported",
44 snapshot.schema_version
45 )));
46 }
47 if snapshot.uid != expected_uid {
48 return Err(FutuError::Codec(
49 "valid broker store uid mismatch".to_string(),
50 ));
51 }
52 Ok(Some(normalize_broker_ids(snapshot.broker_ids)))
53}
54
55pub(super) fn save_valid_broker_snapshot_to_path(
56 path: &Path,
57 uid: u64,
58 broker_ids: &[u32],
59) -> Result<()> {
60 let snapshot = ValidBrokerSnapshotFile {
61 schema_version: VALID_BROKER_SNAPSHOT_SCHEMA_VERSION,
62 uid,
63 broker_ids: normalize_broker_ids(broker_ids.iter().copied()),
64 };
65 let body = serde_json::to_vec_pretty(&snapshot)
66 .map_err(|error| FutuError::Codec(format!("valid broker store encode: {error}")))?;
67 crate::auth::write_secret_file(path, &body).map_err(FutuError::Network)
68}
69
70fn normalize_broker_ids<I>(broker_ids: I) -> Vec<u32>
71where
72 I: IntoIterator<Item = u32>,
73{
74 broker_ids
75 .into_iter()
76 .collect::<BTreeSet<_>>()
77 .into_iter()
78 .collect()
79}