use alloc::format;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::sync::atomic::{AtomicU32, Ordering};
use crate::core::error::Result;
use crate::error;
use crate::core::plan::{HostCaps, Resolved, WaitPlan};
pub const BANDS_MAX: usize = 4;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Band<P> {
pub from_rows: u32,
pub plan: P,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Bands<P> {
bands: Vec<Band<P>>,
}
pub type WaitBands = Bands<WaitPlan>;
impl<P> Bands<P> {
#[must_use]
pub fn select(&self, rows: u32) -> &P {
let i = self.bands.partition_point(|b| b.from_rows <= rows);
&self.bands[i - 1].plan
}
pub fn iter(&self) -> impl Iterator<Item = &Band<P>> {
self.bands.iter()
}
#[must_use]
pub fn map<Q>(&self, mut f: impl FnMut(&P) -> Q) -> Bands<Q> {
Bands {
bands: self
.bands
.iter()
.map(|b| Band {
from_rows: b.from_rows,
plan: f(&b.plan),
})
.collect(),
}
}
fn label_with(&self, f: impl Fn(&P) -> String) -> String {
let mut out = String::new();
for (i, b) in self.bands.iter().enumerate() {
if i > 0 {
out.push_str(&format!(";rows>={}:", b.from_rows));
}
out.push_str(&f(&b.plan));
}
out
}
}
impl WaitBands {
pub fn parse(text: &str) -> Result<Self> {
let refused = |why: String| {
error!(InvalidPlan, "completion wait plan", format!("{text:?}: {why}"))
};
let mut bands: Vec<Band<WaitPlan>> = Vec::new();
for (i, part) in text.split(';').enumerate() {
if i == BANDS_MAX {
return Err(refused(format!("more than {BANDS_MAX} bands")));
}
let (from_rows, plan) = if i == 0 {
(0, part)
} else {
let (edge, plan) = part
.strip_prefix("rows>=")
.and_then(|p| p.split_once(':'))
.ok_or_else(|| refused(format!("band {i}: expected rows>=<n>:<plan>")))?;
let edge: u32 = edge
.parse()
.map_err(|_| refused(format!("band {i}: {edge:?} is not a u32")))?;
(edge, plan)
};
if let Some(prev) = bands.last()
&& from_rows <= prev.from_rows
{
return Err(refused(format!(
"band {i}: rows>={from_rows} does not exceed the previous edge {}",
prev.from_rows
)));
}
bands.push(Band {
from_rows,
plan: WaitPlan::parse(plan)?,
});
}
Ok(Self { bands })
}
#[must_use]
pub fn label(&self) -> String {
self.label_with(WaitPlan::label)
}
#[must_use]
pub fn resolve(&self, caps: &HostCaps) -> Bands<Resolved> {
self.map(|p| p.resolve(caps))
}
}
impl Bands<Resolved> {
#[must_use]
pub fn label(&self) -> String {
self.label_with(Resolved::label)
}
}
#[derive(Clone, Debug, Default)]
pub struct StepRows(Arc<AtomicU32>);
impl StepRows {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn publish(&self, rows: u32) {
self.0.store(rows, Ordering::Relaxed);
}
#[must_use]
pub fn current(&self) -> u32 {
self.0.load(Ordering::Relaxed)
}
}
#[cfg(test)]
#[path = "bands_tests.rs"]
mod tests;