use crate::prelude::StringStorage;
use std::fmt;
use std::marker::PhantomData;
#[derive(Debug, Default, Clone, PartialEq)]
pub(crate) struct ExtensionsCore<SS: StringStorage, E> {
inner: Vec<E>,
_marker: PhantomData<SS>,
}
pub(crate) trait ExtensionItem<SS: StringStorage> {
fn name(&self) -> &SS::String;
}
impl<SS: StringStorage, E: ExtensionItem<SS>> ExtensionsCore<SS, E> {
#[must_use]
pub fn new() -> Self {
Self {
inner: Vec::new(),
_marker: PhantomData,
}
}
pub fn add(&mut self, extension: E) -> &mut Self {
if let Some(pos) = self.inner.iter().position(|e| e.name() == extension.name()) {
self.inner[pos] = extension;
} else {
self.inner.push(extension);
}
self
}
pub fn remove(&mut self, name: &SS::String) -> bool {
if let Some(pos) = self.inner.iter().position(|e| e.name() == name) {
self.inner.remove(pos);
true
} else {
false
}
}
#[must_use]
pub fn get(&self, name: &str) -> Option<&E> {
self.inner.iter().find(|e| e.name().as_ref() == name)
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, E> {
self.inner.iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, E> {
self.inner.iter_mut()
}
}
impl<SS: StringStorage, E: ExtensionItem<SS>> IntoIterator for ExtensionsCore<SS, E> {
type Item = E;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<'a, SS: StringStorage, E: ExtensionItem<SS>> IntoIterator for &'a ExtensionsCore<SS, E> {
type Item = &'a E;
type IntoIter = std::slice::Iter<'a, E>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, SS: StringStorage, E: ExtensionItem<SS>> IntoIterator for &'a mut ExtensionsCore<SS, E> {
type Item = &'a mut E;
type IntoIter = std::slice::IterMut<'a, E>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl<SS: StringStorage, E: ExtensionItem<SS>> fmt::Display for ExtensionsCore<SS, E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "available extensions: ")?;
let mut iter = self.into_iter();
if let Some(first) = iter.next() {
write!(f, "{}", first.name())?;
for ext in iter {
write!(f, ", {}", ext.name())?;
}
}
Ok(())
}
}
#[derive(Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub(crate) struct ExtensionCore<SS: StringStorage> {
name: SS::String,
url: SS::String,
version: SS::String,
}
impl<SS: StringStorage> ExtensionCore<SS> {
pub fn new(name: SS::String, url: SS::String, version: SS::String) -> Self {
Self { name, url, version }
}
pub fn name(&self) -> &SS::String {
&self.name
}
pub fn url(&self) -> &SS::String {
&self.url
}
pub fn version(&self) -> &SS::String {
&self.version
}
}
impl<SS: StringStorage> ExtensionItem<SS> for ExtensionCore<SS> {
fn name(&self) -> &SS::String {
&self.name
}
}
impl<SS: StringStorage> fmt::Display for ExtensionCore<SS> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"name: {}, url: {}, version: {}",
self.name, self.url, self.version
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::resources::storage::OwnedStringStorage;
#[test]
fn test_extension() {
let ext = ExtensionCore::<OwnedStringStorage>::new(
"noise".to_string(),
"https://example.com/noise/1.0".to_string(),
"1.0".to_string(),
);
assert_eq!(ext.name(), &"noise".to_string());
assert_eq!(ext.url(), &"https://example.com/noise/1.0".to_string());
assert_eq!(ext.version(), &"1.0".to_string());
assert_eq!(
format!("{ext}"),
"name: noise, url: https://example.com/noise/1.0, version: 1.0"
);
}
#[test]
fn test_extensions_add_get() {
let mut extensions =
ExtensionsCore::<OwnedStringStorage, ExtensionCore<OwnedStringStorage>>::new();
let noise_extension_v1 = ExtensionCore::new(
"noise".to_string(),
"https://example.com/noise/1.0".to_string(),
"1.0".to_string(),
);
extensions.add(noise_extension_v1.clone());
let found = extensions.get("noise").unwrap();
assert_eq!(found, &noise_extension_v1);
let noise_extension_v2 = ExtensionCore::new(
"noise".to_string(),
"https://example.com/noise/2.0".to_string(),
"2.0".to_string(),
);
extensions.add(noise_extension_v2.clone());
assert_eq!(extensions.len(), 1);
assert_eq!(extensions.get("noise").unwrap(), &noise_extension_v2);
}
#[test]
fn test_extensions_remove_empty() {
let mut exts =
ExtensionsCore::<OwnedStringStorage, ExtensionCore<OwnedStringStorage>>::new();
let ext = ExtensionCore::new(
"noise".to_string(),
"https://example.com/noise/1.0".to_string(),
"1.0".to_string(),
);
exts.add(ext);
assert!(!exts.remove(&"other".to_string()));
assert_eq!(exts.len(), 1);
assert!(!exts.is_empty());
assert!(exts.remove(&"noise".to_string()));
assert_eq!(exts.len(), 0);
assert!(exts.is_empty());
}
#[test]
fn test_extensions_iteration() {
let mut extensions =
ExtensionsCore::<OwnedStringStorage, ExtensionCore<OwnedStringStorage>>::new();
let noise_extension = ExtensionCore::new(
"noise".to_string(),
"https://example.com/noise/1.0".to_string(),
"1.0".to_string(),
);
let solar_extension = ExtensionCore::new(
"solar".to_string(),
"https://example.com/solar/1.0".to_string(),
"1.0".to_string(),
);
extensions.add(noise_extension.clone());
extensions.add(solar_extension.clone());
let mut count = 0;
for ext in &extensions {
assert!(ext == &noise_extension || ext == &solar_extension);
count += 1;
}
assert_eq!(count, 2);
for _ in &mut extensions {
}
let mut names = Vec::new();
for ext in extensions {
names.push(ext.name().clone());
}
assert_eq!(names.len(), 2);
assert!(names.contains(&"noise".to_string()));
assert!(names.contains(&"solar".to_string()));
}
}