gpu_async_reactor/core/
declared.rs1use alloc::format;
13use alloc::string::String;
14use alloc::vec::Vec;
15
16use crate::core::error::{Error, ErrorCode, Result};
17
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
20pub enum Strictness {
21 #[default]
23 Fallback,
24 Strict,
26}
27
28pub trait Declared: Copy + Eq + 'static {
30 type Caps;
32
33 fn name(self) -> &'static str;
35
36 fn lacks(self, caps: &Self::Caps) -> Option<String>;
38
39 fn fallback(self) -> Option<Self>;
41
42 const FALLBACK_CODE: ErrorCode;
44
45 const REFUSED_CODE: ErrorCode;
47}
48
49#[derive(Clone, Debug, PartialEq, Eq)]
51pub struct Resolution<K> {
52 pub requested: K,
54 pub running: K,
56 pub warning: Option<Error>,
58}
59
60impl<K: Declared> Resolution<K> {
61 #[must_use]
63 pub fn label(&self) -> String {
64 if self.running == self.requested {
65 String::from(self.running.name())
66 } else {
67 format!(
68 "{} (fallback from {})",
69 self.running.name(),
70 self.requested.name()
71 )
72 }
73 }
74}
75
76pub fn resolve<K: Declared>(
82 requested: K,
83 strictness: Strictness,
84 caps: &K::Caps,
85) -> Result<Resolution<K>> {
86 let mut running = requested;
87 let mut why: Vec<String> = Vec::new();
88 while let Some(reason) = running.lacks(caps) {
89 if strictness == Strictness::Strict {
90 return Err(Error::new(
91 K::REFUSED_CODE,
92 alloc::vec![String::from(requested.name()), reason],
93 ));
94 }
95 why.push(format!("{}: {reason}", running.name()));
96 match running.fallback() {
97 Some(next) => running = next,
98 None => break,
99 }
100 }
101 let warning = (running != requested).then(|| {
102 Error::new(
103 K::FALLBACK_CODE,
104 alloc::vec![
105 String::from(requested.name()),
106 why.join("; "),
107 String::from(running.name()),
108 ],
109 )
110 });
111 Ok(Resolution {
112 requested,
113 running,
114 warning,
115 })
116}
117
118#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
120pub enum ExecutorKind {
121 Naive,
124 GpuReactor,
127 IoUring,
129}
130
131#[derive(Clone, Debug, PartialEq, Eq)]
133pub struct ExecutorCaps {
134 pub word_wait: core::result::Result<(), String>,
136 pub io_uring: core::result::Result<(), String>,
138}
139
140impl ExecutorKind {
141 pub const ALL: [ExecutorKind; 3] = [Self::Naive, Self::GpuReactor, Self::IoUring];
143
144 pub fn parse(text: &str) -> Result<Self> {
149 Self::ALL
150 .into_iter()
151 .find(|k| k.name() == text)
152 .ok_or_else(|| {
153 let names: Vec<&str> = Self::ALL.iter().map(|k| k.name()).collect();
154 crate::error!(InvalidExecutor, text, names.join(", "))
155 })
156 }
157}
158
159impl Declared for ExecutorKind {
160 type Caps = ExecutorCaps;
161
162 fn name(self) -> &'static str {
163 match self {
164 Self::Naive => "naive",
165 Self::GpuReactor => "gpu-reactor",
166 Self::IoUring => "io-uring",
167 }
168 }
169
170 fn lacks(self, caps: &ExecutorCaps) -> Option<String> {
171 match self {
172 Self::Naive => None,
173 Self::GpuReactor => caps.word_wait.as_ref().err().cloned(),
174 Self::IoUring => caps
175 .io_uring
176 .as_ref()
177 .err()
178 .or(caps.word_wait.as_ref().err())
179 .cloned(),
180 }
181 }
182
183 fn fallback(self) -> Option<Self> {
184 match self {
185 Self::IoUring => Some(Self::GpuReactor),
186 Self::GpuReactor => Some(Self::Naive),
187 Self::Naive => None,
188 }
189 }
190
191 const FALLBACK_CODE: ErrorCode = ErrorCode::ExecutorFallback;
192 const REFUSED_CODE: ErrorCode = ErrorCode::ExecutorRefused;
193}
194
195#[cfg(test)]
196#[path = "declared_tests.rs"]
197mod tests;