Skip to main content

a7n_core/focus/
owner.rs

1// SPDX-License-Identifier: GPL-3.0-or-later
2
3use std::time::Instant;
4
5use a7n_proto::MachineId;
6use a7n_proto::limits;
7use a7n_proto::v1::{Accept, Ack, Handover, Refuse, RefuseReason, envelope::Body};
8
9use super::{Action, Cursor, Focus, Owner, Session, Take};
10
11impl Focus {
12    /// Start a direct take. A pending take ignores further takes; home always works.
13    pub fn take(&mut self, take: Take, now: Instant) -> Vec<Action> {
14        if take.target == self.own_id {
15            return self.go_home();
16        }
17        if matches!(self.owner, Owner::Taking { .. }) || self.driven.is_some() {
18            return Vec::new();
19        }
20        let Some(route) = self.next_route() else {
21            return self.go_home();
22        };
23        let session = Session { take, route };
24        let action = Self::send(
25            session.take.target,
26            Body::Focus(Self::focus_message(&session, session.take.target, false)),
27        );
28        let previous = match std::mem::replace(&mut self.owner, Owner::Home) {
29            Owner::Driving(previous) => Some(previous),
30            _ => None,
31        };
32        self.owner = Owner::Taking {
33            session,
34            previous,
35            deadline: now + limits::TAKE_TIMEOUT,
36        };
37        vec![action]
38    }
39
40    pub fn on_accept(&mut self, peer: MachineId, accept: &Accept) -> Vec<Action> {
41        if !self.pending_matches(peer, accept.route) {
42            return Vec::new();
43        }
44        let Owner::Taking {
45            session, previous, ..
46        } = std::mem::replace(&mut self.owner, Owner::Home)
47        else {
48            return Vec::new();
49        };
50        let mut actions = Vec::new();
51        if let Some(previous) = previous.filter(|p| p.take.target != peer) {
52            self.let_go(previous.take.target, peer, &mut actions);
53        }
54        self.owner = Owner::Driving(session);
55        actions.push(Action::Cursor(Cursor::Driving(peer)));
56        actions
57    }
58
59    pub fn on_refuse(&mut self, peer: MachineId, refuse: &Refuse) -> Vec<Action> {
60        if !self.pending_matches(peer, refuse.route) {
61            return Vec::new();
62        }
63        match RefuseReason::try_from(refuse.reason) {
64            // SENT_HOME is not produced in v1; defensively return home.
65            Ok(RefuseReason::SentHome) => self.go_home(),
66            _ => self.restore_previous(),
67        }
68    }
69
70    fn restore_previous(&mut self) -> Vec<Action> {
71        let Owner::Taking { previous, .. } = std::mem::replace(&mut self.owner, Owner::Home) else {
72            return Vec::new();
73        };
74        let Some(mut previous) = previous else {
75            return vec![Action::Cursor(Cursor::Home)];
76        };
77        let Some(route) = self.next_route() else {
78            self.owner = Owner::Driving(previous);
79            return self.go_home();
80        };
81        previous.route = route;
82        let target = previous.take.target;
83        let action = Self::send(
84            target,
85            Body::Focus(Self::focus_message(&previous, target, true)),
86        );
87        self.owner = Owner::Driving(previous);
88        vec![action, Action::Cursor(Cursor::Driving(target))]
89    }
90
91    pub fn poll(&mut self, now: Instant) -> Vec<Action> {
92        if self.next_deadline().is_some_and(|deadline| now >= deadline) {
93            return self.go_home();
94        }
95        Vec::new()
96    }
97
98    /// Return home and cancel both halves of a pending transition.
99    pub fn go_home(&mut self) -> Vec<Action> {
100        let owner = std::mem::replace(&mut self.owner, Owner::Home);
101        let mut actions = Vec::new();
102        match owner {
103            Owner::Home => return actions,
104            Owner::Driving(session) => self.let_go(session.take.target, self.own_id, &mut actions),
105            Owner::Taking {
106                session, previous, ..
107            } => {
108                if let Some(previous) = previous.filter(|p| p.take.target != session.take.target) {
109                    self.let_go(previous.take.target, self.own_id, &mut actions);
110                }
111                self.let_go(session.take.target, self.own_id, &mut actions);
112            }
113        }
114        actions.push(Action::Cursor(Cursor::Home));
115        actions
116    }
117
118    pub fn on_handover(
119        &mut self,
120        peer: MachineId,
121        handover: &Handover,
122        now: Instant,
123    ) -> Vec<Action> {
124        let Owner::Driving(session) = &self.owner else {
125            return Vec::new();
126        };
127        if session.take.target != peer || session.route.0 != handover.route {
128            return Vec::new();
129        }
130        let Ok(next) = MachineId::try_from(handover.next.as_slice()) else {
131            return Vec::new();
132        };
133        if !handover.taken_over_by.is_empty() {
134            if next != self.own_id
135                || MachineId::try_from(handover.taken_over_by.as_slice()).is_err()
136            {
137                return Vec::new();
138            }
139            self.owner = Owner::Home;
140            return vec![Action::Cursor(Cursor::Home)];
141        }
142        if self.is_locked() {
143            return Vec::new();
144        }
145        if next == self.own_id {
146            return self.go_home();
147        }
148        if !session.take.reach.contains(&next) {
149            return Vec::new();
150        }
151        let take = Take {
152            target: next,
153            landing: handover.landing.clone(),
154            ..session.take.clone()
155        };
156        self.take(take, now)
157    }
158
159    pub fn on_ack(&mut self, peer: MachineId, ack: &Ack) -> Vec<Action> {
160        self.on_accepts(peer, !ack.locked)
161    }
162
163    /// A policy denial cancels both an active drive and a pending take.
164    pub fn on_accepts(&mut self, peer: MachineId, accepts: bool) -> Vec<Action> {
165        let is_target = match &self.owner {
166            Owner::Home => false,
167            Owner::Driving(session) => session.take.target == peer,
168            Owner::Taking {
169                session, previous, ..
170            } => {
171                session.take.target == peer
172                    || previous.as_ref().is_some_and(|p| p.take.target == peer)
173            }
174        };
175        if !accepts && is_target {
176            self.go_home()
177        } else {
178            Vec::new()
179        }
180    }
181
182    /// End ownership, pending takes and route history for this link.
183    /// The daemon calls this whenever a link ends or is replaced by a newer
184    /// link to the same peer.
185    pub fn on_link_lost(&mut self, peer: MachineId) -> Vec<Action> {
186        self.accepted.remove(&peer);
187        let mut actions = Vec::new();
188        if self
189            .driven
190            .as_ref()
191            .is_some_and(|driven| driven.owner == peer)
192        {
193            self.end_driven(&mut actions);
194        }
195        let involved = match &self.owner {
196            Owner::Home => false,
197            Owner::Driving(session) => session.take.target == peer,
198            Owner::Taking {
199                session, previous, ..
200            } => {
201                session.take.target == peer
202                    || previous.as_ref().is_some_and(|p| p.take.target == peer)
203            }
204        };
205        if involved {
206            actions.extend(self.go_home());
207            actions.retain(|action| !matches!(action, Action::Send { peer: destination, .. } if *destination == peer));
208        }
209        actions
210    }
211
212    fn pending_matches(&self, peer: MachineId, route: u64) -> bool {
213        matches!(&self.owner, Owner::Taking { session, .. } if session.take.target == peer && session.route.0 == route)
214    }
215
216    fn let_go(&mut self, peer: MachineId, target: MachineId, actions: &mut Vec<Action>) {
217        if let Some(route) = self.next_route() {
218            actions.push(Self::send(
219                peer,
220                Body::Focus(a7n_proto::v1::Focus {
221                    route: route.0,
222                    target: target.as_bytes().to_vec(),
223                    ..Default::default()
224                }),
225            ));
226        }
227    }
228}