use crate::events::{DDetectedEvent, EventAction, EventType, SDetectedEvent};
use crate::time::Epoch;
pub struct EventQuery<'a, I>
where
I: Iterator<Item = &'a DDetectedEvent>,
{
iter: I,
}
impl<'a, I> EventQuery<'a, I>
where
I: Iterator<Item = &'a DDetectedEvent> + 'a,
{
pub(crate) fn new(iter: I) -> Self {
Self { iter }
}
pub fn by_detector_index(
self,
index: usize,
) -> EventQuery<'a, impl Iterator<Item = &'a DDetectedEvent> + 'a> {
EventQuery::new(self.iter.filter(move |e| e.detector_index == index))
}
pub fn by_name_exact(
self,
name: &str,
) -> EventQuery<'a, impl Iterator<Item = &'a DDetectedEvent> + 'a> {
let name = name.to_string();
EventQuery::new(self.iter.filter(move |e| e.name == name))
}
pub fn by_name_contains(
self,
substring: &str,
) -> EventQuery<'a, impl Iterator<Item = &'a DDetectedEvent> + 'a> {
let substring = substring.to_string();
EventQuery::new(self.iter.filter(move |e| e.name.contains(&substring)))
}
pub fn in_time_range(
self,
start: Epoch,
end: Epoch,
) -> EventQuery<'a, impl Iterator<Item = &'a DDetectedEvent> + 'a> {
EventQuery::new(
self.iter
.filter(move |e| e.window_open >= start && e.window_open <= end),
)
}
pub fn after(
self,
epoch: Epoch,
) -> EventQuery<'a, impl Iterator<Item = &'a DDetectedEvent> + 'a> {
EventQuery::new(self.iter.filter(move |e| e.window_open >= epoch))
}
pub fn before(
self,
epoch: Epoch,
) -> EventQuery<'a, impl Iterator<Item = &'a DDetectedEvent> + 'a> {
EventQuery::new(self.iter.filter(move |e| e.window_open <= epoch))
}
pub fn by_event_type(
self,
event_type: EventType,
) -> EventQuery<'a, impl Iterator<Item = &'a DDetectedEvent> + 'a> {
EventQuery::new(self.iter.filter(move |e| e.event_type == event_type))
}
pub fn by_action(
self,
action: EventAction,
) -> EventQuery<'a, impl Iterator<Item = &'a DDetectedEvent> + 'a> {
EventQuery::new(self.iter.filter(move |e| e.action == action))
}
}
impl<'a, I> Iterator for EventQuery<'a, I>
where
I: Iterator<Item = &'a DDetectedEvent>,
{
type Item = &'a DDetectedEvent;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
pub struct SEventQuery<'a, const S: usize, I>
where
I: Iterator<Item = &'a SDetectedEvent<S>>,
{
iter: I,
}
impl<'a, const S: usize, I> SEventQuery<'a, S, I>
where
I: Iterator<Item = &'a SDetectedEvent<S>> + 'a,
{
pub fn new(iter: I) -> Self {
Self { iter }
}
pub fn by_detector_index(
self,
index: usize,
) -> SEventQuery<'a, S, impl Iterator<Item = &'a SDetectedEvent<S>> + 'a> {
SEventQuery::new(self.iter.filter(move |e| e.detector_index == index))
}
pub fn by_name_exact(
self,
name: &str,
) -> SEventQuery<'a, S, impl Iterator<Item = &'a SDetectedEvent<S>> + 'a> {
let name = name.to_string();
SEventQuery::new(self.iter.filter(move |e| e.name == name))
}
pub fn by_name_contains(
self,
substring: &str,
) -> SEventQuery<'a, S, impl Iterator<Item = &'a SDetectedEvent<S>> + 'a> {
let substring = substring.to_string();
SEventQuery::new(self.iter.filter(move |e| e.name.contains(&substring)))
}
pub fn in_time_range(
self,
start: Epoch,
end: Epoch,
) -> SEventQuery<'a, S, impl Iterator<Item = &'a SDetectedEvent<S>> + 'a> {
SEventQuery::new(
self.iter
.filter(move |e| e.window_open >= start && e.window_open <= end),
)
}
pub fn after(
self,
epoch: Epoch,
) -> SEventQuery<'a, S, impl Iterator<Item = &'a SDetectedEvent<S>> + 'a> {
SEventQuery::new(self.iter.filter(move |e| e.window_open >= epoch))
}
pub fn before(
self,
epoch: Epoch,
) -> SEventQuery<'a, S, impl Iterator<Item = &'a SDetectedEvent<S>> + 'a> {
SEventQuery::new(self.iter.filter(move |e| e.window_open <= epoch))
}
pub fn by_event_type(
self,
event_type: EventType,
) -> SEventQuery<'a, S, impl Iterator<Item = &'a SDetectedEvent<S>> + 'a> {
SEventQuery::new(self.iter.filter(move |e| e.event_type == event_type))
}
pub fn by_action(
self,
action: EventAction,
) -> SEventQuery<'a, S, impl Iterator<Item = &'a SDetectedEvent<S>> + 'a> {
SEventQuery::new(self.iter.filter(move |e| e.action == action))
}
}
impl<'a, const S: usize, I> Iterator for SEventQuery<'a, S, I>
where
I: Iterator<Item = &'a SDetectedEvent<S>>,
{
type Item = &'a SDetectedEvent<S>;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
#[cfg(test)]
#[allow(non_snake_case)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use crate::time::TimeSystem;
use nalgebra::DVector;
fn create_test_event(
name: &str,
detector_index: usize,
event_type: EventType,
action: EventAction,
time_offset_seconds: f64,
) -> DDetectedEvent {
let base_epoch = Epoch::from_jd(2451545.0, TimeSystem::UTC);
let event_epoch = base_epoch + time_offset_seconds;
let state = DVector::from_vec(vec![7000e3, 0.0, 0.0, 0.0, 7.5e3, 0.0]);
DDetectedEvent {
window_open: event_epoch,
window_close: event_epoch + 10.0,
entry_state: state.clone(),
exit_state: state,
value: 0.0,
name: name.to_string(),
action,
event_type,
detector_index,
}
}
#[test]
fn test_EventQuery_by_name_exact_matches() {
let events = [
create_test_event(
"Altitude Event",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
),
create_test_event(
"Perigee Event",
1,
EventType::Instantaneous,
EventAction::Continue,
100.0,
),
];
let result: Vec<_> = EventQuery::new(events.iter())
.by_name_exact("Altitude Event")
.collect();
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "Altitude Event");
}
#[test]
fn test_EventQuery_by_name_exact_no_match() {
let events = [create_test_event(
"Altitude Event",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
)];
let result: Vec<_> = EventQuery::new(events.iter())
.by_name_exact("Nonexistent Event")
.collect();
assert_eq!(result.len(), 0);
}
#[test]
fn test_EventQuery_by_name_exact_partial_no_match() {
let events = [create_test_event(
"Altitude Event",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
)];
let result: Vec<_> = EventQuery::new(events.iter())
.by_name_exact("Altitude")
.collect();
assert_eq!(result.len(), 0);
}
#[test]
fn test_EventQuery_after_includes_matching_epoch() {
let base_epoch = Epoch::from_jd(2451545.0, TimeSystem::UTC);
let events = [create_test_event(
"Event",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
)];
let result: Vec<_> = EventQuery::new(events.iter()).after(base_epoch).collect();
assert_eq!(result.len(), 1);
}
#[test]
fn test_EventQuery_after_includes_later_events() {
let base_epoch = Epoch::from_jd(2451545.0, TimeSystem::UTC);
let events = [
create_test_event(
"Early",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
),
create_test_event(
"Late",
1,
EventType::Instantaneous,
EventAction::Continue,
200.0,
),
];
let result: Vec<_> = EventQuery::new(events.iter())
.after(base_epoch + 100.0)
.collect();
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "Late");
}
#[test]
fn test_EventQuery_after_excludes_earlier_events() {
let base_epoch = Epoch::from_jd(2451545.0, TimeSystem::UTC);
let events = [
create_test_event(
"Early",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
),
create_test_event(
"Late",
1,
EventType::Instantaneous,
EventAction::Continue,
200.0,
),
];
let result: Vec<_> = EventQuery::new(events.iter())
.after(base_epoch + 300.0)
.collect();
assert_eq!(result.len(), 0);
}
#[test]
fn test_EventQuery_before_includes_matching_epoch() {
let base_epoch = Epoch::from_jd(2451545.0, TimeSystem::UTC);
let events = [create_test_event(
"Event",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
)];
let result: Vec<_> = EventQuery::new(events.iter()).before(base_epoch).collect();
assert_eq!(result.len(), 1);
}
#[test]
fn test_EventQuery_before_includes_earlier_events() {
let base_epoch = Epoch::from_jd(2451545.0, TimeSystem::UTC);
let events = [
create_test_event(
"Early",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
),
create_test_event(
"Late",
1,
EventType::Instantaneous,
EventAction::Continue,
200.0,
),
];
let result: Vec<_> = EventQuery::new(events.iter())
.before(base_epoch + 100.0)
.collect();
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "Early");
}
#[test]
fn test_EventQuery_before_excludes_later_events() {
let base_epoch = Epoch::from_jd(2451545.0, TimeSystem::UTC);
let events = [
create_test_event(
"Early",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
),
create_test_event(
"Late",
1,
EventType::Instantaneous,
EventAction::Continue,
200.0,
),
];
let result: Vec<_> = EventQuery::new(events.iter())
.before(base_epoch - 100.0)
.collect();
assert_eq!(result.len(), 0);
}
#[test]
fn test_EventQuery_by_event_type_instantaneous() {
let events = [
create_test_event(
"Instant",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
),
create_test_event("Window", 1, EventType::Window, EventAction::Continue, 100.0),
];
let result: Vec<_> = EventQuery::new(events.iter())
.by_event_type(EventType::Instantaneous)
.collect();
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "Instant");
assert_eq!(result[0].event_type, EventType::Instantaneous);
}
#[test]
fn test_EventQuery_by_event_type_window() {
let events = [
create_test_event(
"Instant",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
),
create_test_event("Window", 1, EventType::Window, EventAction::Continue, 100.0),
];
let result: Vec<_> = EventQuery::new(events.iter())
.by_event_type(EventType::Window)
.collect();
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "Window");
assert_eq!(result[0].event_type, EventType::Window);
}
#[test]
fn test_EventQuery_by_event_type_no_match() {
let events = [create_test_event(
"Instant",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
)];
let result: Vec<_> = EventQuery::new(events.iter())
.by_event_type(EventType::Window)
.collect();
assert_eq!(result.len(), 0);
}
#[test]
fn test_EventQuery_by_action_stop() {
let events = [
create_test_event(
"Continue Event",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
),
create_test_event(
"Stop Event",
1,
EventType::Instantaneous,
EventAction::Stop,
100.0,
),
];
let result: Vec<_> = EventQuery::new(events.iter())
.by_action(EventAction::Stop)
.collect();
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "Stop Event");
assert_eq!(result[0].action, EventAction::Stop);
}
#[test]
fn test_EventQuery_by_action_continue() {
let events = [
create_test_event(
"Continue Event",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
),
create_test_event(
"Stop Event",
1,
EventType::Instantaneous,
EventAction::Stop,
100.0,
),
];
let result: Vec<_> = EventQuery::new(events.iter())
.by_action(EventAction::Continue)
.collect();
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "Continue Event");
assert_eq!(result[0].action, EventAction::Continue);
}
#[test]
fn test_EventQuery_by_action_no_match() {
let events = [create_test_event(
"Continue Event",
0,
EventType::Instantaneous,
EventAction::Continue,
0.0,
)];
let result: Vec<_> = EventQuery::new(events.iter())
.by_action(EventAction::Stop)
.collect();
assert_eq!(result.len(), 0);
}
#[test]
fn test_EventQuery_combined_filters() {
let base_epoch = Epoch::from_jd(2451545.0, TimeSystem::UTC);
let events = [
create_test_event(
"Target Event",
0,
EventType::Window,
EventAction::Stop,
100.0,
),
create_test_event(
"Target Event",
1,
EventType::Instantaneous,
EventAction::Continue,
200.0,
),
create_test_event(
"Other Event",
2,
EventType::Window,
EventAction::Stop,
150.0,
),
create_test_event(
"Target Event",
3,
EventType::Window,
EventAction::Continue,
50.0,
),
];
let result: Vec<_> = EventQuery::new(events.iter())
.by_name_exact("Target Event")
.by_event_type(EventType::Window)
.by_action(EventAction::Stop)
.after(base_epoch + 75.0)
.collect();
assert_eq!(result.len(), 1);
assert_eq!(result[0].name, "Target Event");
assert_eq!(result[0].detector_index, 0);
assert_eq!(result[0].event_type, EventType::Window);
assert_eq!(result[0].action, EventAction::Stop);
}
}