use std::collections::BTreeMap;
use std::sync::{Arc, Weak};
use itertools::Itertools;
use derive_more::Display;
use crate::Extension;
use crate::extension::{ExtensionId, ExtensionRegistry, Version, semver_compatible};
#[derive(Debug, Display, Default, Clone)]
#[display("WeakExtensionRegistry[{}]", exts.keys().join(", "))]
pub struct WeakExtensionRegistry {
exts: BTreeMap<ExtensionId, WeakExtensionVersions>,
}
#[derive(Debug, Clone)]
pub struct WeakExtensionVersions {
id: ExtensionId,
versions: BTreeMap<Version, Weak<Extension>>,
}
impl WeakExtensionVersions {
fn new(id: ExtensionId, version: Version, ext: Weak<Extension>) -> Self {
Self {
id,
versions: [(version, ext)].into(),
}
}
#[must_use]
fn latest(&self) -> (&Version, &Weak<Extension>) {
self.versions
.last_key_value()
.expect("WeakExtensionVersions always contains at least one extension")
}
#[must_use]
fn get_compatible(&self, requested: &Version) -> Option<(&Version, &Weak<Extension>)> {
let (version, extension) = self.versions.range(requested..).next()?;
semver_compatible(version, requested).then_some((version, extension))
}
#[must_use]
fn get_req(&self, requested: Option<&Version>) -> Option<(&Version, &Weak<Extension>)> {
match requested {
Some(version) => self.get_compatible(version),
None => Some(self.latest()),
}
}
fn register(&mut self, version: Version, ext: Weak<Extension>) -> bool {
#[cfg(debug_assertions)]
{
let extension = ext
.upgrade()
.expect("weak extension ARC must be upgradeable when registered");
assert_eq!(
extension.name(),
&self.id,
"extension id does not match WeakExtensionVersions id"
);
drop(extension);
}
let compatible_version = self.compatible_registered_version(&version);
let Some(compatible_version) = compatible_version else {
self.versions.insert(version, ext);
return true;
};
if compatible_version > version {
return false;
}
self.versions.remove(&compatible_version);
self.versions.insert(version, ext);
true
}
fn compatible_registered_version(&self, version: &Version) -> Option<Version> {
if let Some((candidate, _)) = self.versions.range(..=version).next_back()
&& semver_compatible(version, candidate)
{
return Some(candidate.clone());
}
self.versions
.range(version..)
.next()
.filter(|(candidate, _)| semver_compatible(candidate, version))
.map(|(candidate, _)| candidate.clone())
}
fn iter(&self) -> impl Iterator<Item = (&Version, &Weak<Extension>)> {
self.versions.iter()
}
fn into_iter(self) -> impl Iterator<Item = (Version, Weak<Extension>)> {
self.versions.into_iter()
}
}
impl WeakExtensionRegistry {
pub fn new(
extensions: impl IntoIterator<Item = (ExtensionId, Version, Weak<Extension>)>,
) -> Self {
let mut res = Self::default();
for (id, version, ext) in extensions {
res.register(id, version, ext);
}
res
}
#[must_use]
pub fn get_latest(&self, name: &str) -> Option<(&Version, &Weak<Extension>)> {
self.exts.get(name).map(WeakExtensionVersions::latest)
}
#[must_use]
pub fn get_req(
&self,
name: &str,
version: Option<&Version>,
) -> Option<(&Version, &Weak<Extension>)> {
self.exts.get(name).and_then(|ext| ext.get_req(version))
}
#[must_use]
pub fn contains(&self, name: &str) -> bool {
self.exts.contains_key(name)
}
pub fn register(
&mut self,
id: ExtensionId,
version: Version,
ext: impl Into<Weak<Extension>>,
) -> bool {
let ext = ext.into();
match self.exts.entry(id.clone()) {
std::collections::btree_map::Entry::Occupied(mut occupied) => {
occupied.get_mut().register(version, ext)
}
std::collections::btree_map::Entry::Vacant(vacant) => {
vacant.insert(WeakExtensionVersions::new(id, version, ext));
true
}
}
}
pub fn iter(&self) -> impl Iterator<Item = (&ExtensionId, &Version, &Weak<Extension>)> {
self.exts.iter().map(|(id, versions)| {
let (version, ext) = versions.latest();
(id, version, ext)
})
}
pub fn iter_all(&self) -> impl Iterator<Item = (&ExtensionId, &Version, &Weak<Extension>)> {
self.exts.iter().flat_map(|(id, versions)| {
versions
.iter()
.map(move |(version, ext)| (id, version, ext))
})
}
pub fn extensions(&self) -> impl Iterator<Item = (&Version, &Weak<Extension>)> {
self.exts.values().map(WeakExtensionVersions::latest)
}
pub fn all_extensions(
&self,
) -> impl Iterator<Item = (&ExtensionId, &Version, &Weak<Extension>)> {
self.exts.values().flat_map(|versions| {
versions
.iter()
.map(move |(version, ext)| (&versions.id, version, ext))
})
}
pub fn ids(&self) -> impl Iterator<Item = &ExtensionId> {
self.exts.keys()
}
}
impl IntoIterator for WeakExtensionRegistry {
type Item = (ExtensionId, Version, Weak<Extension>);
type IntoIter = std::vec::IntoIter<(ExtensionId, Version, Weak<Extension>)>;
fn into_iter(self) -> Self::IntoIter {
self.exts
.into_iter()
.flat_map(|(id, ext_versions)| {
ext_versions
.into_iter()
.map(move |(version, ext)| (id.clone(), version, ext))
})
.collect_vec()
.into_iter()
}
}
impl<'a> TryFrom<&'a WeakExtensionRegistry> for ExtensionRegistry {
type Error = ();
fn try_from(weak: &'a WeakExtensionRegistry) -> Result<Self, Self::Error> {
let exts: Vec<Arc<Extension>> = weak
.all_extensions()
.map(|(_, _, w)| w.upgrade().ok_or(()))
.try_collect()?;
Ok(ExtensionRegistry::new(exts))
}
}
impl TryFrom<WeakExtensionRegistry> for ExtensionRegistry {
type Error = ();
fn try_from(weak: WeakExtensionRegistry) -> Result<Self, Self::Error> {
let exts: Vec<Arc<Extension>> = weak
.into_iter()
.map(|(_, _, w)| w.upgrade().ok_or(()))
.try_collect()?;
Ok(ExtensionRegistry::new(exts))
}
}
impl<'a> From<&'a ExtensionRegistry> for WeakExtensionRegistry {
fn from(reg: &'a ExtensionRegistry) -> Self {
Self::new(reg.iter_all().map(|ext| {
(
ext.name().clone(),
ext.version().clone(),
Arc::downgrade(ext),
)
}))
}
}