• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

voiceapiai / ralertsinua / 9134287912

17 May 2024 09:08PM UTC coverage: 29.214% (-0.7%) from 29.893%
9134287912

push

github

web-flow
chore: change dependecnies for cache and errors (#9)

The recent changes introduce several updates across the project, focusing on improving error handling, enhancing the release process, and refining functionality. Key updates include a new CI workflow for automated releases, enhanced error diagnostics with miette, caching improvements in the HTTP client, and various simplifications to serialization and deserialization logic. Additionally, new configuration options and methods have been added to support better application performance and maintainability.

77 of 375 new or added lines in 21 files covered. (20.53%)

18 existing lines in 12 files now uncovered.

565 of 1934 relevant lines covered (29.21%)

4.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/components/map.rs
1
use geo::Rect as GeoRect;
2
use ralertsinua_geo::*;
3
use ralertsinua_models::*;
4
use ratatui::widgets::canvas::Canvas;
5
use ratatui::{prelude::*, widgets::*};
6
use rust_i18n::t;
7
use std::fmt::Debug;
8
use tokio::sync::mpsc::UnboundedSender;
9
#[allow(unused)]
10
use tracing::debug;
11
// use tui_popup::Popup;
12

13
use super::{Component, Frame, Result, WithPlacement};
14
use crate::{action::*, config::*, layout::*, tui_helpers::*};
15

16
#[derive(Debug)]
17
pub struct Map<'a> {
18
    command_tx: Option<UnboundedSender<Action>>,
19
    placement: LayoutPoint,
20
    #[allow(unused)]
21
    title: Line<'a>,
22
    #[allow(unused)]
23
    config: Config,
24
    bounding_rect: GeoRect,
25
    boundary: CountryBoundary,
26
    locations: [Location; 27],
27
    selected_location_uid: i32,
28
    oblast_statuses: AirRaidAlertOblastStatuses,
29
    alerts: Alerts,
30
    //
31
    width: u16,
32
    height: u16,
33
    resolution: (f64, f64),
34
}
35

36
impl<'a> Map<'a> {
37
    #[inline]
38
    pub fn new() -> Self {
×
39
        Self {
×
40
            command_tx: Option::default(),
×
41
            placement: LayoutPoint(LayoutArea::Left, Some(LayoutTab::Tab1)),
×
42
            title: Line::default(),
×
43
            config: Config::default(),
×
44
            boundary: CountryBoundary::default(),
×
45
            bounding_rect: *UKRAINE_BBOX,
×
46
            locations: core::array::from_fn(|_| Location::default()),
×
47
            selected_location_uid: -1,
×
48
            oblast_statuses: AirRaidAlertOblastStatuses::default(),
×
NEW
49
            alerts: Alerts::default(),
×
50
            //
×
51
            width: 0,
×
52
            height: 0,
×
53
            resolution: (0.0, 0.0),
×
54
        }
×
55
    }
×
56

57
    #[inline]
58
    pub fn set_grid_size(&mut self, width: u16, height: u16) {
×
59
        self.width = width;
×
60
        self.height = height;
×
61
        self.resolution = (f64::from(width) * 2.0, f64::from(height) * 4.0);
×
62
        debug!(target:"app", "Map grid size: width: {}, height: {}, x_Y_bounds: {:?}, resolution: {:?}", width, height, self.get_x_y_bounds(), self.resolution);
×
63
    }
×
64

65
    #[inline]
66
    pub fn get_location_by<P>(&self, mut predicate: P) -> Option<Location>
×
67
    where
×
68
        P: FnMut(&Location) -> bool,
×
69
    {
×
70
        self.locations.iter().find(|r| predicate(r)).cloned()
×
71
    }
×
72

73
    #[inline]
NEW
74
    pub fn get_selected_location(&self) -> Option<Location> {
×
NEW
75
        self.get_location_by(|l| l.location_uid == self.selected_location_uid)
×
NEW
76
    }
×
77

78
    #[inline]
NEW
79
    pub fn get_selected_alert_status(&self) -> Option<AirRaidAlertOblastStatus> {
×
NEW
80
        self.oblast_statuses
×
NEW
81
            .get_by_location_uid(self.selected_location_uid)
×
NEW
82
    }
×
83

84
    #[inline]
NEW
85
    pub fn get_selected_alert(&self) -> Option<Alert> {
×
NEW
86
        self.alerts
×
NEW
87
            .get_alerts_by_location_uid(self.selected_location_uid)
×
NEW
88
            .first()
×
NEW
89
            .cloned()
×
NEW
90
    }
×
91
}
92

93
impl WithPlacement<'_> for Map<'_> {
94
    #[inline]
95
    fn placement(&self) -> &LayoutPoint {
×
96
        &self.placement
×
97
    }
×
98
}
99

100
impl WithBoundingRect for Map<'_> {
101
    #[inline]
102
    fn bounding_rect(&self) -> geo::Rect {
×
103
        self.bounding_rect
×
104
    }
×
105
}
106

107
impl<'a> Component<'a> for Map<'a> {
UNCOV
108
    fn init(&mut self, r: Rect) -> Result<()> {
×
109
        self.set_grid_size(r.width, r.height);
×
110
        Ok(())
×
111
    }
×
112

113
    fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
×
114
        self.command_tx = Some(tx);
×
115
        Ok(())
×
116
    }
×
117

NEW
118
    fn register_config_handler(&mut self, config: Config) -> Result<()> {
×
NEW
119
        self.config = config;
×
NEW
120
        Ok(())
×
NEW
121
    }
×
122

123
    fn update(&mut self, action: Action) -> Result<Option<Action>> {
×
124
        match action {
×
125
            Action::Tick => {}
×
126
            Action::Resize(width, heith) => self.set_grid_size(width, heith),
×
127
            Action::GetBoundaries(boundary) => {
×
128
                self.boundary = boundary;
×
129
            }
×
130
            Action::GetLocations(locations) => {
×
131
                self.locations = locations;
×
132
            }
×
133
            Action::GetAirRaidAlertOblastStatuses(data) => {
×
134
                self.oblast_statuses = data;
×
135
            }
×
NEW
136
            Action::GetActiveAlerts(data) => {
×
NEW
137
                self.alerts = data;
×
NEW
138
            }
×
139
            Action::SelectLocationByUid(a) => match a {
×
140
                Some(location_uid) => {
×
141
                    self.selected_location_uid = location_uid as i32;
×
142
                    debug!(target:"app", "Map: selected_location_uid: {}", location_uid);
×
143
                }
144
                None => {
×
145
                    self.selected_location_uid = -1;
×
146
                }
×
147
            },
NEW
148
            Action::Online(online) => {
×
NEW
149
                self.title = get_title_with_online_status(
×
NEW
150
                    t!("views.Map.title"),
×
NEW
151
                    self.config.online(),
×
NEW
152
                )
×
NEW
153
                .alignment(Alignment::Left);
×
NEW
154
            }
×
UNCOV
155
            _ => {}
×
156
        }
157
        Ok(None)
×
158
    }
×
159

160
    fn draw(&mut self, f: &mut Frame) -> Result<()> {
×
161
        let size: Rect = f.size();
×
162
        let area: Rect = self.get_area(size)?;
×
163
        let (x_bounds, y_bounds) = self.get_x_y_bounds();
×
NEW
164
        let selected_location = self.get_selected_location();
×
NEW
165
        let selected_alert_status = self.get_selected_alert_status();
×
NEW
166
        let selected_alert = self.get_selected_alert();
×
NEW
167
        let title = self.title.clone();
×
168
        let widget = Canvas::default()
×
NEW
169
            .block(Block::bordered().title(title))
×
170
            .marker(Marker::Braille)
×
171
            .x_bounds(x_bounds)
×
172
            .y_bounds(y_bounds)
×
173
            .paint(move |ctx| {
×
174
                //  Draw country borders with ctx
×
175
                ctx.draw(&self.boundary);
×
176

×
177
                // Draw & Print selected location with ctx
×
178
                self.locations.iter().for_each(|l| {
×
179
                    // Draw location
×
180
                    ctx.draw(l);
×
181
                    // Print location name
×
182
                    let (x, y) = l.center();
×
183
                    let text = l
×
184
                        .get_name_by_locale(self.config.get_locale())
×
185
                        .split(' ')
×
186
                        .next()
×
187
                        .unwrap_or("");
×
188
                    let status: &AlertStatus = self
×
189
                        .oblast_statuses
×
190
                        .iter()
×
191
                        .find(|&os| os.location_uid == l.location_uid)
×
192
                        .unwrap()
×
193
                        .status();
×
194
                    let is_selected = (l.location_uid) == self.selected_location_uid;
×
NEW
195
                    let line = get_styled_line_icon_by_status(status, &is_selected);
×
196
                    ctx.print(x, y, line);
×
197
                });
×
198
            })
×
199
            .background_color(Color::Reset);
×
200
        f.render_widget(widget, area);
×
NEW
201

×
NEW
202
        let popup_area = get_bottom_left_rect(area, 30, 20);
×
NEW
203
        let mut popup_bg = Color::Reset;
×
NEW
204
        let mut lines: Vec<Line> = vec![
×
NEW
205
            "No details".into(),
×
NEW
206
            "To view details, select a location on the map using Up/Down arrow keys".into(),
×
NEW
207
            "↓: move down".into(),
×
NEW
208
            "↑: move up".into(),
×
NEW
209
        ];
×
210

211
        // popup
NEW
212
        if let Some(sas) = selected_alert_status {
×
NEW
213
            popup_bg = get_color_by_status(sas.status());
×
NEW
214
            lines = vec![
×
NEW
215
                sas.location_title_en().to_string().into(),
×
NEW
216
                sas.status().to_string().into(),
×
NEW
217
            ];
×
NEW
218
            if let Some(sa) = selected_alert {
×
NEW
219
                let d = dur::Duration::from_std(sa.get_alert_duration());
×
NEW
220
                lines = vec![
×
NEW
221
                    sas.location_title_en().to_string().into(),
×
NEW
222
                    sa.alert_type.to_string().into(),
×
NEW
223
                    d.to_string().into(),
×
NEW
224
                    sa.notes.unwrap_or_default().into(),
×
NEW
225
                ];
×
NEW
226
            };
×
NEW
227
        };
×
NEW
228
        let paragraph = Paragraph::new(Text::from(lines))
×
NEW
229
            .dark_gray()
×
NEW
230
            .alignment(Alignment::Left);
×
NEW
231
        let block = Block::bordered()
×
NEW
232
            .bg(popup_bg)
×
NEW
233
            .title("Alert Details:".white().bold().italic());
×
NEW
234
        f.render_widget(paragraph.block(block), popup_area);
×
235
        Ok(())
×
236
    }
×
237
}
238

239
/* #[cfg(test)]
240
mod tests {
241
    use super::*;
242
    use geo::HasDimensions;
243

244
    #[test]
245
    fn test_map_new() {
246
        let map = Map::new(Ukraine::new_arc(), Arc::new(Config::init().unwrap()));
247
        assert!(map.command_tx.is_none());
248
        assert!(!map.map.boundary().is_empty());
249
        assert!(map.ukraine.read().unwrap().locations().is_empty());
250
        // match map.boundary.try_from()
251
    }
252
} */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc