mostro_client/parser/
orders.rs1use std::collections::HashMap;
2
3use crate::parser::common::{apply_kind_color, apply_status_color, create_error_cell};
4use crate::util::Event;
5use anyhow::Result;
6use chrono::DateTime;
7use comfy_table::presets::UTF8_FULL;
8use comfy_table::*;
9use log::{error, info};
10use mostro_core::prelude::*;
11use nostr_sdk::prelude::*;
12use uuid::Uuid;
13
14use crate::nip33::order_from_tags;
15
16pub fn parse_orders_events(
17 events: Events,
18 currency: Option<String>,
19 status: Option<Status>,
20 kind: Option<mostro_core::order::Kind>,
21) -> Vec<SmallOrder> {
22 let mut latest_by_id: HashMap<Uuid, SmallOrder> = HashMap::new();
24
25 for event in events.iter() {
26 let mut order = match order_from_tags(event.tags.clone()) {
28 Ok(o) => o,
29 Err(e) => {
30 error!("{e:?}");
31 continue;
32 }
33 };
34 let order_id = match order.id {
36 Some(id) => id,
37 None => {
38 info!("Order ID is none");
39 continue;
40 }
41 };
42 if order.kind.is_none() {
44 info!("Order kind is none");
45 continue;
46 }
47 order.created_at = Some(event.created_at.as_u64() as i64);
49 latest_by_id
51 .entry(order_id)
52 .and_modify(|existing| {
53 let new_ts = order.created_at.unwrap_or(0);
54 let old_ts = existing.created_at.unwrap_or(0);
55 if new_ts > old_ts {
56 *existing = order.clone();
57 }
58 })
59 .or_insert(order);
60 }
61
62 let mut requested: Vec<SmallOrder> = latest_by_id
63 .into_values()
64 .filter(|o| status.map(|s| o.status == Some(s)).unwrap_or(true))
65 .filter(|o| currency.as_ref().map(|c| o.fiat_code == *c).unwrap_or(true))
66 .filter(|o| {
67 kind.as_ref()
68 .map(|k| o.kind.as_ref() == Some(k))
69 .unwrap_or(true)
70 })
71 .collect();
72
73 requested.sort_by(|a, b| b.created_at.cmp(&a.created_at));
74 requested
75}
76
77pub fn print_order_preview(ord: Payload) -> Result<String, String> {
78 let single_order = match ord {
79 Payload::Order(o) => o,
80 _ => return Err("Error".to_string()),
81 };
82
83 let mut table = Table::new();
84
85 table
86 .load_preset(UTF8_FULL)
87 .set_content_arrangement(ContentArrangement::Dynamic)
88 .set_width(160)
89 .set_header(vec![
90 Cell::new("π Kind")
91 .add_attribute(Attribute::Bold)
92 .set_alignment(CellAlignment::Center),
93 Cell::new("βΏ Amount")
94 .add_attribute(Attribute::Bold)
95 .set_alignment(CellAlignment::Center),
96 Cell::new("π± Fiat")
97 .add_attribute(Attribute::Bold)
98 .set_alignment(CellAlignment::Center),
99 Cell::new("π΅ Fiat Amt")
100 .add_attribute(Attribute::Bold)
101 .set_alignment(CellAlignment::Center),
102 Cell::new("π³ Payment Method")
103 .add_attribute(Attribute::Bold)
104 .set_alignment(CellAlignment::Center),
105 Cell::new("π Premium %")
106 .add_attribute(Attribute::Bold)
107 .set_alignment(CellAlignment::Center),
108 ]);
109
110 let r = Row::from(vec![
112 if let Some(k) = single_order.kind {
113 apply_kind_color(
114 Cell::new(k.to_string()).set_alignment(CellAlignment::Center),
115 &k,
116 )
117 } else {
118 Cell::new("BUY/SELL").set_alignment(CellAlignment::Center)
119 },
120 if single_order.amount == 0 {
121 Cell::new("market").set_alignment(CellAlignment::Center)
122 } else {
123 Cell::new(single_order.amount).set_alignment(CellAlignment::Center)
124 },
125 Cell::new(single_order.fiat_code.to_string()).set_alignment(CellAlignment::Center),
126 if single_order.min_amount.is_none() && single_order.max_amount.is_none() {
128 Cell::new(single_order.fiat_amount.to_string()).set_alignment(CellAlignment::Center)
129 } else {
130 let range_str = match (single_order.min_amount, single_order.max_amount) {
131 (Some(min), Some(max)) => format!("{}-{}", min, max),
132 (Some(min), None) => format!("{}-?", min),
133 (None, Some(max)) => format!("?-{}", max),
134 (None, None) => "?".to_string(),
135 };
136 Cell::new(range_str).set_alignment(CellAlignment::Center)
137 },
138 Cell::new(single_order.payment_method.to_string()).set_alignment(CellAlignment::Center),
139 Cell::new(single_order.premium.to_string()).set_alignment(CellAlignment::Center),
140 ]);
141
142 table.add_row(r);
143
144 let mut result = table.to_string();
145 result.push('\n');
146 result.push_str("βββββββββββββββββββββββββββββββββββββββ\n");
147 result.push_str("π Order Preview - Please review carefully\n");
148 result.push_str("π‘ This order will be submitted to Mostro\n");
149 result.push_str("β
All details look correct? (Y/n)\n");
150 result.push_str("βββββββββββββββββββββββββββββββββββββββ\n");
151
152 Ok(result)
153}
154
155pub fn print_orders_table(orders_table: Vec<Event>) -> Result<String> {
156 let mut table = Table::new();
157 let orders_table: Vec<SmallOrder> = orders_table
159 .into_iter()
160 .filter_map(|event| {
161 if let Event::SmallOrder(order) = event {
162 Some(order)
163 } else {
164 None
165 }
166 })
167 .collect();
168
169 let mut rows: Vec<Row> = Vec::new();
171
172 if orders_table.is_empty() {
173 table
174 .load_preset(UTF8_FULL)
175 .set_content_arrangement(ContentArrangement::Dynamic)
176 .set_width(160)
177 .set_header(vec![Cell::new("π No Offers")
178 .add_attribute(Attribute::Bold)
179 .set_alignment(CellAlignment::Center)]);
180
181 let mut r = Row::new();
183
184 r.add_cell(create_error_cell(
185 "No offers found with requested parametersβ¦",
186 ));
187
188 rows.push(r);
190 } else {
191 table
192 .load_preset(UTF8_FULL)
193 .set_content_arrangement(ContentArrangement::Dynamic)
194 .set_width(160)
195 .set_header(vec![
196 Cell::new("π Kind")
197 .add_attribute(Attribute::Bold)
198 .set_alignment(CellAlignment::Center),
199 Cell::new("π Order Id")
200 .add_attribute(Attribute::Bold)
201 .set_alignment(CellAlignment::Center),
202 Cell::new("π Status")
203 .add_attribute(Attribute::Bold)
204 .set_alignment(CellAlignment::Center),
205 Cell::new("βΏ Amount")
206 .add_attribute(Attribute::Bold)
207 .set_alignment(CellAlignment::Center),
208 Cell::new("π± Fiat")
209 .add_attribute(Attribute::Bold)
210 .set_alignment(CellAlignment::Center),
211 Cell::new("π΅ Fiat Amt")
212 .add_attribute(Attribute::Bold)
213 .set_alignment(CellAlignment::Center),
214 Cell::new("π³ Payment Method")
215 .add_attribute(Attribute::Bold)
216 .set_alignment(CellAlignment::Center),
217 Cell::new("π
Created")
218 .add_attribute(Attribute::Bold)
219 .set_alignment(CellAlignment::Center),
220 ]);
221
222 for single_order in orders_table.into_iter() {
224 let date = DateTime::from_timestamp(single_order.created_at.unwrap_or(0), 0);
225
226 let r = Row::from(vec![
227 if let Some(k) = single_order.kind {
228 apply_kind_color(
229 Cell::new(k.to_string()).set_alignment(CellAlignment::Center),
230 &k,
231 )
232 } else {
233 Cell::new("BUY/SELL").set_alignment(CellAlignment::Center)
234 },
235 Cell::new(
236 single_order
237 .id
238 .map(|id| id.to_string())
239 .unwrap_or_else(|| "N/A".to_string()),
240 )
241 .set_alignment(CellAlignment::Center),
242 {
243 let status = single_order
244 .status
245 .unwrap_or(mostro_core::order::Status::Active)
246 .to_string();
247 apply_status_color(
248 Cell::new(&status).set_alignment(CellAlignment::Center),
249 &status,
250 )
251 },
252 if single_order.amount == 0 {
253 Cell::new("market").set_alignment(CellAlignment::Center)
254 } else {
255 Cell::new(single_order.amount.to_string()).set_alignment(CellAlignment::Center)
256 },
257 Cell::new(single_order.fiat_code.to_string()).set_alignment(CellAlignment::Center),
258 if single_order.min_amount.is_none() && single_order.max_amount.is_none() {
260 Cell::new(single_order.fiat_amount.to_string())
261 .set_alignment(CellAlignment::Center)
262 } else {
263 let range_str = match (single_order.min_amount, single_order.max_amount) {
264 (Some(min), Some(max)) => format!("{}-{}", min, max),
265 (Some(min), None) => format!("{}-?", min),
266 (None, Some(max)) => format!("?-{}", max),
267 (None, None) => "?".to_string(),
268 };
269 Cell::new(range_str).set_alignment(CellAlignment::Center)
270 },
271 Cell::new(single_order.payment_method.to_string())
272 .set_alignment(CellAlignment::Center),
273 Cell::new(
274 date.map(|d| d.format("%Y-%m-%d %H:%M").to_string())
275 .unwrap_or_else(|| "Invalid date".to_string()),
276 )
277 .set_alignment(CellAlignment::Center),
278 ]);
279 rows.push(r);
280 }
281 }
282
283 table.add_rows(rows);
284
285 Ok(table.to_string())
286}
287
288#[cfg(test)]
289mod tests {}