use crate::{
prelude::*,
unsize::{
impls::{ListIter, ListLength, UnsizedGenerics},
FromOwned,
},
};
use std::collections::BTreeSet;
#[unsized_type(skip_idl, owned_type = BTreeSet<T>, owned_from_ptr = unsized_set_owned_from_ptr::<T, L>, skip_init_struct)]
pub struct Set<T, L = u32>
where
T: UnsizedGenerics + Ord,
L: ListLength,
{
#[unsized_start]
list: List<T, L>,
}
impl<T, L> FromOwned for Set<T, L>
where
T: UnsizedGenerics + Ord,
L: ListLength,
{
fn byte_size(owned: &Self::Owned) -> usize {
List::<T, L>::byte_size_from_len(owned.len())
}
fn from_owned(owned: Self::Owned, bytes: &mut &mut [u8]) -> Result<usize> {
List::<T, L>::from_owned_from_iter(owned, bytes)
}
}
#[allow(clippy::unnecessary_wraps)]
fn unsized_set_owned_from_ptr<T, L>(r: &Set<T, L>) -> Result<BTreeSet<T>>
where
T: UnsizedGenerics + Ord,
L: ListLength,
{
Ok(r.list.iter().copied().collect())
}
impl<T, L> Set<T, L>
where
T: UnsizedGenerics + Ord,
L: ListLength,
{
#[must_use]
pub fn len(&self) -> usize {
self.list.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.list.is_empty()
}
#[must_use]
pub fn contains(&self, value: &T) -> bool {
self.list.binary_search(value).is_ok()
}
#[must_use]
pub fn get_by_index(&self, index: usize) -> Option<&T> {
self.list.get(index)
}
#[must_use]
pub fn iter(&self) -> ListIter<'_, T, L> {
self.list.iter()
}
}
#[unsized_impl]
impl<T, L> Set<T, L>
where
T: UnsizedGenerics + Ord,
L: ListLength,
{
pub fn insert_all(&mut self, values: impl IntoIterator<Item = T>) -> Result<usize> {
let mut count = 0;
values.into_iter().try_for_each(|value| {
if self.insert(value)? {
count += 1;
}
crate::Ok(())
})?;
Ok(count)
}
pub fn insert(&mut self, value: T) -> Result<bool> {
match self.list.binary_search(&value) {
Ok(_existing_index) => Ok(false),
Err(insertion_index) => {
self.list().insert(insertion_index, value)?;
Ok(true)
}
}
}
pub fn remove(&mut self, value: &T) -> Result<bool> {
match self.list.binary_search(value) {
Ok(existing_index) => {
self.list().remove(existing_index)?;
Ok(true)
}
Err(_) => Ok(false),
}
}
pub fn clear(&mut self) -> Result<()> {
self.list().remove_range(..)?;
Ok(())
}
}
impl<'a, T, L> IntoIterator for &'a Set<T, L>
where
T: UnsizedGenerics + Ord,
L: ListLength,
{
type Item = &'a T;
type IntoIter = ListIter<'a, T, L>;
fn into_iter(self) -> Self::IntoIter {
self.list.into_iter()
}
}
#[cfg(all(feature = "idl", not(target_os = "solana")))]
mod idl_impl {
use super::*;
use crate::idl::TypeToIdl;
use star_frame_idl::{ty::IdlTypeDef, IdlDefinition};
impl<T, L> TypeToIdl for Set<T, L>
where
T: UnsizedGenerics + TypeToIdl + Ord,
L: ListLength + TypeToIdl,
{
type AssociatedProgram = System;
fn type_to_idl(idl_definition: &mut IdlDefinition) -> crate::IdlResult<IdlTypeDef> {
Ok(IdlTypeDef::Set {
len_ty: L::type_to_idl(idl_definition)?.into(),
item_ty: T::type_to_idl(idl_definition)?.into(),
})
}
}
}