| use serde::Serialize; |
| use std::collections::HashMap; |
| use std::sync::Arc; |
| use tokio::sync::RwLock; |
|
|
| #[derive(Clone)] |
| pub struct AppState { |
| pub latest: Arc<RwLock<HashMap<String, Quote>>>, |
| pub codes: Arc<Vec<String>>, |
| pub started_at: String, |
| } |
|
|
| impl AppState { |
| pub fn new(codes: Vec<String>, started_at: String) -> Self { |
| Self { |
| latest: Arc::new(RwLock::new(HashMap::new())), |
| codes: Arc::new(codes), |
| started_at, |
| } |
| } |
| } |
|
|
| #[derive(Clone, Debug, Serialize)] |
| pub struct Quote { |
| pub received_at: String, |
| pub code: String, |
| pub name: Option<String>, |
| pub previous_close: Option<String>, |
| pub open: Option<String>, |
| pub price: Option<String>, |
| pub high: Option<String>, |
| pub low: Option<String>, |
| pub bid1: Option<String>, |
| pub ask1: Option<String>, |
| pub volume: Option<String>, |
| pub amount: Option<String>, |
| pub trade_date: Option<String>, |
| pub trade_time: Option<String>, |
| pub status: Option<String>, |
| pub raw_fields: String, |
| pub fields: Vec<String>, |
| } |
|
|
| pub fn quote_from_record(record: &str, received_at: String) -> Option<Quote> { |
| let (code, raw_fields) = record.split_once('=')?; |
| let fields: Vec<String> = raw_fields |
| .split(',') |
| .map(|s| s.trim_matches('"').to_string()) |
| .collect(); |
|
|
| let get = |idx: usize| fields.get(idx).cloned().filter(|s| !s.is_empty()); |
|
|
| |
| |
| |
| Some(Quote { |
| received_at, |
| code: code.to_string(), |
| name: get(0), |
| previous_close: get(1), |
| open: get(2), |
| price: get(3), |
| high: get(4), |
| low: get(5), |
| bid1: get(6), |
| ask1: get(7), |
| volume: get(8), |
| amount: get(9), |
| trade_date: get(30), |
| trade_time: get(31), |
| status: get(32), |
| raw_fields: raw_fields.to_string(), |
| fields, |
| }) |
| } |
|
|