1use std::{ops::ControlFlow, pin, time};
2
3use dope_core::driver::schedule::clock::Sample;
4use drive::{block, clock, serve, turn};
5use o3::base::{cell::region, time::Positive};
6use turn::scope;
7
8use crate::{
9 client,
10 executor::{self, drain, drive, root, session},
11 topology::lending,
12};
13
14pub struct Access<
16 'app,
17 'life: 'app,
18 'd: 'life,
19 D,
20 Q = (),
21 Plan = <D as executor::raw::Layout<'d>>::ParkPlan,
22> {
23 pub(crate) core: &'app mut session::Core<'life, 'd, Q>,
24 view: executor::raw::View<'app, 'd, D, executor::raw::Live, Plan>,
25}
26
27impl<'app, 'life: 'app, 'd: 'life, D, Q, Plan> Access<'app, 'life, 'd, D, Q, Plan> {
28 pub(crate) fn reborrow(&mut self) -> Access<'_, 'life, 'd, D, Q, Plan> {
29 Access {
30 core: &mut *self.core,
31 view: self.view,
32 }
33 }
34
35 pub fn state_token(&mut self) -> &mut region::Token<region::Identity<'d, 1>> {
36 &mut self.core.scope.context().into_regions().1
37 }
38
39 pub fn root_client(&mut self) -> <D::Provider as client::Provider<'d>>::Client<'app>
41 where
42 D: lending::Root<'d>,
43 D::Provider: 'app,
44 {
45 self.client(|client| client)
46 }
47
48 pub fn client<P>(
49 &mut self,
50 select: impl for<'borrow> FnOnce(pin::Pin<&'borrow D>) -> pin::Pin<&'borrow P>,
51 ) -> <P::Provider as client::Provider<'d>>::Client<'app>
52 where
53 P: lending::Root<'d>,
54 P::Provider: 'app,
55 {
56 let root = select(self.view.app(self.core.scope.token()));
57 let provider = lending::Root::provider(root);
58 client::Provider::provide(client::Context::new(provider))
59 }
60
61 pub fn client_with<P, C>(
62 &mut self,
63 select: impl for<'borrow> FnOnce(pin::Pin<&'borrow D>) -> pin::Pin<&'borrow P>,
64 issue: impl for<'borrow> FnOnce(client::Context<'borrow, 'app, 'd, P>) -> C,
65 ) -> C {
66 let provider = select(self.view.app(self.core.scope.token()));
67 issue(client::Context::new(provider))
68 }
69
70 pub fn inspect<P, C>(
71 &mut self,
72 select: impl for<'borrow> FnOnce(pin::Pin<&'borrow D>) -> pin::Pin<&'borrow P>,
73 inspect: impl for<'borrow> FnOnce(pin::Pin<&'borrow P>) -> C,
74 ) -> C {
75 inspect(select(self.view.app(self.core.scope.token())))
76 }
77}
78
79impl<'app, 'life: 'app, 'd: 'life, D, Q, Plan> Access<'app, 'life, 'd, D, Q, Plan>
80where
81 D: executor::raw::App<'d, ParkPlan = Plan>,
82 Q: executor::Startup,
83{
84 pub(super) const fn new(
85 core: &'app mut session::Core<'life, 'd, Q>,
86 view: executor::raw::View<'app, 'd, D, executor::raw::Live, Plan>,
87 ) -> Self {
88 Self { core, view }
89 }
90
91 pub(super) fn drive<R, C>(
92 self,
93 source: C,
94 root: R,
95 tick: impl Fn(C) -> Result<Sample, drive::Error>,
96 ) -> Result<R::Output, drive::Error>
97 where
98 R: root::Root<'d>,
99 C: clock::Time,
100 {
101 let shutdown = turn::shutdown(self.core.lifecycle);
102 self.drive_with(source, root, tick, shutdown)
103 }
104
105 pub(crate) fn drive_with<R, C>(
106 self,
107 source: C,
108 root: R,
109 tick: impl Fn(C) -> Result<Sample, drive::Error>,
110 shutdown: impl FnMut() -> turn::Status,
111 ) -> Result<R::Output, drive::Error>
112 where
113 R: root::Root<'d>,
114 C: clock::Time,
115 {
116 self.with(|state| {
117 block::Block::new(state.driver_ref(), state, source)?.run(root, tick, shutdown)
118 })
119 }
120
121 pub(super) fn drive_fixed<R, C>(
122 self,
123 source: C,
124 root: R,
125 tick: impl Fn(C) -> Result<Sample, drive::Error>,
126 ) -> Result<session::Fixed<'d, R::Output>, drive::Error>
127 where
128 R: root::Root<'d>,
129 C: clock::Time,
130 {
131 let shutdown = turn::shutdown(self.core.lifecycle);
132 self.with(|state| {
133 block::Block::new(state.driver_ref(), state, source)?.run_fixed(root, tick, shutdown)
134 })
135 }
136
137 pub(super) fn run(self) -> Result<(), drive::Error> {
138 let shutdown = turn::shutdown(self.core.lifecycle);
139 self.with(|state| serve::run(state, clock::wall(), drive::tick, shutdown))
140 }
141
142 pub(super) fn run_for(self, timeout: time::Duration) -> Result<(), drive::Error> {
143 let source = clock::Bound::until(clock::wall(), timeout)?;
144 let shutdown = turn::shutdown(self.core.lifecycle);
145 self.with(|state| serve::run(state, source, drive::timeout, shutdown))
146 }
147
148 pub(super) fn drive_until<R>(self, root: R) -> Result<ControlFlow<R::Output>, drive::Error>
149 where
150 R: root::Root<'d>,
151 {
152 let shutdown = turn::shutdown(self.core.lifecycle);
153 self.with(|state| {
154 block::Block::new(state.driver_ref(), state, clock::wall())?.run_until(
155 root,
156 drive::tick,
157 shutdown,
158 )
159 })
160 }
161
162 pub(super) fn run_drain(mut self, timeout: Positive) -> Result<drain::Outcome, drive::Error>
163 where
164 D: executor::raw::Drain<'d>,
165 {
166 self.reborrow().run()?;
167 self.drain(timeout)
168 }
169
170 pub(super) fn drive_drain<R>(
171 mut self,
172 timeout: Positive,
173 root: R,
174 ) -> Result<ControlFlow<R::Output, drain::Outcome>, drive::Error>
175 where
176 D: executor::raw::Drain<'d>,
177 R: root::Root<'d>,
178 {
179 match self.reborrow().drive_until(root)? {
180 ControlFlow::Break(output) => Ok(ControlFlow::Break(output)),
181 ControlFlow::Continue(()) => self.drain(timeout).map(ControlFlow::Continue),
182 }
183 }
184
185 fn drain(self, timeout: Positive) -> Result<drain::Outcome, drive::Error>
186 where
187 D: executor::raw::Drain<'d>,
188 {
189 let source = clock::Bound::until(clock::wall(), timeout.get())?;
190 let shutdown = turn::shutdown(self.core.lifecycle);
191 self.with(|state| drain::run(state, source, shutdown))
192 }
193
194 fn with<T>(
195 self,
196 run: impl FnOnce(scope::Scope<'_, '_, 'app, 'd, D, executor::raw::Live>) -> T,
197 ) -> T {
198 let view = self.view;
199 let core = &mut *self.core;
200 let pending = &mut core.pending;
201 let (token, mut driver, turn) = core.scope.split();
202 run(scope::Scope::new(&mut driver, view, token, turn, pending))
203 }
204}