1use std::sync::Arc;
19
20use thiserror::Error;
21
22#[derive(Clone, Debug, PartialEq, Eq, Error)]
24pub enum AttributeError {
25 #[error("input buffer initial and max must be nonzero")]
26 ZeroInputBuffer,
27 #[error("input buffer initial ({initial}) must be <= max ({max})")]
28 InputBufferMinExceedsMax { initial: usize, max: usize },
29 #[error("batch size must be greater than zero")]
30 ZeroBatchSize,
31 #[error("parallelism must be greater than zero")]
32 ZeroParallelism,
33}
34
35pub type AttributeResult<T> = Result<T, AttributeError>;
37
38#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
44pub enum Fusion {
45 #[default]
47 Auto,
48 TypedOnly,
50 ErasedOnly,
52}
53
54#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
59pub enum MetricsLevel {
60 #[default]
62 Off,
63 Counts,
65 Timing,
67 Stalls,
69}
70
71#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
76pub enum KernelBackend {
77 Auto,
79 #[default]
81 ArrowRust,
82 Python,
84}
85
86#[derive(Clone, Debug, PartialEq, Eq)]
88pub enum Attribute {
89 Name(Arc<str>),
91 InputBuffer { initial: usize, max: usize },
93 Dispatcher(Arc<str>),
95 BatchSize(usize),
97 Parallelism(usize),
99 Fusion(Fusion),
101 MetricsLevel(MetricsLevel),
103 KernelBackend(KernelBackend),
105}
106
107#[derive(Clone, Debug, Default, PartialEq, Eq)]
112pub struct Attributes {
113 attribute_list: Arc<[Attribute]>,
114}
115
116impl Attributes {
117 #[must_use]
119 pub fn none() -> Self {
120 Self::default()
121 }
122
123 #[must_use]
125 pub fn named(name: impl Into<String>) -> Self {
126 Self {
127 attribute_list: Arc::from([Attribute::Name(Arc::from(name.into()))]),
128 }
129 }
130
131 #[must_use]
133 pub fn input_buffer(initial: usize, max: usize) -> Self {
134 Self::try_input_buffer(initial, max).expect("invalid input buffer attribute")
135 }
136
137 pub fn try_input_buffer(initial: usize, max: usize) -> AttributeResult<Self> {
139 validate_input_buffer(initial, max)?;
140 Ok(Self {
141 attribute_list: Arc::from([Attribute::InputBuffer { initial, max }]),
142 })
143 }
144
145 #[must_use]
147 pub fn dispatcher(name: impl Into<String>) -> Self {
148 Self {
149 attribute_list: Arc::from([Attribute::Dispatcher(Arc::from(name.into()))]),
150 }
151 }
152
153 #[must_use]
158 pub fn batch_size(size: usize) -> Self {
159 Self::try_batch_size(size).expect("invalid batch size attribute")
160 }
161
162 pub fn try_batch_size(size: usize) -> AttributeResult<Self> {
164 if size == 0 {
165 return Err(AttributeError::ZeroBatchSize);
166 }
167 Ok(Self {
168 attribute_list: Arc::from([Attribute::BatchSize(size)]),
169 })
170 }
171
172 #[must_use]
177 pub fn parallelism(parallelism: usize) -> Self {
178 Self::try_parallelism(parallelism).expect("invalid parallelism attribute")
179 }
180
181 pub fn try_parallelism(parallelism: usize) -> AttributeResult<Self> {
183 if parallelism == 0 {
184 return Err(AttributeError::ZeroParallelism);
185 }
186 Ok(Self {
187 attribute_list: Arc::from([Attribute::Parallelism(parallelism)]),
188 })
189 }
190
191 #[must_use]
193 pub fn fusion(fusion: Fusion) -> Self {
194 Self {
195 attribute_list: Arc::from([Attribute::Fusion(fusion)]),
196 }
197 }
198
199 #[must_use]
201 pub fn metrics_level(level: MetricsLevel) -> Self {
202 Self {
203 attribute_list: Arc::from([Attribute::MetricsLevel(level)]),
204 }
205 }
206
207 #[must_use]
209 pub fn kernel_backend(backend: KernelBackend) -> Self {
210 Self {
211 attribute_list: Arc::from([Attribute::KernelBackend(backend)]),
212 }
213 }
214
215 #[must_use]
217 pub fn and(mut self, other: Self) -> Self {
218 if other.attribute_list.is_empty() {
219 return self;
220 }
221 if self.attribute_list.is_empty() {
222 return other;
223 }
224 let mut combined =
225 Vec::with_capacity(other.attribute_list.len() + self.attribute_list.len());
226 combined.extend(other.attribute_list.iter().cloned());
227 combined.extend(self.attribute_list.iter().cloned());
228 self.attribute_list = Arc::from(combined.into_boxed_slice());
229 self
230 }
231
232 #[must_use]
234 pub fn attribute_list(&self) -> &[Attribute] {
235 &self.attribute_list
236 }
237
238 pub fn validate(&self) -> AttributeResult<()> {
240 for attribute in self.attribute_list.iter() {
241 match attribute {
242 Attribute::InputBuffer { initial, max } => validate_input_buffer(*initial, *max)?,
243 Attribute::BatchSize(0) => return Err(AttributeError::ZeroBatchSize),
244 Attribute::Parallelism(0) => return Err(AttributeError::ZeroParallelism),
245 Attribute::Name(_)
246 | Attribute::Dispatcher(_)
247 | Attribute::BatchSize(_)
248 | Attribute::Parallelism(_)
249 | Attribute::Fusion(_)
250 | Attribute::MetricsLevel(_)
251 | Attribute::KernelBackend(_) => {}
252 }
253 }
254 Ok(())
255 }
256
257 #[must_use]
259 pub fn name(&self) -> Option<&str> {
260 self.attribute_list
261 .iter()
262 .find_map(|attribute| match attribute {
263 Attribute::Name(name) => Some(name.as_ref()),
264 _ => None,
265 })
266 }
267
268 #[must_use]
270 pub fn input_buffer_hint(&self) -> Option<(usize, usize)> {
271 self.attribute_list
272 .iter()
273 .find_map(|attribute| match attribute {
274 Attribute::InputBuffer { initial, max } => Some((*initial, *max)),
275 _ => None,
276 })
277 }
278
279 #[must_use]
281 pub fn dispatcher_hint(&self) -> Option<&str> {
282 self.attribute_list
283 .iter()
284 .find_map(|attribute| match attribute {
285 Attribute::Dispatcher(name) => Some(name.as_ref()),
286 _ => None,
287 })
288 }
289
290 #[must_use]
292 pub fn batch_size_hint(&self) -> Option<usize> {
293 self.attribute_list
294 .iter()
295 .find_map(|attribute| match attribute {
296 Attribute::BatchSize(size) => Some(*size),
297 _ => None,
298 })
299 }
300
301 #[must_use]
303 pub fn parallelism_hint(&self) -> Option<usize> {
304 self.attribute_list
305 .iter()
306 .find_map(|attribute| match attribute {
307 Attribute::Parallelism(parallelism) => Some(*parallelism),
308 _ => None,
309 })
310 }
311
312 #[must_use]
314 pub fn fusion_hint(&self) -> Option<Fusion> {
315 self.attribute_list
316 .iter()
317 .find_map(|attribute| match attribute {
318 Attribute::Fusion(fusion) => Some(*fusion),
319 _ => None,
320 })
321 }
322
323 #[must_use]
325 pub fn metrics_level_hint(&self) -> Option<MetricsLevel> {
326 self.attribute_list
327 .iter()
328 .find_map(|attribute| match attribute {
329 Attribute::MetricsLevel(level) => Some(*level),
330 _ => None,
331 })
332 }
333
334 #[must_use]
336 pub fn kernel_backend_hint(&self) -> Option<KernelBackend> {
337 self.attribute_list
338 .iter()
339 .find_map(|attribute| match attribute {
340 Attribute::KernelBackend(backend) => Some(*backend),
341 _ => None,
342 })
343 }
344
345 #[must_use]
347 pub fn with_name(mut self, name: impl Into<String>) -> Self {
348 let mut attributes = Vec::with_capacity(self.attribute_list.len() + 1);
349 attributes.push(Attribute::Name(Arc::from(name.into())));
350 attributes.extend(self.attribute_list.iter().cloned());
351 self.attribute_list = Arc::from(attributes.into_boxed_slice());
352 self
353 }
354}
355
356fn validate_input_buffer(initial: usize, max: usize) -> AttributeResult<()> {
357 if initial == 0 || max == 0 {
358 return Err(AttributeError::ZeroInputBuffer);
359 }
360 if initial > max {
361 return Err(AttributeError::InputBufferMinExceedsMax { initial, max });
362 }
363 Ok(())
364}