datum/attributes.rs
1//! Akka-style stream/stage metadata.
2//!
3//! [`Attributes`] is a small, immutable, cloneable bag of [`Attribute`]s attached to a stream
4//! blueprint or a graph stage — a name, an input-buffer hint, or a dispatcher hint. Attributes
5//! carry intent and introspection (they are surfaced to user `setup` closures and the graph
6//! builder, and combined down a nested blueprint); they do not by themselves drive execution.
7//!
8//! Combine two bags with [`Attributes::and`]: the **argument** takes precedence over the receiver,
9//! so the most-recently-applied (most specific) value wins the accessor lookups
10//! ([`Attributes::name`], [`Attributes::input_buffer_hint`], [`Attributes::dispatcher_hint`]).
11//!
12//! Mirrors `akka.stream.Attributes`; `Attribute`/`Attributes` are re-exported from the crate root.
13
14use std::sync::Arc;
15
16/// A single piece of stream/stage metadata.
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub enum Attribute {
19 /// Human-readable stage/stream name (used in diagnostics).
20 Name(Arc<str>),
21 /// Requested input-buffer sizing hint (`initial` ≤ `max`).
22 InputBuffer { initial: usize, max: usize },
23 /// Dispatcher name hint for where the stage's work should run.
24 Dispatcher(Arc<str>),
25}
26
27/// An ordered, immutable collection of [`Attribute`]s.
28///
29/// Lookups return the first matching attribute; [`Attributes::and`] prepends the argument's
30/// attributes so the more specific bag wins. Construction never starts any work.
31#[derive(Clone, Debug, Default, PartialEq, Eq)]
32pub struct Attributes {
33 attribute_list: Vec<Attribute>,
34}
35
36impl Attributes {
37 /// An empty attribute set (the `Default`).
38 #[must_use]
39 pub fn none() -> Self {
40 Self::default()
41 }
42
43 /// A set holding a single [`Attribute::Name`].
44 #[must_use]
45 pub fn named(name: impl Into<String>) -> Self {
46 Self {
47 attribute_list: vec![Attribute::Name(Arc::from(name.into()))],
48 }
49 }
50
51 /// A set holding a single [`Attribute::InputBuffer`] hint.
52 #[must_use]
53 pub fn input_buffer(initial: usize, max: usize) -> Self {
54 Self {
55 attribute_list: vec![Attribute::InputBuffer { initial, max }],
56 }
57 }
58
59 /// A set holding a single [`Attribute::Dispatcher`] hint.
60 #[must_use]
61 pub fn dispatcher(name: impl Into<String>) -> Self {
62 Self {
63 attribute_list: vec![Attribute::Dispatcher(Arc::from(name.into()))],
64 }
65 }
66
67 /// Combine two sets; the attributes of `other` take precedence on accessor lookups.
68 #[must_use]
69 pub fn and(mut self, other: Self) -> Self {
70 if other.attribute_list.is_empty() {
71 return self;
72 }
73 if self.attribute_list.is_empty() {
74 return other;
75 }
76 let mut combined = other.attribute_list;
77 combined.extend(self.attribute_list);
78 self.attribute_list = combined;
79 self
80 }
81
82 /// The underlying attributes, in lookup order (most specific first).
83 #[must_use]
84 pub fn attribute_list(&self) -> &[Attribute] {
85 &self.attribute_list
86 }
87
88 /// The first [`Attribute::Name`], if any.
89 #[must_use]
90 pub fn name(&self) -> Option<&str> {
91 self.attribute_list
92 .iter()
93 .find_map(|attribute| match attribute {
94 Attribute::Name(name) => Some(name.as_ref()),
95 _ => None,
96 })
97 }
98
99 /// The first [`Attribute::InputBuffer`] hint as `(initial, max)`, if any.
100 #[must_use]
101 pub fn input_buffer_hint(&self) -> Option<(usize, usize)> {
102 self.attribute_list
103 .iter()
104 .find_map(|attribute| match attribute {
105 Attribute::InputBuffer { initial, max } => Some((*initial, *max)),
106 _ => None,
107 })
108 }
109
110 /// The first [`Attribute::Dispatcher`] hint, if any.
111 #[must_use]
112 pub fn dispatcher_hint(&self) -> Option<&str> {
113 self.attribute_list
114 .iter()
115 .find_map(|attribute| match attribute {
116 Attribute::Dispatcher(name) => Some(name.as_ref()),
117 _ => None,
118 })
119 }
120
121 /// Return a copy with a [`Attribute::Name`] prepended so it wins [`Attributes::name`].
122 #[must_use]
123 pub fn with_name(mut self, name: impl Into<String>) -> Self {
124 self.attribute_list
125 .insert(0, Attribute::Name(Arc::from(name.into())));
126 self
127 }
128}