1use reqwest;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::error::Error;
5
6#[derive(Debug, Serialize)]
7pub struct QueryRequest {
8 pub select: Vec<String>,
9 pub where_clause: Option<String>,
10 pub order_by: Option<String>,
11 pub skip: Option<usize>,
12 pub take: Option<usize>,
13}
14
15#[derive(Debug, Deserialize, Clone)]
16pub struct QueryResponse {
17 pub data: Vec<Value>,
18 pub count: usize,
19 pub query: QueryInfo,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub source: Option<String>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub table: Option<String>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub cached: Option<bool>,
26}
27
28#[derive(Debug, Deserialize, Clone)]
29pub struct QueryInfo {
30 pub select: Vec<String>,
31 pub where_clause: Option<String>,
32 pub order_by: Option<String>,
33}
34
35#[derive(Debug, Deserialize)]
36pub struct SchemaResponse {
37 pub table_name: String,
38 pub columns: Vec<ColumnInfo>,
39}
40
41#[derive(Debug, Deserialize)]
42pub struct ColumnInfo {
43 pub name: String,
44 pub r#type: String,
45 pub is_nullable: bool,
46}
47
48#[derive(Clone)]
49pub struct ApiClient {
50 pub base_url: String,
51 client: reqwest::blocking::Client,
52}
53
54impl ApiClient {
55 pub fn new(base_url: &str) -> Self {
56 Self {
57 base_url: base_url.to_string(),
58 client: reqwest::blocking::Client::new(),
59 }
60 }
61
62 pub fn query_trades(&self, sql: &str) -> Result<QueryResponse, Box<dyn Error>> {
63 let (select_fields, where_clause, order_by) = self.parse_sql(sql)?;
65
66 let request = QueryRequest {
67 select: select_fields,
68 where_clause,
69 order_by,
70 skip: None,
71 take: Some(100),
72 };
73
74 let mut json_request = serde_json::json!({
76 "select": request.select,
77 "skip": request.skip,
78 "take": request.take,
79 });
80
81 if let Some(where_clause) = &request.where_clause {
82 json_request["where"] = serde_json::Value::String(where_clause.clone());
83 }
84
85 if let Some(order_by) = &request.order_by {
86 json_request["orderBy"] = serde_json::Value::String(order_by.clone());
87 }
88
89 let response = self
92 .client
93 .post(format!("{}/api/trade/query", self.base_url))
94 .json(&json_request)
95 .send()?;
96
97 if !response.status().is_success() {
98 let error_text = response.text()?;
99 return Err(format!("API Error: {}", error_text).into());
100 }
101
102 let result: QueryResponse = response.json()?;
103 Ok(result)
104 }
105
106 pub fn get_schema(&self) -> Result<SchemaResponse, Box<dyn Error>> {
107 let response = self
108 .client
109 .get(format!("{}/api/trade/schema/trade_deal", self.base_url))
110 .send()?;
111
112 if !response.status().is_success() {
113 return Err("Failed to fetch schema".into());
114 }
115
116 let schema: SchemaResponse = response.json()?;
117 Ok(schema)
118 }
119
120 fn parse_sql(
121 &self,
122 sql: &str,
123 ) -> Result<(Vec<String>, Option<String>, Option<String>), Box<dyn Error>> {
124 let sql_lower = sql.to_lowercase();
125
126 let select_start = sql_lower.find("select").ok_or("SELECT not found")? + 6;
128 let from_pos = sql_lower.find("from").ok_or("FROM not found")?;
129 let select_part = sql[select_start..from_pos].trim();
130
131 let select_fields: Vec<String> = if select_part == "*" {
132 vec!["*".to_string()]
133 } else {
134 select_part
135 .split(',')
136 .map(|s| s.trim().to_string())
137 .collect()
138 };
139
140 let where_clause = if let Some(where_pos) = sql_lower.find("where") {
142 let where_start = where_pos + 5;
143 let order_pos = sql_lower.find("order by");
144 let where_end = order_pos.unwrap_or(sql.len());
145 Some(sql[where_start..where_end].trim().to_string())
146 } else {
147 None
148 };
149
150 let order_by = if let Some(order_pos) = sql_lower.find("order by") {
152 let order_start = order_pos + 8;
153 Some(sql[order_start..].trim().to_string())
154 } else {
155 None
156 };
157
158 Ok((select_fields, where_clause, order_by))
159 }
160}