1use fidl_fuchsia_tracing_controller as trace;
6use futures::FutureExt;
7use std::collections::BTreeSet;
8use std::future::Future;
9use std::pin::Pin;
10use std::task::{Context, Poll};
11
12#[derive(Debug, Clone, Eq, PartialEq)]
13pub enum TriggerAction {
14 Terminate,
15}
16
17impl From<trace::Action> for TriggerAction {
18 fn from(t: trace::Action) -> Self {
19 match t {
20 trace::Action::Terminate => Self::Terminate,
21 _ => panic!("Unknown trigger action: {t:?}"),
22 }
23 }
24}
25
26#[derive(Debug, Clone)]
27pub struct Trigger {
28 pub alert: Option<String>,
30 pub action: Option<TriggerAction>,
31}
32
33impl From<trace::Trigger> for Trigger {
34 fn from(t: trace::Trigger) -> Self {
35 Self { alert: t.alert, action: t.action.map(Into::into) }
36 }
37}
38
39impl From<&trace::Trigger> for Trigger {
40 fn from(t: &trace::Trigger) -> Self {
41 Self { alert: t.alert.clone(), action: t.action.map(Into::into) }
42 }
43}
44#[derive(Debug, Clone, PartialEq, Eq)]
46struct TriggerSetItem {
47 alert: String,
48 action: TriggerAction,
49}
50
51impl TriggerSetItem {
52 fn new(t: Trigger) -> Option<Self> {
53 let alert = t.alert?;
54 let action = t.action?;
55 Some(Self { alert, action })
56 }
57
58 fn lookup(alert: String) -> Self {
60 Self { alert: alert, action: TriggerAction::Terminate }
61 }
62}
63
64impl std::cmp::PartialOrd for TriggerSetItem {
65 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
66 Some(self.cmp(other))
67 }
68}
69
70impl std::cmp::Ord for TriggerSetItem {
71 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
72 self.alert.cmp(&other.alert)
73 }
74}
75
76type TriggersFut<'a> = Pin<Box<dyn Future<Output = Option<TriggerAction>> + Send + 'a>>;
77
78pub struct TriggersWatcher<'a> {
79 inner: TriggersFut<'a>,
80}
81
82impl<'a> TriggersWatcher<'a> {
83 pub fn new(
84 controller: trace::SessionProxy,
85 triggers: Vec<Trigger>,
86 shutdown: async_channel::Receiver<()>,
87 ) -> Self {
88 Self {
89 inner: Box::pin(async move {
90 let items: Vec<_> =
91 triggers.into_iter().filter_map(|i| TriggerSetItem::new(i)).collect();
92 let set: BTreeSet<TriggerSetItem> = items.iter().map(|t| t).cloned().collect();
93 let mut shutdown_fut = shutdown.recv().fuse();
94 loop {
95 let mut watch_alert = controller.watch_alert().fuse();
96 futures::select! {
97 _ = shutdown_fut => {
98 log::info!("received shutdown alert");
99 break;
100 }
101 alert = watch_alert => {
102 let Ok(alert) = alert else { break };
103 log::info!("alert received: {}", alert);
104 let lookup_item = TriggerSetItem::lookup(alert);
105 if set.contains(&lookup_item) {
106 return set.get(&lookup_item).map(|s| s.action.clone());
107 }
108 }
109 }
110 }
111 None
112 }),
113 }
114 }
115}
116
117impl Future for TriggersWatcher<'_> {
118 type Output = Option<TriggerAction>;
119
120 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
121 Pin::new(&mut self.inner).poll(cx)
122 }
123}