use std::fmt;
use std::sync::Arc;
use once_cell::sync::OnceCell;
use vortex_error::VortexResult;
use vortex_error::vortex_err;
use crate::plan::PlanRef;
type ChildInitializer = dyn Fn(usize) -> VortexResult<PlanRef> + 'static + Send + Sync;
#[derive(Clone)]
pub struct PlanChildren {
initializer: Option<Arc<ChildInitializer>>,
cache: Arc<[OnceCell<PlanRef>]>,
}
impl PlanChildren {
pub(crate) fn lazy(
len: usize,
initializer: impl Fn(usize) -> VortexResult<PlanRef> + 'static + Send + Sync,
) -> Self {
Self {
initializer: Some(Arc::new(initializer)),
cache: (0..len).map(|_| OnceCell::new()).collect::<Vec<_>>().into(),
}
}
pub fn len(&self) -> usize {
self.cache.len()
}
pub fn is_empty(&self) -> bool {
self.cache.is_empty()
}
pub fn get(&self, index: usize) -> VortexResult<Option<PlanRef>> {
let Some(cell) = self.cache.get(index) else {
return Ok(None);
};
if let Some(child) = cell.get() {
return Ok(Some(child.clone()));
}
let initializer = self
.initializer
.as_ref()
.ok_or_else(|| vortex_err!("Plan child {index} was not initialized"))?;
Ok(Some(cell.get_or_try_init(|| initializer(index))?.clone()))
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = VortexResult<PlanRef>> + '_ {
(0..self.len()).map(|index| {
self.get(index)?
.ok_or_else(|| vortex_err!("Plan child {index} is absent"))
})
}
pub fn to_vec(&self) -> VortexResult<Vec<PlanRef>> {
self.iter().collect()
}
}
impl From<Vec<PlanRef>> for PlanChildren {
fn from(children: Vec<PlanRef>) -> Self {
let cache = children
.into_iter()
.map(OnceCell::with_value)
.collect::<Vec<_>>()
.into();
Self {
initializer: None,
cache,
}
}
}
impl<const N: usize> From<[PlanRef; N]> for PlanChildren {
fn from(children: [PlanRef; N]) -> Self {
Vec::from(children).into()
}
}
impl Default for PlanChildren {
fn default() -> Self {
Vec::new().into()
}
}
impl fmt::Debug for PlanChildren {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("PlanChildren")
.field("len", &self.len())
.field(
"initialized",
&self
.cache
.iter()
.filter(|slot| slot.get().is_some())
.count(),
)
.finish()
}
}