use std::any::Any;
use std::collections::BTreeMap;
use std::sync::Arc;
use parking_lot::RwLock;
use vortex_session::ArcSwapMap;
use vortex_session::SessionExt;
use vortex_session::SessionGuard;
use vortex_session::SessionVar;
use vortex_session::registry::Id;
use crate::ComponentKind;
use crate::Edition;
use crate::EditionDeclaration;
use crate::EditionError;
use crate::EditionId;
use crate::EditionInclusion;
use crate::parse_release;
#[derive(Clone, Debug, Default)]
pub struct EditionSession {
inner: Arc<RwLock<Inner>>,
}
#[derive(Debug, Default)]
struct Inner {
editions: BTreeMap<String, Edition>,
inclusions: BTreeMap<ComponentKind, BTreeMap<Id, EditionInclusion>>,
}
type EditionsByFamily = ArcSwapMap<Id, EditionId>;
#[derive(Clone, Debug, Default)]
pub struct EnabledEditions {
inner: EditionsByFamily,
}
impl EnabledEditions {
pub fn editions(&self) -> Vec<EditionId> {
self.inner.read(|map| map.values().copied().collect())
}
fn enable(&self, edition: EditionId) {
self.inner.insert(Id::from(edition.family), edition);
}
}
impl EditionSession {
pub fn empty() -> Self {
Self {
inner: Arc::new(RwLock::new(Inner::default())),
}
}
pub fn declare(&self, declaration: &EditionDeclaration) -> Result<(), EditionError> {
self.declare_edition(declaration.edition)?;
for member in declaration.added {
self.declare_inclusion(EditionInclusion::new(
member.kind,
member.component,
declaration.edition.id,
))?;
}
Ok(())
}
pub fn declare_edition(&self, edition: Edition) -> Result<(), EditionError> {
let mut inner = self.inner.write();
let key = edition.id.to_string();
if inner.editions.contains_key(&key) {
return Err(EditionError::new(format!("duplicate edition {key}")));
}
inner.editions.insert(key, edition);
Ok(())
}
pub fn declare_inclusion(&self, inclusion: EditionInclusion) -> Result<(), EditionError> {
let mut inner = self.inner.write();
let by_id = inner.inclusions.entry(inclusion.kind).or_default();
if by_id.contains_key(&inclusion.component_id) {
return Err(EditionError::new(format!(
"duplicate edition inclusion for {} {}",
inclusion.kind, inclusion.component_id
)));
}
by_id.insert(inclusion.component_id, inclusion);
Ok(())
}
pub fn editions(&self) -> Vec<Edition> {
let mut editions: Vec<Edition> = self.inner.read().editions.values().copied().collect();
editions.sort_by_key(|e| (e.id.family, e.id.year, e.id.month, e.id.version));
editions
}
pub fn find(&self, id: &EditionId) -> Option<Edition> {
self.inner.read().editions.get(&id.to_string()).copied()
}
pub fn current(&self, family: &str) -> Option<Edition> {
self.editions()
.into_iter()
.rfind(|e| e.id.family == family && !e.is_draft())
}
pub fn components_in(&self, edition: &EditionId, kind: ComponentKind) -> Vec<EditionInclusion> {
let inner = self.inner.read();
let Some(by_id) = inner.inclusions.get(&kind) else {
return vec![];
};
by_id
.values()
.filter(|inclusion| inclusion.since.is_at_or_before(edition))
.copied()
.collect()
}
pub fn validate(&self) -> Result<(), EditionError> {
let editions = self.editions();
for edition in &editions {
edition.id.validate()?;
if let Some(version) = edition.min_vortex_version
&& parse_release(version).is_none()
{
return Err(EditionError::new(format!(
"edition {} declares malformed min_vortex_version {version:?}",
edition.id
)));
}
}
for pair in editions.windows(2) {
let (prev, next) = (&pair[0], &pair[1]);
if prev.id.family == next.id.family && prev.is_draft() && !next.is_draft() {
return Err(EditionError::new(format!(
"frozen edition {} follows draft {}; drafts must be newest in a family",
next.id, prev.id,
)));
}
}
let inner = self.inner.read();
for inclusion in inner.inclusions.values().flat_map(|by_id| by_id.values()) {
inclusion.validate()?;
let Some(edition) = inner.editions.get(&inclusion.since.to_string()) else {
return Err(EditionError::new(format!(
"{} {} is included in undeclared edition {}",
inclusion.kind, inclusion.component_id, inclusion.since
)));
};
if let Some(required) = inclusion.required_vortex_release.and_then(parse_release)
&& let Some(declared) = edition.min_vortex_version.and_then(parse_release)
&& required > declared
{
return Err(EditionError::new(format!(
"{} {} requires release {}, newer than edition {}'s declared \
min_vortex_version",
inclusion.kind,
inclusion.component_id,
inclusion.required_vortex_release.unwrap_or_default(),
edition.id,
)));
}
}
Ok(())
}
}
impl SessionVar for EditionSession {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl SessionVar for EnabledEditions {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
pub trait EditionSessionExt: SessionExt {
fn editions(&self) -> SessionGuard<'_, EditionSession> {
self.get::<EditionSession>()
}
fn enabled_editions(&self) -> SessionGuard<'_, EnabledEditions> {
self.get::<EnabledEditions>()
}
fn register_edition(&self, declaration: &EditionDeclaration) -> Result<(), EditionError> {
self.editions().declare(declaration)
}
fn enable_edition(&self, edition: EditionId) -> Result<(), EditionError> {
if self.editions().find(&edition).is_none() {
return Err(EditionError::new(format!(
"cannot enable unregistered edition {edition}"
)));
}
self.enabled_editions().enable(edition);
Ok(())
}
fn enabled_component_ids(&self, kind: ComponentKind) -> Vec<Id> {
let Some(enabled) = self.get_opt::<EnabledEditions>() else {
return vec![];
};
let editions = self.editions();
let mut ids: Vec<Id> = enabled
.editions()
.iter()
.flat_map(|edition| editions.components_in(edition, kind))
.map(|inclusion| inclusion.component_id)
.collect();
ids.sort_unstable();
ids.dedup();
ids
}
}
impl<S: SessionExt> EditionSessionExt for S {}