Skip to main content

a7n_core/focus/
responder.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3use std::time::Instant;
4
5use a7n_proto::limits;
6use a7n_proto::v1::{
7    Accept, Focus as WireFocus, Handover, InputEvent, Landing, Refuse, RefuseReason, envelope::Body,
8};
9use a7n_proto::{MachineId, Route};
10
11use super::{Action, Driven, Focus};
12
13impl Focus {
14    /// Process a Focus from an authenticated peer. Policy is supplied per message.
15    pub fn on_focus(
16        &mut self,
17        peer: MachineId,
18        focus: &WireFocus,
19        allow_drive: bool,
20        now: Instant,
21    ) -> Vec<Action> {
22        if self
23            .accepted
24            .get(&peer)
25            .is_some_and(|route| focus.route <= route.0)
26        {
27            return Vec::new();
28        }
29        let Ok(target) = MachineId::try_from(focus.target.as_slice()) else {
30            return vec![Self::refuse(
31                peer,
32                focus.route,
33                RefuseReason::Malformed,
34                None,
35            )];
36        };
37        let Ok(reach) = focus
38            .reach
39            .iter()
40            .map(|id| MachineId::try_from(id.as_slice()))
41            .collect::<Result<Vec<_>, _>>()
42        else {
43            return vec![Self::refuse(
44                peer,
45                focus.route,
46                RefuseReason::Malformed,
47                None,
48            )];
49        };
50        if target != self.own_id {
51            // Remember even a let-go arriving before its take, so delayed takes stay stale.
52            self.accepted.insert(peer, Route(focus.route));
53            let mut actions = Vec::new();
54            if self
55                .driven
56                .as_ref()
57                .is_some_and(|driven| driven.owner == peer)
58            {
59                self.end_driven(&mut actions);
60            }
61            return actions;
62        }
63        if !allow_drive {
64            return vec![Self::refuse(
65                peer,
66                focus.route,
67                RefuseReason::NotAllowed,
68                None,
69            )];
70        }
71        self.accept_take(peer, focus, reach, now)
72    }
73
74    fn accept_take(
75        &mut self,
76        peer: MachineId,
77        focus: &WireFocus,
78        reach: Vec<MachineId>,
79        now: Instant,
80    ) -> Vec<Action> {
81        let mut actions = Vec::new();
82        if let Some(driven) = &self.driven {
83            if driven.owner != peer {
84                if now.saturating_duration_since(driven.last_input) < limits::IDLE_TAKEOVER {
85                    return vec![Self::refuse(
86                        peer,
87                        focus.route,
88                        RefuseReason::Owned,
89                        Some(driven.owner),
90                    )];
91                }
92                self.take_over(peer, &mut actions);
93            }
94        } else if self
95            .last_local_input
96            .is_some_and(|last| now.saturating_duration_since(last) < limits::IDLE_TAKEOVER)
97        {
98            return vec![Self::refuse(
99                peer,
100                focus.route,
101                RefuseReason::Busy,
102                Some(self.own_id),
103            )];
104        }
105        actions.extend(self.go_home());
106        self.accepted.insert(peer, Route(focus.route));
107        self.driven = Some(Driven {
108            owner: peer,
109            route: Route(focus.route),
110            last_input: now,
111            reach,
112        });
113        actions.push(Self::send(
114            peer,
115            Body::Accept(Accept { route: focus.route }),
116        ));
117        actions.push(Action::Driven {
118            owner: peer,
119            landing: if focus.stay {
120                None
121            } else {
122                focus.landing.clone()
123            },
124            resistance_px: focus.resistance_px,
125        });
126        actions
127    }
128
129    /// Call for an accepted `InputBatch` or `Motion`, before injecting input.
130    /// Rejected routes never update the idle timer. This path does not allocate.
131    pub fn on_remote_input(&mut self, peer: MachineId, route: Route, now: Instant) -> bool {
132        let Some(driven) = self.driven.as_mut() else {
133            return false;
134        };
135        if driven.owner != peer || driven.route != route {
136            return false;
137        }
138        driven.last_input = now;
139        true
140    }
141
142    /// Whether to inject a reliable event after route and held-input checks.
143    /// Excess downs beyond the held-input cap are dropped before injection.
144    pub fn on_input_event(
145        &mut self,
146        peer: MachineId,
147        route: Route,
148        event: &InputEvent,
149        now: Instant,
150    ) -> bool {
151        if !self.on_remote_input(peer, route, now) {
152            return false;
153        }
154        self.held.apply(event)
155    }
156
157    /// Whether to apply physical input, followed by effects of a local takeover.
158    /// An active remote owner suppresses local input without refreshing local activity.
159    pub fn on_local_input(&mut self, now: Instant) -> (bool, Vec<Action>) {
160        let mut actions = Vec::new();
161        if let Some(driven) = &self.driven {
162            if now.saturating_duration_since(driven.last_input) < limits::IDLE_TAKEOVER {
163                return (false, actions);
164            }
165            self.take_over(self.own_id, &mut actions);
166        }
167        self.last_local_input = Some(now);
168        (true, actions)
169    }
170
171    /// Request a reachable peer (or the owner's home); ownership ends on let-go.
172    #[must_use]
173    pub fn handover(&self, next: MachineId, landing: Option<Landing>) -> Vec<Action> {
174        let Some(driven) = &self.driven else {
175            return Vec::new();
176        };
177        if next != driven.owner && !driven.reach.contains(&next) {
178            return Vec::new();
179        }
180        vec![Self::send(
181            driven.owner,
182            Body::Handover(Handover {
183                route: driven.route.0,
184                next: next.as_bytes().to_vec(),
185                landing,
186                taken_over_by: Vec::new(),
187            }),
188        )]
189    }
190
191    /// End this peer's ownership immediately when local policy is revoked.
192    pub fn deny_drive(&mut self, peer: MachineId) -> Vec<Action> {
193        let mut actions = Vec::new();
194        if self
195            .driven
196            .as_ref()
197            .is_some_and(|driven| driven.owner == peer)
198        {
199            self.end_driven(&mut actions);
200        }
201        actions
202    }
203
204    pub(super) fn end_driven(&mut self, actions: &mut Vec<Action>) {
205        self.driven = None;
206        let released = self.held.release_all();
207        if !released.is_empty() {
208            actions.push(Action::Release(released));
209        }
210        actions.push(Action::EndedDriven);
211    }
212
213    fn take_over(&mut self, replacement: MachineId, actions: &mut Vec<Action>) {
214        let Some(driven) = self.driven.take() else {
215            return;
216        };
217        self.end_driven(actions);
218        actions.push(Self::send(
219            driven.owner,
220            Body::Handover(Handover {
221                route: driven.route.0,
222                next: driven.owner.as_bytes().to_vec(),
223                landing: None,
224                taken_over_by: replacement.as_bytes().to_vec(),
225            }),
226        ));
227    }
228
229    fn refuse(
230        peer: MachineId,
231        route: u64,
232        reason: RefuseReason,
233        other: Option<MachineId>,
234    ) -> Action {
235        Self::send(
236            peer,
237            Body::Refuse(Refuse {
238                route,
239                reason: reason.into(),
240                other: other.map_or_else(Vec::new, |id| id.as_bytes().to_vec()),
241            }),
242        )
243    }
244}