sim_lib_stream_core/
buffer.rs1use sim_kernel::{Error, Expr, Result, Symbol};
15use sim_value::access;
16pub(crate) use sim_value::kind::expr_kind;
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum BackpressureOutcome {
21 Accepted,
23 DroppedNewest,
25 DroppedOldest,
28 Blocked,
30 TimedOut,
32 Rejected,
34 Closed,
36}
37
38impl BackpressureOutcome {
39 pub fn wire_label(self) -> &'static str {
41 match self {
42 Self::Accepted => "accepted",
43 Self::DroppedNewest => "dropped-newest",
44 Self::DroppedOldest => "dropped-oldest",
45 Self::Blocked => "blocked",
46 Self::TimedOut => "timed-out",
47 Self::Rejected => "rejected",
48 Self::Closed => "closed",
49 }
50 }
51
52 pub fn symbol(self) -> Symbol {
54 Symbol::qualified("stream/backpressure", self.wire_label())
55 }
56
57 pub fn from_symbol(symbol: &Symbol) -> Result<Self> {
62 match symbol.as_qualified_str().as_str() {
63 "accepted" | "stream/backpressure/accepted" => Ok(Self::Accepted),
64 "dropped-newest" | "stream/backpressure/dropped-newest" => Ok(Self::DroppedNewest),
65 "dropped-oldest" | "stream/backpressure/dropped-oldest" => Ok(Self::DroppedOldest),
66 "blocked" | "stream/backpressure/blocked" => Ok(Self::Blocked),
67 "timed-out" | "stream/backpressure/timed-out" => Ok(Self::TimedOut),
68 "rejected" | "stream/backpressure/rejected" => Ok(Self::Rejected),
69 "closed" | "stream/backpressure/closed" => Ok(Self::Closed),
70 other => Err(Error::Eval(format!(
71 "unknown stream backpressure outcome {other}"
72 ))),
73 }
74 }
75}
76
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
79pub enum BufferOverflowPolicy {
80 DropNewest,
82 DropOldest,
84 Error,
86}
87
88impl BufferOverflowPolicy {
89 pub fn symbol(self) -> Symbol {
91 match self {
92 Self::DropNewest => Symbol::qualified("stream/overflow", "drop-newest"),
93 Self::DropOldest => Symbol::qualified("stream/overflow", "drop-oldest"),
94 Self::Error => Symbol::qualified("stream/overflow", "error"),
95 }
96 }
97
98 pub fn from_symbol(symbol: &Symbol) -> Result<Self> {
102 match symbol.as_qualified_str().as_str() {
103 "stream/overflow/drop-newest" => Ok(Self::DropNewest),
104 "stream/overflow/drop-oldest" => Ok(Self::DropOldest),
105 "stream/overflow/error" => Ok(Self::Error),
106 other => Err(Error::Eval(format!(
107 "unknown stream buffer overflow policy {other}"
108 ))),
109 }
110 }
111}
112
113#[derive(Clone, Debug, PartialEq, Eq)]
130pub struct BufferPolicy {
131 capacity: usize,
132 overflow: BufferOverflowPolicy,
133}
134
135impl BufferPolicy {
136 pub fn bounded(capacity: usize) -> Result<Self> {
141 Self::bounded_with_overflow(capacity, BufferOverflowPolicy::DropNewest)
142 }
143
144 pub fn bounded_with_overflow(capacity: usize, overflow: BufferOverflowPolicy) -> Result<Self> {
148 if capacity == 0 {
149 return Err(Error::Eval(
150 "stream buffer capacity must be greater than zero".to_owned(),
151 ));
152 }
153 Ok(Self { capacity, overflow })
154 }
155
156 pub fn capacity(&self) -> usize {
158 self.capacity
159 }
160
161 pub fn overflow(&self) -> BufferOverflowPolicy {
163 self.overflow
164 }
165
166 pub fn symbol(&self) -> Symbol {
168 Symbol::qualified("stream/buffer", format!("bounded-{}", self.capacity))
169 }
170
171 pub fn to_expr(&self) -> Expr {
174 Expr::Map(vec![
175 (
176 Expr::Symbol(Symbol::new("capacity")),
177 Expr::String(self.capacity.to_string()),
178 ),
179 (
180 Expr::Symbol(Symbol::new("overflow")),
181 Expr::Symbol(self.overflow.symbol()),
182 ),
183 ])
184 }
185
186 pub fn from_expr(expr: &Expr) -> Result<Self> {
192 let Expr::Map(entries) = expr else {
193 return Err(Error::TypeMismatch {
194 expected: "stream buffer policy map",
195 found: expr_kind(expr),
196 });
197 };
198 let capacity = string_field(entries, "capacity")?
199 .parse::<usize>()
200 .map_err(|err| Error::Eval(format!("invalid stream buffer capacity: {err}")))?;
201 let overflow = BufferOverflowPolicy::from_symbol(symbol_field(entries, "overflow")?)?;
202 Self::bounded_with_overflow(capacity, overflow)
203 }
204}
205
206pub(crate) fn string_field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Result<&'a str> {
207 access::entry_required_str(entries, name, "string field")
208}
209
210pub(crate) fn symbol_field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Result<&'a Symbol> {
211 access::entry_required_sym(entries, name, "symbol field")
212}
213
214pub(crate) fn field<'a>(entries: &'a [(Expr, Expr)], name: &str) -> Result<&'a Expr> {
215 entries
216 .iter()
217 .find_map(|(key, value)| match key {
218 Expr::Symbol(symbol) if symbol.namespace.is_none() && symbol.name.as_ref() == name => {
219 Some(value)
220 }
221 _ => None,
222 })
223 .ok_or_else(|| Error::Eval(format!("stream value missing {name} field")))
224}