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 #[must_use]
56 pub fn new(base_url: &str) -> Self {
57 Self {
58 base_url: base_url.to_string(),
59 client: reqwest::blocking::Client::new(),
60 }
61 }
62
63 pub fn query_trades(&self, sql: &str) -> Result<QueryResponse, Box<dyn Error>> {
64 let (select_fields, where_clause, order_by) = self.parse_sql(sql)?;
66
67 let request = QueryRequest {
68 select: select_fields,
69 where_clause,
70 order_by,
71 skip: None,
72 take: Some(100),
73 };
74
75 let mut json_request = serde_json::json!({
77 "select": request.select,
78 "skip": request.skip,
79 "take": request.take,
80 });
81
82 if let Some(where_clause) = &request.where_clause {
83 json_request["where"] = serde_json::Value::String(where_clause.clone());
84 }
85
86 if let Some(order_by) = &request.order_by {
87 json_request["orderBy"] = serde_json::Value::String(order_by.clone());
88 }
89
90 let response = self
93 .client
94 .post(format!("{}/api/trade/query", self.base_url))
95 .json(&json_request)
96 .send()?;
97
98 if !response.status().is_success() {
99 let error_text = response.text()?;
100 return Err(format!("API Error: {error_text}").into());
101 }
102
103 let result: QueryResponse = response.json()?;
104 Ok(result)
105 }
106
107 pub fn get_schema(&self) -> Result<SchemaResponse, Box<dyn Error>> {
108 let response = self
109 .client
110 .get(format!("{}/api/trade/schema/trade_deal", self.base_url))
111 .send()?;
112
113 if !response.status().is_success() {
114 return Err("Failed to fetch schema".into());
115 }
116
117 let schema: SchemaResponse = response.json()?;
118 Ok(schema)
119 }
120
121 fn parse_sql(
122 &self,
123 sql: &str,
124 ) -> Result<(Vec<String>, Option<String>, Option<String>), Box<dyn Error>> {
125 let sql_lower = sql.to_lowercase();
126
127 let select_start = sql_lower.find("select").ok_or("SELECT not found")? + 6;
129 let from_pos = sql_lower.find("from").ok_or("FROM not found")?;
130 let select_part = sql[select_start..from_pos].trim();
131
132 let select_fields: Vec<String> = if select_part == "*" {
133 vec!["*".to_string()]
134 } else {
135 select_part
136 .split(',')
137 .map(|s| s.trim().to_string())
138 .collect()
139 };
140
141 let where_clause = if let Some(where_pos) = sql_lower.find("where") {
143 let where_start = where_pos + 5;
144 let order_pos = sql_lower.find("order by");
145 let where_end = order_pos.unwrap_or(sql.len());
146 Some(sql[where_start..where_end].trim().to_string())
147 } else {
148 None
149 };
150
151 let order_by = if let Some(order_pos) = sql_lower.find("order by") {
153 let order_start = order_pos + 8;
154 Some(sql[order_start..].trim().to_string())
155 } else {
156 None
157 };
158
159 Ok((select_fields, where_clause, order_by))
160 }
161}