Skip to main content

flyr/
table.rs

1use comfy_table::{Table, ContentArrangement, presets::UTF8_FULL};
2
3use crate::model::SearchResult;
4
5fn format_price(price: Option<i64>, currency: &str) -> String {
6    let p = match price {
7        Some(p) => p,
8        None => return "—".to_string(),
9    };
10    match currency {
11        "USD" => format!("${p}"),
12        "EUR" => format!("€{p}"),
13        "GBP" => format!("£{p}"),
14        "JPY" | "CNY" => format!("¥{p}"),
15        "KRW" => format!("₩{p}"),
16        "INR" => format!("₹{p}"),
17        "THB" => format!("฿{p}"),
18        _ => format!("{p} {currency}"),
19    }
20}
21
22pub fn render(result: &SearchResult, currency: &str) -> String {
23    let mut table = Table::new();
24    table
25        .load_preset(UTF8_FULL)
26        .set_content_arrangement(ContentArrangement::Dynamic)
27        .set_header(vec![
28            "Airlines", "Route", "Depart", "Arrive", "Duration", "Stops", "Aircraft", "Price",
29        ]);
30
31    for flight in &result.flights {
32        let airlines = flight.airlines.join(", ");
33
34        let route: Vec<String> = flight
35            .segments
36            .iter()
37            .map(|s| format!("{} → {}", s.from_airport.code, s.to_airport.code))
38            .collect();
39        let route_str = route.join("\n");
40
41        let depart = flight
42            .segments
43            .first()
44            .map(|s| s.departure.to_string())
45            .unwrap_or_else(|| "—".to_string());
46
47        let arrive = flight
48            .segments
49            .last()
50            .map(|s| s.arrival.to_string())
51            .unwrap_or_else(|| "—".to_string());
52
53        let duration = if flight.segments.is_empty() {
54            "—".to_string()
55        } else {
56            let total_duration: u32 = flight.segments.iter().map(|s| s.duration_minutes).sum();
57            let hours = total_duration / 60;
58            let mins = total_duration % 60;
59            format!("{hours}h {mins:02}m")
60        };
61
62        let stops = if flight.segments.is_empty() {
63            "—".to_string()
64        } else if flight.segments.len() == 1 {
65            "Nonstop".to_string()
66        } else {
67            let n = flight.segments.len() - 1;
68            let stopovers: Vec<&str> = flight.segments[..flight.segments.len() - 1]
69                .iter()
70                .map(|s| s.to_airport.code.as_str())
71                .collect();
72            format!("{n} ({})", stopovers.join(", "))
73        };
74
75        let aircraft: Vec<String> = flight
76            .segments
77            .iter()
78            .filter_map(|s| s.aircraft.clone())
79            .collect();
80        let aircraft_str = aircraft.join(", ");
81
82        let price = format_price(flight.price, currency);
83
84        table.add_row(vec![
85            &airlines,
86            &route_str,
87            &depart,
88            &arrive,
89            &duration,
90            &stops,
91            &aircraft_str,
92            &price,
93        ]);
94    }
95
96    table.to_string()
97}