From d691a10106a4f5604758cba116212c13316d06c9 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sun, 10 Dec 2023 17:40:50 +0100 Subject: [PATCH 01/24] WIP --- Cargo.toml | 1 + README.md | 2 +- src/lib.rs | 9 +- src/vec.rs | 681 +++++++++++++++++++++++++++++++++---------------- src/vec_of.rs | 2 +- tests/tests.rs | 2 +- 6 files changed, 472 insertions(+), 225 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ed3ce7a..cfd57c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ categories = ["data-structures"] [features] serde = ["dep:serde"] id_prim = [] +is_id_vec_of = [] [dependencies] serde = { version = "1.0.193", optional = true } diff --git a/README.md b/README.md index 8bc8f18..607452f 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ You can create an identified vec with any element type that implements the `Iden ```rust extern crate identified_vec; -use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf}; +use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; use std::cell::RefCell; #[derive(Eq, PartialEq, Clone, Debug)] diff --git a/src/lib.rs b/src/lib.rs index 5c7c2a2..4dfc4d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ //! //! ``` //! extern crate identified_vec; -//! use identified_vec::{IdentifiedVec, IdentifiedVecOf, Identifiable}; +//! use identified_vec::{IsIdentifiableVec, IdentifiedVec, IdentifiedVecOf, Identifiable}; //! use std::cell::RefCell; //! //! #[derive(Eq, PartialEq, Clone, Debug)] @@ -123,13 +123,14 @@ //! //! ``` //! extern crate identified_vec; -//! use identified_vec::{IdentifiedVec, IdentifiedVecOf, Identifiable}; +//! use identified_vec::{IsIdentifiableVec, IdentifiedVec, IdentifiedVecOf, Identifiable}; //! //! // closure which plucks out an ID from an element. //! let numbers = IdentifiedVec::::new_identifying_element(|e| *e); //! ``` mod identifiable_trait; +mod is_id_vec_of; mod primitives_identifiable; mod serde_error; mod vec; @@ -154,7 +155,11 @@ pub mod identified_vec_of { #[cfg(feature = "serde")] pub use crate::serde_error::*; + + #[cfg(feature = "is_id_vec_of")] + pub use crate::is_id_vec_of::*; } pub use crate::identified_vec::*; pub use crate::identified_vec_of::*; +pub use crate::vec::IsIdentifiableVec; diff --git a/src/vec.rs b/src/vec.rs index b9d4ab8..38c0808 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -2,8 +2,259 @@ use std::collections::HashMap; use std::fmt::{Debug, Display}; use std::hash::{Hash, Hasher}; +pub trait IsIdentifiableVec: Sized { + type Element; + type ID: Eq + Hash + Clone + Debug; + + /// Constructs a new, empty `IdentifiedVec` with the specified + /// `id_of_element` closure + fn new_identifying_element(id_of_element: fn(&Self::Element) -> Self::ID) -> Self; + + /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to + /// determine the element for any elements with duplicate identity. + /// + /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements + /// that may not have unique ids. This initializer calls the `combine` closure with the current + /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element + /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them + /// to produce a new element, or even throw an error. + /// + /// - Parameters: + /// - elements: A sequence of elements to use for the new `identified_vec`. + /// - id_of_element: The function which extracts the identifier for an element, + /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. + /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` + /// implements high-quality hashing. + fn try_from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Self::Element) -> Self::ID, + combine: fn( + (usize, &Self::Element, &Self::Element), + ) -> Result, + ) -> Result + where + It: IntoIterator; + + fn from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Self::Element) -> Self::ID, + combine: fn((usize, &Self::Element, &Self::Element)) -> ConflictResolutionChoice, + ) -> Self + where + It: IntoIterator; + + /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. + fn ids(&self) -> &Vec; + + /// Returns the number of elements in the `identified_vec`, also referred to as its 'length'. + fn len(&self) -> usize; + + /// Returns the index for the given id. + /// + /// If an element identified by the given id is found in the `identified_vec`, this method returns an index + /// into the array that corresponds to the element. + /// + /// ``` + /// extern crate identified_vec; + /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// + /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] + /// struct User { + /// id: &'static str, + /// } + /// + /// impl User { + /// fn new(id: &'static str) -> Self { + /// Self { id } + /// } + /// } + /// + /// impl Identifiable for User { + /// type ID = &'static str; + /// fn id(&self) -> Self::ID { + /// self.id + /// } + /// } + /// + /// let mut users = + /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); + /// + /// assert_eq!(users.index_of_id(&"u_1729"), Some(1)); + /// assert_eq!(users.index_of_id(&"u_1337"), None); + /// ``` + /// + /// - Parameter id: The id to find in the `identified_vec`. + /// - Returns: The index for the element identified by `id` if found in the `identified_vec`; otherwise, + /// `None`. + /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. + fn index_of_id(&self, id: &Self::ID) -> Option; + + /// Returns a mutable reference to the element identified by `id` if any, else None. + /// + /// - Parameter id: The id to find in the `identified_vec`. + /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, + /// `None`. + /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. + fn get_mut(&mut self, id: &Self::ID) -> Option<&mut Self::Element>; + + fn elements(&self) -> Vec<&Self::Element>; + + /// Returns `true` if the `identified_vec` contains the `element.` + fn contains(&self, element: &Self::Element) -> bool; + + /// Returns `true if the `identified_vec` contains an element for the specified `id` + fn contains_id(&self, id: &Self::ID) -> bool; + + /// Returns a reference to the element corresponding to the `id`, if found, else `None`. + fn get(&self, id: &Self::ID) -> Option<&Self::Element>; + + /// Returns a reference to the element at index if found, else `None`. + fn get_at_index(&self, index: usize) -> Option<&Self::Element>; + + /// Append a new member to the end of the `identified_vec`, if the `identified_vec` doesn't already contain it. + /// + /// - Parameter item: The element to add to the `identified_vec`. + /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether + /// the operation added a new element, and `index` is the index of `item` in the resulting + /// `identified_vec`. + /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on + /// the `Self::ID` type, if it implements high-quality hashing. + fn append(&mut self, element: Self::Element) -> (bool, usize); + + /// Append the contents of an iterator to the end of the set, excluding elements that are already + /// members. + /// + /// - Parameter elements: A finite sequence of elements to append. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::Element` type, if it implements high-quality hashing. + fn append_other(&mut self, other: It) + where + It: IntoIterator; + + /// Adds the given element to the `identified_vec` unconditionally, either appending it to the `identified_vec``, or + /// replacing an existing value if it's already present. + /// + /// - Parameter item: The value to append or replace. + /// - Returns: The original element that was replaced by this operation, or `None` if the value was + /// appended to the end of the collection. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::ID` type, if it implements high-quality hashing. + fn update_or_append(&mut self, element: Self::Element) -> Option; + + /// Replace the member at the given index with a new value of the same identity. + /// + /// - Parameter item: The new value that should replace the original element. `item` must match + /// the identity of the original value. + /// - Parameter index: The index of the element to be replaced. + /// - Returns: The original element that was replaced. + fn update_at(&mut self, element: Self::Element, index: usize) -> Self::Element; + + /// Insert a new member to this identified_vec at the specified index, if the identified_vec doesn't already contain + /// it. + /// + /// - Parameter element: The element to insert. + /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether + /// the operation added a new element, and `index` is the index of `element` in the resulting + /// identified_vec. If `inserted` is true, then the returned `index` may be different from the index + /// requested. + /// + /// - Complexity: The operation is expected to perform amortized O(`self.count`) copy, hash, and + /// compare operations on the `Self::ID` type, if it implements high-quality hashing. (Insertions need + /// to make room in the storage identified_vec to add the inserted element.) + fn insert(&mut self, element: Self::Element, at: usize) -> (bool, usize); + + /// Adds the given element into the set unconditionally, either inserting it at the specified + /// index, or replacing an existing value if it's already present. + /// + /// - Parameter item: The value to append or replace. + /// - Parameter index: The index at which to insert the new member if `item` isn't already in the + /// set. + /// - Returns: The original element that was replaced by this operation, or `None` if the value was + /// newly inserted into the collection. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::ID` type, if it implements high-quality hashing. + fn update_or_insert( + &mut self, + element: Self::Element, + index: usize, + ) -> (Option, usize); + + ///////////// + // Remove // + ///////////// + + /// Removes the element identified by the given id from the `identified_vec`. + /// + /// ``` + /// extern crate identified_vec; + /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// + /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] + /// struct User { + /// id: &'static str, + /// } + /// + /// impl User { + /// fn new(id: &'static str) -> Self { + /// Self { id } + /// } + /// } + /// + /// impl Identifiable for User { + /// type ID = &'static str; + /// fn id(&self) -> Self::ID { + /// self.id + /// } + /// } + /// + /// let mut users = + /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); + /// + /// assert_eq!(users.remove_by_id(&"u_1729"), Some(User::new("u_1729"))); + /// assert_eq!(users.elements(), [&User::new("u_42")]); + /// assert_eq!(users.remove_by_id(&"u_1337"), None); + /// assert_eq!(users.len(), 1); + /// ``` + /// + /// - Parameter id: The id of the element to be removed from the `identified_vec`. + /// - Returns: The element that was removed, or `None` if the element was not present in the array. + /// - Complexity: O(`count`) + fn remove_by_id(&mut self, id: &Self::ID) -> Option; + + /// Removes the given element from the `identified_vec`. + /// + /// If the element is found in the `identified_vec`, this method returns the element. + /// + /// If the element isn't found in the `identified_vec`, `remove` returns `None`. + /// + /// - Parameter element: The element to remove. + /// - Returns: The value that was removed, or `None` if the element was not present in the `identified_vec`. + /// - Complexity: O(`count`) + fn remove(&mut self, element: &Self::Element) -> Option; + + /// Removes and returns the element at the specified position. + /// + /// All the elements following the specified position are moved to close the resulting gap. + /// + /// - Parameter index: The position of the element to remove. + /// - Returns: The removed element. + /// - Precondition: `index` must be a valid index of the collection that is not equal to the + /// collection's end index. + /// - Complexity: O(`count`) + fn remove_at(&mut self, index: usize) -> Self::Element; + + /// Removes all the elements at the specified `offsets` from the `identified_vec`. + /// + /// - Parameter offsets: The offsets of all elements to be removed. + /// - Complexity: O(*n*) where *n* is the length of the `identified_vec`. + fn remove_at_offsets(&mut self, offsets: It) + where + It: IntoIterator; +} + /// Representation of a choice in a conflict resolution -/// where two elements with the same ID exists, if `ChooseFirst`, +/// where two elements with the same Self::ID exists, if `ChooseFirst`, /// is specified the first/current/existing value will be used, but /// if `ChooseLast` is specified then the new/last will be replace /// the first/current/existing. @@ -31,7 +282,7 @@ pub enum ConflictResolutionChoice { /// /// ``` /// extern crate identified_vec; -/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf}; +/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf, IsIdentifiableVec}; /// use std::cell::RefCell; /// /// #[derive(Eq, PartialEq, Clone, Debug)] @@ -84,7 +335,7 @@ pub enum ConflictResolutionChoice { /// .collect::>() /// ); /// -/// // Element with same ID is not appended: +/// // Self::Element with same Self::ID is not appended: /// users.append(User::new("u_42", "Tom Mervolo Dolder")); /// assert_eq!( /// users.elements(), @@ -97,7 +348,7 @@ pub enum ConflictResolutionChoice { /// .collect::>() /// ); /// -/// // Element with same ID replaces existing if an `update_*` method is used: +/// // Self::Element with same Self::ID replaces existing if an `update_*` method is used: /// // e.g. `update_or_insert`: /// users.update_or_insert(User::new("u_42", "Tom Mervolo Dolder"), 0); /// assert_eq!( @@ -142,7 +393,7 @@ pub enum ConflictResolutionChoice { /// /// ``` /// /// extern crate identified_vec; -/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf}; +/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf, IsIdentifiableVec}; /// /// let numbers = IdentifiedVec::::new_identifying_element(|e| *e); /// ``` @@ -156,7 +407,7 @@ pub enum ConflictResolutionChoice { /// # Performance /// /// Like the standard `HashMap` type, the performance of hashing operations in -/// `IdentifiedVec` is highly sensitive to the quality of hashing implemented by the `ID` +/// `IdentifiedVec` is highly sensitive to the quality of hashing implemented by the `Self::ID` /// type. Failing to correctly implement hashing can easily lead to unacceptable performance, with /// the severity of the effect increasing with the size of the underlying hash table. /// @@ -165,7 +416,7 @@ pub enum ConflictResolutionChoice { /// ensure hashed collection types exhibit their target performance, it is important to ensure that /// such collisions cannot be induced merely by adding a particular list of members to the set. /// -/// When `ID` implements `Hash` correctly, testing for membership in an ordered set is expected +/// When `Self::ID` implements `Hash` correctly, testing for membership in an ordered set is expected /// to take O(1) equality checks on average. Hash collisions can still occur organically, so the /// worst-case lookup performance is technically still O(*n*) (where *n* is the size of the set); /// however, long lookup chains are unlikely to occur in practice. @@ -176,46 +427,42 @@ pub enum ConflictResolutionChoice { /// should not be mutated in place, as it will drift from its associated dictionary key. Identified /// bec is designed to avoid this invariant. Mutating an element's id will result in a runtime error. #[derive(Debug, Clone)] -pub struct IdentifiedVec +pub struct IdentifiedVec where - ID: Eq + Hash + Clone + Debug, + I: Eq + Hash + Clone + Debug, { /// The holder of the insertion order - pub(crate) order: Vec, + pub(crate) order: Vec, /// The storage of elements. - pub(crate) elements: HashMap, + pub(crate) elements: HashMap, - /// Function which extracts the ID of an Element. - pub(crate) _id_of_element: fn(&Element) -> ID, + /// Function which extracts the Self::ID of an Self::Element. + pub(crate) _id_of_element: fn(&E) -> I, } -/////////////////////// -//// Constructors /// -/////////////////////// -impl IdentifiedVec +impl IsIdentifiableVec for IdentifiedVec where - ID: Eq + Hash + Clone + Debug, + I: Eq + Hash + Clone + Debug, { - /// Constructs a new, empty `IdentifiedVec` with the specified + //////////////////// + // Constructors // + //////////////////// + + type ID = I; + type Element = E; + + /// Constructs a new, empty `IdentifiedVec` with the specified /// `id_of_element` closure #[inline] - pub fn new_identifying_element(id_of_element: fn(&Element) -> ID) -> Self { + fn new_identifying_element(id_of_element: fn(&E) -> I) -> Self { Self { order: Vec::new(), elements: HashMap::new(), _id_of_element: id_of_element, } } -} -/////////////////////// -//// Constructors /// -/////////////////////// -impl IdentifiedVec -where - ID: Eq + Hash + Clone + Debug, -{ /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to /// determine the element for any elements with duplicate identity. /// @@ -230,20 +477,22 @@ where /// - id_of_element: The function which extracts the identifier for an element, /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` /// implements high-quality hashing. #[cfg(not(tarpaulin_include))] // false negative #[inline] - pub fn try_from_iter_select_unique_ids_with( - elements: I, - id_of_element: fn(&Element) -> ID, - combine: fn((usize, &Element, &Element)) -> Result, - ) -> Result + fn try_from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Self::Element) -> Self::ID, + combine: fn( + (usize, &Self::Element, &Self::Element), + ) -> Result, + ) -> Result where - I: IntoIterator, + It: IntoIterator, { - let mut _order = Vec::::new(); - let mut _elements = HashMap::::new(); + let mut _order = Vec::::new(); + let mut _elements = HashMap::::new(); for element in elements.into_iter() { let id = id_of_element(&element); @@ -288,20 +537,20 @@ where /// - id_of_element: The function which extracts the identifier for an element, /// - combine: Closure used combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`) /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` /// implements high-quality hashing. #[cfg(not(tarpaulin_include))] // false negative #[inline] - pub fn from_iter_select_unique_ids_with( - elements: I, - id_of_element: fn(&Element) -> ID, - combine: fn((usize, &Element, &Element)) -> ConflictResolutionChoice, + fn from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Self::Element) -> Self::ID, + combine: fn((usize, &Self::Element, &Self::Element)) -> ConflictResolutionChoice, ) -> Self where - I: IntoIterator, + It: IntoIterator, { - let mut _order = Vec::::new(); - let mut _elements = HashMap::::new(); + let mut _order = Vec::::new(); + let mut _elements = HashMap::::new(); for element in elements.into_iter() { let id = id_of_element(&element); @@ -324,26 +573,22 @@ where elements: _elements, } } -} -/////////////////////// -//// Public Get /// -/////////////////////// -impl IdentifiedVec -where - ID: Eq + Hash + Clone + Debug, -{ - /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. + //////////////////// + // Public Get // + //////////////////// + + /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. /// /// - Complexity: O(1) #[inline] - pub fn ids(&self) -> &Vec { + fn ids(&self) -> &Vec { &self.order } /// Returns the number of elements in the `identified_vec`, also referred to as its 'length'. #[inline] - pub fn len(&self) -> usize { + fn len(&self) -> usize { if cfg!(debug_assertions) { assert_eq!(self.order.len(), self.elements.len()); } @@ -357,7 +602,7 @@ where /// /// ``` /// extern crate identified_vec; - /// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; /// /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] /// struct User { @@ -387,9 +632,9 @@ where /// - Parameter id: The id to find in the `identified_vec`. /// - Returns: The index for the element identified by `id` if found in the `identified_vec`; otherwise, /// `None`. - /// - Complexity: Expected to be O(1) on average, if `ID` implements high-quality hashing. + /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. #[inline] - pub fn index_of_id(&self, id: &ID) -> Option { + fn index_of_id(&self, id: &Self::ID) -> Option { self.order.iter().position(|i| i == id) } @@ -398,149 +643,52 @@ where /// - Parameter id: The id to find in the `identified_vec`. /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, /// `None`. - /// - Complexity: Expected to be O(1) on average, if `ID` implements high-quality hashing. + /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. #[inline] - pub fn get_mut(&mut self, id: &ID) -> Option<&mut Element> { + fn get_mut(&mut self, id: &Self::ID) -> Option<&mut Self::Element> { self.elements.get_mut(id) } + //////////////////// + // Public Get // + //////////////////// + /// A read-only collection of references to the elements contained in this array, as a `Vec<&Elements>`. /// /// N.B. that this method is not constant time. /// - /// If `Element` implements `Clone` you can use `self.items()` which returns a `Vec`, of cloned elements. + /// If `Self::Element` implements `Clone` you can use `self.items()` which returns a `Vec`, of cloned elements. /// /// - Complexity: O(n) #[inline] - pub fn elements(&self) -> Vec<&Element> { + fn elements(&self) -> Vec<&Self::Element> { self.iter().collect() } /// Returns `true` if the `identified_vec` contains the `element.` #[inline] - pub fn contains(&self, element: &Element) -> bool { + fn contains(&self, element: &Self::Element) -> bool { self.elements.contains_key(&self.id(&element)) } /// Returns `true if the `identified_vec` contains an element for the specified `id` #[inline] - pub fn contains_id(&self, id: &ID) -> bool { + fn contains_id(&self, id: &Self::ID) -> bool { self.elements.contains_key(id) } /// Returns a reference to the element corresponding to the `id`, if found, else `None`. #[inline] - pub fn get(&self, id: &ID) -> Option<&Element> { + fn get(&self, id: &Self::ID) -> Option<&Self::Element> { self.elements.get(id) } /// Returns a reference to the element at index if found, else `None`. #[inline] - pub fn get_at_index(&self, index: usize) -> Option<&Element> { + fn get_at_index(&self, index: usize) -> Option<&Self::Element> { self.order.get(index).and_then(|id| self.get(id)) } -} - -impl IdentifiedVec -where - Element: Clone, - ID: Eq + Hash + Clone + Debug, -{ - /// A read-only collection of clones of the elements contained in this array, as a `Vec`. - /// - /// N.B. that this method is not constant time. - /// - /// Use `self.elements()` if you are looking for a collection of references. - /// - /// - Complexity: O(n) - #[inline] - pub fn items(&self) -> Vec { - self.iter().map(|e| e.clone()).collect() - } -} - -/// An iterator over the items of an `IdentifiedVec`. -pub struct IdentifiedVecIterator<'a, ID, Element> -where - ID: Eq + Hash + Clone + Debug, -{ - identified_vec: &'a IdentifiedVec, - index: usize, -} - -impl<'a, ID, Element> Iterator for IdentifiedVecIterator<'a, ID, Element> -where - ID: Eq + Hash + Clone + Debug, -{ - type Item = &'a Element; - - fn next(&mut self) -> Option { - if self.index < self.identified_vec.len() { - let id = Some(&self.identified_vec.order[self.index]).unwrap(); - self.index += 1; - return self.identified_vec.get(id); - } else { - None - } - } -} - -impl IdentifiedVec -where - ID: Eq + Hash + Clone + Debug, -{ - pub fn iter(&self) -> IdentifiedVecIterator { - IdentifiedVecIterator { - identified_vec: self, - index: 0, - } - } -} - -/// An owning iterator over the items of an `IdentifiedVec`. -pub struct IdentifiedVecIntoIterator -where - ID: Eq + Hash + Clone + Debug, -{ - identified_vec: IdentifiedVec, -} - -impl Iterator for IdentifiedVecIntoIterator -where - ID: Eq + Hash + Clone + Debug, -{ - type Item = Element; - - fn next(&mut self) -> Option { - if self.identified_vec.len() == 0 { - return None; - } - let result = self.identified_vec.remove_at(0); - Some(result) - } -} -impl IntoIterator for IdentifiedVec -where - ID: Eq + Hash + Clone + Debug, -{ - type Item = Element; - type IntoIter = IdentifiedVecIntoIterator; - - fn into_iter(self) -> Self::IntoIter { - Self::IntoIter { - identified_vec: self, - } - } -} - -/////////////////////// -//// Public Insert /// -/////////////////////// -impl IdentifiedVec -where - ID: Eq + Hash + Clone + Debug, -{ /// Append a new member to the end of the `identified_vec`, if the `identified_vec` doesn't already contain it. /// /// - Parameter item: The element to add to the `identified_vec`. @@ -548,9 +696,9 @@ where /// the operation added a new element, and `index` is the index of `item` in the resulting /// `identified_vec`. /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on - /// the `ID` type, if it implements high-quality hashing. + /// the `Self::ID` type, if it implements high-quality hashing. #[inline] - pub fn append(&mut self, element: Element) -> (bool, usize) { + fn append(&mut self, element: E) -> (bool, usize) { self.insert(element, self.end_index()) } @@ -559,11 +707,11 @@ where /// /// - Parameter elements: A finite sequence of elements to append. /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Element` type, if it implements high-quality hashing. + /// operations on the `Self::Element` type, if it implements high-quality hashing. #[inline] - pub fn append_other(&mut self, other: I) + fn append_other(&mut self, other: It) where - I: IntoIterator, + It: IntoIterator, { other.into_iter().for_each(|i| _ = self.append(i)) } @@ -575,9 +723,9 @@ where /// - Returns: The original element that was replaced by this operation, or `None` if the value was /// appended to the end of the collection. /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `ID` type, if it implements high-quality hashing. + /// operations on the `Self::ID` type, if it implements high-quality hashing. #[inline] - pub fn update_or_append(&mut self, element: Element) -> Option { + fn update_or_append(&mut self, element: E) -> Option { let id = self.id(&element); self._update_value(element, id) } @@ -590,7 +738,7 @@ where /// - Returns: The original element that was replaced. /// - Complexity: Amortized O(1). #[inline] - pub fn update_at(&mut self, element: Element, index: usize) -> Element { + fn update_at(&mut self, element: E, index: usize) -> E { let old_id = self .order .get(index) @@ -616,11 +764,11 @@ where /// requested. /// /// - Complexity: The operation is expected to perform amortized O(`self.count`) copy, hash, and - /// compare operations on the `ID` type, if it implements high-quality hashing. (Insertions need + /// compare operations on the `Self::ID` type, if it implements high-quality hashing. (Insertions need /// to make room in the storage identified_vec to add the inserted element.) #[cfg(not(tarpaulin_include))] // false negative #[inline] - pub fn insert(&mut self, element: Element, at: usize) -> (bool, usize) { + fn insert(&mut self, element: E, at: usize) -> (bool, usize) { let id = self.id(&element); if let Some(existing) = self.index_of_id(&id) { return (false, existing.clone()); @@ -638,26 +786,21 @@ where /// - Returns: The original element that was replaced by this operation, or `None` if the value was /// newly inserted into the collection. /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `ID` type, if it implements high-quality hashing. + /// operations on the `Self::ID` type, if it implements high-quality hashing. #[inline] - pub fn update_or_insert(&mut self, element: Element, index: usize) -> (Option, usize) { + fn update_or_insert(&mut self, element: E, index: usize) -> (Option, usize) { let id = self.id(&element); self._update_value_inserting_at(element, id, index) } -} -/////////////////////// -//// Public Remove /// -/////////////////////// -impl IdentifiedVec -where - ID: Eq + Hash + Clone + Debug, -{ + //////////////////// + // Public Remove // + //////////////////// /// Removes the element identified by the given id from the `identified_vec`. /// /// ``` /// extern crate identified_vec; - /// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; /// /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] /// struct User { @@ -691,7 +834,7 @@ where /// - Complexity: O(`count`) #[cfg(not(tarpaulin_include))] // false negative #[inline] - pub fn remove_by_id(&mut self, id: &ID) -> Option { + fn remove_by_id(&mut self, id: &I) -> Option { match self.index_of_id(id) { Some(index) => { self.order.remove(index); @@ -714,7 +857,7 @@ where /// - Returns: The value that was removed, or `None` if the element was not present in the `identified_vec`. /// - Complexity: O(`count`) #[inline] - pub fn remove(&mut self, element: &Element) -> Option { + fn remove(&mut self, element: &E) -> Option { self.remove_by_id(&self.id(element)) } @@ -728,12 +871,15 @@ where /// collection's end index. /// - Complexity: O(`count`) #[inline] - pub fn remove_at(&mut self, index: usize) -> Element { + fn remove_at(&mut self, index: usize) -> E { let id = self .order .get(index) .expect("Precondition failure, index out of bounds"); - let removed = self.elements.remove(id).expect("Element of existing id"); + let removed = self + .elements + .remove(id) + .expect("Self::Element of existing id"); self.order.remove(index); return removed; } @@ -743,9 +889,9 @@ where /// - Parameter offsets: The offsets of all elements to be removed. /// - Complexity: O(*n*) where *n* is the length of the `identified_vec`. #[inline] - pub fn remove_at_offsets(&mut self, offsets: I) + fn remove_at_offsets(&mut self, offsets: It) where - I: IntoIterator, + It: IntoIterator, { let mut internal_offset = 0; offsets.into_iter().for_each(|i| { @@ -755,33 +901,128 @@ where } } +impl IdentifiedVec +where + E: Clone, + I: Eq + Hash + Clone + Debug, +{ + /// A read-only collection of clones of the elements contained in this array, as a `Vec`. + /// + /// N.B. that this method is not constant time. + /// + /// Use `self.elements()` if you are looking for a collection of references. + /// + /// - Complexity: O(n) + #[inline] + pub fn items(&self) -> Vec { + self.iter().map(|e| e.clone()).collect() + } +} + +/// An iterator over the items of an `IdentifiedVec`. +pub struct IdentifiedVecIterator<'a, I, E> +where + I: Eq + Hash + Clone + Debug, +{ + identified_vec: &'a IdentifiedVec, + index: usize, +} + +impl<'a, I, E> Iterator for IdentifiedVecIterator<'a, I, E> +where + I: Eq + Hash + Clone + Debug, +{ + type Item = &'a E; + + fn next(&mut self) -> Option { + if self.index < self.identified_vec.len() { + let id = Some(&self.identified_vec.order[self.index]).unwrap(); + self.index += 1; + return self.identified_vec.get(id); + } else { + None + } + } +} + +impl IdentifiedVec +where + I: Eq + Hash + Clone + Debug, +{ + pub fn iter(&self) -> IdentifiedVecIterator { + IdentifiedVecIterator { + identified_vec: self, + index: 0, + } + } +} + +/// An owning iterator over the items of an `IdentifiedVec`. +pub struct IdentifiedVecIntoIterator +where + I: Eq + Hash + Clone + Debug, +{ + identified_vec: IdentifiedVec, +} + +impl Iterator for IdentifiedVecIntoIterator +where + I: Eq + Hash + Clone + Debug, +{ + type Item = E; + + fn next(&mut self) -> Option { + if self.identified_vec.len() == 0 { + return None; + } + let result = self.identified_vec.remove_at(0); + Some(result) + } +} + +impl IntoIterator for IdentifiedVec +where + I: Eq + Hash + Clone + Debug, +{ + type Item = E; + type IntoIter = IdentifiedVecIntoIterator; + + fn into_iter(self) -> Self::IntoIter { + Self::IntoIter { + identified_vec: self, + } + } +} + +impl IdentifiedVec where I: Eq + Hash + Clone + Debug {} + /////////////////////// //// Eq /// /////////////////////// -impl PartialEq for IdentifiedVec +impl PartialEq for IdentifiedVec where - Element: PartialEq, - ID: Eq + Hash + Clone + Debug, + E: PartialEq, + I: Eq + Hash + Clone + Debug, { fn eq(&self, other: &Self) -> bool { self.elements() == other.elements() } } -impl Eq for IdentifiedVec +impl Eq for IdentifiedVec where - Element: Eq, - ID: Eq + Hash + Clone + Debug, + E: Eq, + I: Eq + Hash + Clone + Debug, { } /////////////////////// //// Hash /// /////////////////////// -impl Hash for IdentifiedVec +impl Hash for IdentifiedVec where - Element: Hash, - ID: Eq + Hash + Clone + Debug, + E: Hash, + I: Eq + Hash + Clone + Debug, { fn hash(&self, state: &mut H) { self.elements().hash(state); @@ -791,10 +1032,10 @@ where /////////////////////// //// Display /// /////////////////////// -impl Display for IdentifiedVec +impl Display for IdentifiedVec where - Element: Debug, - ID: Eq + Hash + Clone + Debug, + E: Debug, + I: Eq + Hash + Clone + Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.elements().fmt(f) @@ -804,9 +1045,9 @@ where /////////////////////// //// PRIVATE /// /////////////////////// -impl IdentifiedVec +impl IdentifiedVec where - ID: Eq + Hash + Clone + Debug, + I: Eq + Hash + Clone + Debug, { /// Next index for element appended #[inline] @@ -814,16 +1055,16 @@ where self.len() } - /// Returns the ID of an Element + /// Returns the Self::ID of an Self::Element #[inline] - fn id(&self, of: &Element) -> ID { + fn id(&self, of: &E) -> I { (self._id_of_element)(of) } - /// Inserting ID at an index, returning if it did, if not, the index of the existing. + /// Inserting Self::ID at an index, returning if it did, if not, the index of the existing. #[cfg(not(tarpaulin_include))] // false negative #[inline] - fn _insert_id_at(&mut self, id: ID, index: usize) -> (bool, usize) { + fn _insert_id_at(&mut self, id: I, index: usize) -> (bool, usize) { match self.index_of_id(&id) { Some(existing) => (false, existing), None => { @@ -834,7 +1075,7 @@ where } #[inline] - fn _update_value(&mut self, element: Element, for_key: ID) -> Option { + fn _update_value(&mut self, element: E, for_key: I) -> Option { let value = element; let key = for_key; @@ -852,10 +1093,10 @@ where #[inline] fn _update_value_inserting_at( &mut self, - element: Element, - for_key: ID, + element: E, + for_key: I, index: usize, - ) -> (Option, usize) { + ) -> (Option, usize) { let id = for_key; let value = element; @@ -874,10 +1115,10 @@ where /////////////////////// //// DEBUG /// /////////////////////// -impl IdentifiedVec +impl IdentifiedVec where - Element: Debug, - ID: Eq + Hash + Clone + Debug, + E: Debug, + I: Eq + Hash + Clone + Debug, { #[cfg(not(tarpaulin_include))] #[cfg(debug_assertions)] diff --git a/src/vec_of.rs b/src/vec_of.rs index f6801c1..f915195 100644 --- a/src/vec_of.rs +++ b/src/vec_of.rs @@ -7,7 +7,7 @@ use std::fmt::Debug; use crate::serde_error::IdentifiedVecOfSerdeFailure; use crate::{ identified_vec_of::Identifiable, - vec::{ConflictResolutionChoice, IdentifiedVec}, + vec::{ConflictResolutionChoice, IdentifiedVec, IsIdentifiableVec}, }; #[cfg(feature = "serde")] diff --git a/tests/tests.rs b/tests/tests.rs index b415fdd..5b07a44 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -4,7 +4,7 @@ use std::{cell::RefCell, collections::HashSet, fmt::Debug, ops::Deref}; use identified_vec::{ ConflictResolutionChoice, Identifiable, IdentifiedVec, IdentifiedVecOf, - IdentifiedVecOfSerdeFailure, + IdentifiedVecOfSerdeFailure, IsIdentifiableVec, }; #[derive(Eq, PartialEq, Clone)] From cc004c83a05d0ecfef3f91dcedae77a8f71542ac Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 13 Dec 2023 21:40:16 +0100 Subject: [PATCH 02/24] WIP --- Cargo.lock | 22 +- Cargo.toml | 28 +- src/identifiable_trait.rs | 18 - src/lib.rs | 165 ----- src/primitives_identifiable.rs | 26 - src/serde_error.rs | 6 - src/vec.rs | 1133 -------------------------------- src/vec_of.rs | 154 ----- 8 files changed, 19 insertions(+), 1533 deletions(-) delete mode 100644 src/identifiable_trait.rs delete mode 100644 src/lib.rs delete mode 100644 src/primitives_identifiable.rs delete mode 100644 src/serde_error.rs delete mode 100644 src/vec.rs delete mode 100644 src/vec_of.rs diff --git a/Cargo.lock b/Cargo.lock index 12f4005..0a90669 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,17 +31,27 @@ dependencies = [ "thiserror", ] +[[package]] +name = "identified_vec_macros" +version = "0.1.0" +dependencies = [ + "identified_vec", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "itoa" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "libc" -version = "0.2.150" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "maplit" @@ -105,9 +115,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "serde" diff --git a/Cargo.toml b/Cargo.toml index cfd57c2..7a5579a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,27 +1,5 @@ -[package] -name = "identified_vec" -version = "0.1.4" -edition = "2021" -authors = ["Alexander Cyon "] -description = "Like HashSet but retaining INSERTION order and without `Hash` requirement on the Element type." -license = "MIT" -readme = "README.md" -repository = "https://github.com/Sajjon/identified_vec" -keywords = ["identifiable", "vec", "orderset", "set", "hashset"] -categories = ["data-structures"] +[workspace] -[features] -serde = ["dep:serde"] -id_prim = [] -is_id_vec_of = [] +resolver = "2" -[dependencies] -serde = { version = "1.0.193", optional = true } -thiserror = "1.0.50" - -[dev-dependencies] -identified_vec = { path = ".", features = ["id_prim", "serde"] } -serde = "1.0.193" -serde_json = "1.0.108" -rand = "0.8.5" -maplit = "1.0.2" +members = ["identified_vec", "identified_vec_macros"] diff --git a/src/identifiable_trait.rs b/src/identifiable_trait.rs deleted file mode 100644 index db68826..0000000 --- a/src/identifiable_trait.rs +++ /dev/null @@ -1,18 +0,0 @@ -use std::fmt::Debug; -use std::hash::Hash; - -/// The `Identifiable` trait allows you to use the -/// `IdentifiedVecOf instead of the more verbose -/// `IdentifiedVec` but also allows you to -/// skip the `id_of_element: fn(&Element) -> ID` closure when -/// initializing a new identified vec. -pub trait Identifiable { - /// The type that your `Element` will use as its globally unique and stable ID, - /// must impl `Hash` since it is used as a key in `IdentifiedVecOf`'s internal - /// `HashMap`. Must impl `Clone` since we need to be able to clone it as a key - type ID: Eq + Hash + Clone + Debug; - - /// Return `Element`'s globally unique and stable ID, used to uniquely identify - /// the `Element` in the `IdentifiedVecOf` collection of elements. - fn id(&self) -> Self::ID; -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 4dfc4d1..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,165 +0,0 @@ -//! A collection of unique identifiable elements which retains **insertion** order, inspired by [Pointfree's Swift Identified Collections](https://github.com/pointfreeco/swift-identified-collections). -//! -//! Similar to the standard `Vec`, the `IdentifiedVec` maintain their elements in a particular user-specified order. However, unlike `Vec`, the `IdentifiedVec` introduce the ability to uniquely identify elements, using a hash table to ensure that no two elements have the same identity, and to efficiently look up elements corresponding to specific identifiers. -//! -//! `IdentifiedVec` is a useful alternative to `Vec` when you need to be able to efficiently access unique elements by a stable identifier. It is also a useful alternative to `BTreeSet`, where the `Ord` trait requirement may be too strict, an a useful alternative to `HashSet` where `Hash` trait requirement may be too strict. -//! -//! You can create an identified vec with any element type that implements the `Identifiable` trait. -//! -//! # Example -//! -//! ``` -//! extern crate identified_vec; -//! use identified_vec::{IsIdentifiableVec, IdentifiedVec, IdentifiedVecOf, Identifiable}; -//! use std::cell::RefCell; -//! -//! #[derive(Eq, PartialEq, Clone, Debug)] -//! struct User { -//! id: &'static str, -//! name: RefCell<&'static str>, -//! } -//! -//! impl User { -//! fn new(id: &'static str, name: &'static str) -> Self { -//! Self { -//! id, -//! name: RefCell::new(name), -//! } -//! } -//! fn name(&self) -> &'static str { -//! *self.name.borrow() -//! } -//! } -//! -//! impl Identifiable for User { -//! type ID = &'static str; -//! fn id(&self) -> Self::ID { -//! self.id -//! } -//! } -//! -//! let mut users = IdentifiedVecOf::::from_iter([ -//! User::new("u_42", "Satoshi Nakamoto"), -//! User::new("u_1337", "Leia Skywalker"), -//! ]); -//! -//! assert_eq!( -//! users.get(&"u_42").map(|u| u.name()), -//! Some("Satoshi Nakamoto") -//! ); -//! -//! assert_eq!( -//! users.get_at_index(1).map(|u| u.name()), -//! Some("Leia Skywalker") -//! ); -//! -//! users.append(User::new("u_237", "Alan Turing")); -//! assert_eq!( -//! users.elements(), -//! [ -//! User::new("u_42", "Satoshi Nakamoto"), -//! User::new("u_1337", "Leia Skywalker"), -//! User::new("u_237", "Alan Turing"), -//! ] -//! .iter() -//! .collect::>() -//! ); -//! -//! // Element with same ID is not appended: -//! users.append(User::new("u_42", "Tom Mervolo Dolder")); -//! assert_eq!( -//! users.elements(), -//! [ -//! User::new("u_42", "Satoshi Nakamoto"), -//! User::new("u_1337", "Leia Skywalker"), -//! User::new("u_237", "Alan Turing"), -//! ] -//! .iter() -//! .collect::>() -//! ); -//! -//! // Element with same ID replaces existing if an `update_*` method is used: -//! // e.g. `update_or_insert`: -//! users.update_or_insert(User::new("u_42", "Tom Mervolo Dolder"), 0); -//! assert_eq!( -//! users.elements(), -//! [ -//! User::new("u_42", "Tom Mervolo Dolder"), -//! User::new("u_1337", "Leia Skywalker"), -//! User::new("u_237", "Alan Turing"), -//! ] -//! .iter() -//! .collect::>() -//! ); -//! -//! // or `update_or_append` -//! users.update_or_append(User::new("u_237", "Marie Curie")); -//! assert_eq!( -//! users.elements(), -//! [ -//! User::new("u_42", "Tom Mervolo Dolder"), -//! User::new("u_1337", "Leia Skywalker"), -//! User::new("u_237", "Marie Curie"), -//! ] -//! .iter() -//! .collect::>() -//! ); -//! -//! // or mutate with `get_mut(id)` -//! *users.get_mut(&"u_1337").unwrap().name.get_mut() = "Yoda"; -//! assert_eq!( -//! users.elements(), -//! [ -//! User::new("u_42", "Tom Mervolo Dolder"), -//! User::new("u_1337", "Yoda"), -//! User::new("u_237", "Marie Curie"), -//! ] -//! .iter() -//! .collect::>() -//! ); -//! ``` -//! -//! Or you can provide a closure that describes an element's identity: -//! -//! ``` -//! extern crate identified_vec; -//! use identified_vec::{IsIdentifiableVec, IdentifiedVec, IdentifiedVecOf, Identifiable}; -//! -//! // closure which plucks out an ID from an element. -//! let numbers = IdentifiedVec::::new_identifying_element(|e| *e); -//! ``` - -mod identifiable_trait; -mod is_id_vec_of; -mod primitives_identifiable; -mod serde_error; -mod vec; -mod vec_of; - -pub mod identified_vec { - //! A collection of unique identifiable elements which retains **insertion** order. - pub use crate::vec::*; -} - -pub mod identified_vec_of { - //! The `Identifiable` trait allows you to use the - //! `IdentifiedVecOf instead of the more verbose - //! `IdentifiedVec` but also allows you to - //! skip the `id_of_element: fn(&Element) -> ID` closure when - //! initializing a new identified vec. - pub use crate::identifiable_trait::*; - pub use crate::vec_of::*; - - #[cfg(feature = "id_prim")] - pub use crate::primitives_identifiable::*; - - #[cfg(feature = "serde")] - pub use crate::serde_error::*; - - #[cfg(feature = "is_id_vec_of")] - pub use crate::is_id_vec_of::*; -} - -pub use crate::identified_vec::*; -pub use crate::identified_vec_of::*; -pub use crate::vec::IsIdentifiableVec; diff --git a/src/primitives_identifiable.rs b/src/primitives_identifiable.rs deleted file mode 100644 index 1081c11..0000000 --- a/src/primitives_identifiable.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![cfg(feature = "id_prim")] - -use crate::identified_vec_of::Identifiable; - -macro_rules! impl_id { - ($primitive_type:ident) => { - impl Identifiable for $primitive_type { - type ID = $primitive_type; - fn id(&self) -> Self::ID { - *self - } - } - }; -} - -impl_id!(i8); -impl_id!(i16); -impl_id!(i32); -impl_id!(i64); -impl_id!(i128); -impl_id!(u8); -impl_id!(u16); -impl_id!(u32); -impl_id!(u64); -impl_id!(u128); -impl_id!(bool); diff --git a/src/serde_error.rs b/src/serde_error.rs deleted file mode 100644 index 5a25995..0000000 --- a/src/serde_error.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[cfg(feature = "serde")] -#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] -pub enum IdentifiedVecOfSerdeFailure { - #[error("Duplicate element at offset {0}")] - DuplicateElementsAtIndex(usize), -} diff --git a/src/vec.rs b/src/vec.rs deleted file mode 100644 index 38c0808..0000000 --- a/src/vec.rs +++ /dev/null @@ -1,1133 +0,0 @@ -use std::collections::HashMap; -use std::fmt::{Debug, Display}; -use std::hash::{Hash, Hasher}; - -pub trait IsIdentifiableVec: Sized { - type Element; - type ID: Eq + Hash + Clone + Debug; - - /// Constructs a new, empty `IdentifiedVec` with the specified - /// `id_of_element` closure - fn new_identifying_element(id_of_element: fn(&Self::Element) -> Self::ID) -> Self; - - /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to - /// determine the element for any elements with duplicate identity. - /// - /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements - /// that may not have unique ids. This initializer calls the `combine` closure with the current - /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element - /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them - /// to produce a new element, or even throw an error. - /// - /// - Parameters: - /// - elements: A sequence of elements to use for the new `identified_vec`. - /// - id_of_element: The function which extracts the identifier for an element, - /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. - /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` - /// implements high-quality hashing. - fn try_from_iter_select_unique_ids_with( - elements: It, - id_of_element: fn(&Self::Element) -> Self::ID, - combine: fn( - (usize, &Self::Element, &Self::Element), - ) -> Result, - ) -> Result - where - It: IntoIterator; - - fn from_iter_select_unique_ids_with( - elements: It, - id_of_element: fn(&Self::Element) -> Self::ID, - combine: fn((usize, &Self::Element, &Self::Element)) -> ConflictResolutionChoice, - ) -> Self - where - It: IntoIterator; - - /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. - fn ids(&self) -> &Vec; - - /// Returns the number of elements in the `identified_vec`, also referred to as its 'length'. - fn len(&self) -> usize; - - /// Returns the index for the given id. - /// - /// If an element identified by the given id is found in the `identified_vec`, this method returns an index - /// into the array that corresponds to the element. - /// - /// ``` - /// extern crate identified_vec; - /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; - /// - /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] - /// struct User { - /// id: &'static str, - /// } - /// - /// impl User { - /// fn new(id: &'static str) -> Self { - /// Self { id } - /// } - /// } - /// - /// impl Identifiable for User { - /// type ID = &'static str; - /// fn id(&self) -> Self::ID { - /// self.id - /// } - /// } - /// - /// let mut users = - /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); - /// - /// assert_eq!(users.index_of_id(&"u_1729"), Some(1)); - /// assert_eq!(users.index_of_id(&"u_1337"), None); - /// ``` - /// - /// - Parameter id: The id to find in the `identified_vec`. - /// - Returns: The index for the element identified by `id` if found in the `identified_vec`; otherwise, - /// `None`. - /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. - fn index_of_id(&self, id: &Self::ID) -> Option; - - /// Returns a mutable reference to the element identified by `id` if any, else None. - /// - /// - Parameter id: The id to find in the `identified_vec`. - /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, - /// `None`. - /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. - fn get_mut(&mut self, id: &Self::ID) -> Option<&mut Self::Element>; - - fn elements(&self) -> Vec<&Self::Element>; - - /// Returns `true` if the `identified_vec` contains the `element.` - fn contains(&self, element: &Self::Element) -> bool; - - /// Returns `true if the `identified_vec` contains an element for the specified `id` - fn contains_id(&self, id: &Self::ID) -> bool; - - /// Returns a reference to the element corresponding to the `id`, if found, else `None`. - fn get(&self, id: &Self::ID) -> Option<&Self::Element>; - - /// Returns a reference to the element at index if found, else `None`. - fn get_at_index(&self, index: usize) -> Option<&Self::Element>; - - /// Append a new member to the end of the `identified_vec`, if the `identified_vec` doesn't already contain it. - /// - /// - Parameter item: The element to add to the `identified_vec`. - /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether - /// the operation added a new element, and `index` is the index of `item` in the resulting - /// `identified_vec`. - /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on - /// the `Self::ID` type, if it implements high-quality hashing. - fn append(&mut self, element: Self::Element) -> (bool, usize); - - /// Append the contents of an iterator to the end of the set, excluding elements that are already - /// members. - /// - /// - Parameter elements: A finite sequence of elements to append. - /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::Element` type, if it implements high-quality hashing. - fn append_other(&mut self, other: It) - where - It: IntoIterator; - - /// Adds the given element to the `identified_vec` unconditionally, either appending it to the `identified_vec``, or - /// replacing an existing value if it's already present. - /// - /// - Parameter item: The value to append or replace. - /// - Returns: The original element that was replaced by this operation, or `None` if the value was - /// appended to the end of the collection. - /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::ID` type, if it implements high-quality hashing. - fn update_or_append(&mut self, element: Self::Element) -> Option; - - /// Replace the member at the given index with a new value of the same identity. - /// - /// - Parameter item: The new value that should replace the original element. `item` must match - /// the identity of the original value. - /// - Parameter index: The index of the element to be replaced. - /// - Returns: The original element that was replaced. - fn update_at(&mut self, element: Self::Element, index: usize) -> Self::Element; - - /// Insert a new member to this identified_vec at the specified index, if the identified_vec doesn't already contain - /// it. - /// - /// - Parameter element: The element to insert. - /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether - /// the operation added a new element, and `index` is the index of `element` in the resulting - /// identified_vec. If `inserted` is true, then the returned `index` may be different from the index - /// requested. - /// - /// - Complexity: The operation is expected to perform amortized O(`self.count`) copy, hash, and - /// compare operations on the `Self::ID` type, if it implements high-quality hashing. (Insertions need - /// to make room in the storage identified_vec to add the inserted element.) - fn insert(&mut self, element: Self::Element, at: usize) -> (bool, usize); - - /// Adds the given element into the set unconditionally, either inserting it at the specified - /// index, or replacing an existing value if it's already present. - /// - /// - Parameter item: The value to append or replace. - /// - Parameter index: The index at which to insert the new member if `item` isn't already in the - /// set. - /// - Returns: The original element that was replaced by this operation, or `None` if the value was - /// newly inserted into the collection. - /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::ID` type, if it implements high-quality hashing. - fn update_or_insert( - &mut self, - element: Self::Element, - index: usize, - ) -> (Option, usize); - - ///////////// - // Remove // - ///////////// - - /// Removes the element identified by the given id from the `identified_vec`. - /// - /// ``` - /// extern crate identified_vec; - /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; - /// - /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] - /// struct User { - /// id: &'static str, - /// } - /// - /// impl User { - /// fn new(id: &'static str) -> Self { - /// Self { id } - /// } - /// } - /// - /// impl Identifiable for User { - /// type ID = &'static str; - /// fn id(&self) -> Self::ID { - /// self.id - /// } - /// } - /// - /// let mut users = - /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); - /// - /// assert_eq!(users.remove_by_id(&"u_1729"), Some(User::new("u_1729"))); - /// assert_eq!(users.elements(), [&User::new("u_42")]); - /// assert_eq!(users.remove_by_id(&"u_1337"), None); - /// assert_eq!(users.len(), 1); - /// ``` - /// - /// - Parameter id: The id of the element to be removed from the `identified_vec`. - /// - Returns: The element that was removed, or `None` if the element was not present in the array. - /// - Complexity: O(`count`) - fn remove_by_id(&mut self, id: &Self::ID) -> Option; - - /// Removes the given element from the `identified_vec`. - /// - /// If the element is found in the `identified_vec`, this method returns the element. - /// - /// If the element isn't found in the `identified_vec`, `remove` returns `None`. - /// - /// - Parameter element: The element to remove. - /// - Returns: The value that was removed, or `None` if the element was not present in the `identified_vec`. - /// - Complexity: O(`count`) - fn remove(&mut self, element: &Self::Element) -> Option; - - /// Removes and returns the element at the specified position. - /// - /// All the elements following the specified position are moved to close the resulting gap. - /// - /// - Parameter index: The position of the element to remove. - /// - Returns: The removed element. - /// - Precondition: `index` must be a valid index of the collection that is not equal to the - /// collection's end index. - /// - Complexity: O(`count`) - fn remove_at(&mut self, index: usize) -> Self::Element; - - /// Removes all the elements at the specified `offsets` from the `identified_vec`. - /// - /// - Parameter offsets: The offsets of all elements to be removed. - /// - Complexity: O(*n*) where *n* is the length of the `identified_vec`. - fn remove_at_offsets(&mut self, offsets: It) - where - It: IntoIterator; -} - -/// Representation of a choice in a conflict resolution -/// where two elements with the same Self::ID exists, if `ChooseFirst`, -/// is specified the first/current/existing value will be used, but -/// if `ChooseLast` is specified then the new/last will be replace -/// the first/current/existing. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum ConflictResolutionChoice { - ChooseFirst, - ChooseLast, -} - -/// An ordered collection of identifiable elements. -/// -/// Similar to the standard `Vec`, identified vecs maintain their elements in a particular -/// user-specified order, and they support efficient random access traversal of their members. -/// However, unlike `Vec`, identified vecs introduce the ability to uniquely identify elements, -/// using a hash table to ensure that no two elements have the same identity, and to efficiently -/// look up elements corresponding to specific identifiers. -/// -/// `IdentifiedVec` is a useful alternative to `Vec` when you need to be able to efficiently -/// access unique elements by a stable identifier. It is also a useful alternative to `BTreeSet`, -/// where the `Ord` requirement may be too strict, an a useful alternative to `HashSet` where -/// `Hash` requirement may be too strict. -/// -/// You can create an identified vec with any element type that implements the `Identifiable` -/// trait. -/// -/// ``` -/// extern crate identified_vec; -/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf, IsIdentifiableVec}; -/// use std::cell::RefCell; -/// -/// #[derive(Eq, PartialEq, Clone, Debug)] -/// struct User { -/// id: &'static str, -/// name: RefCell<&'static str>, -/// } -/// -/// impl User { -/// fn new(id: &'static str, name: &'static str) -> Self { -/// Self { -/// id, -/// name: RefCell::new(name), -/// } -/// } -/// fn name(&self) -> &'static str { -/// *self.name.borrow() -/// } -/// } -/// -/// impl Identifiable for User { -/// type ID = &'static str; -/// fn id(&self) -> Self::ID { -/// self.id -/// } -/// } -/// -/// let mut users = IdentifiedVecOf::::from_iter([ -/// User::new("u_42", "Satoshi Nakamoto"), -/// User::new("u_1337", "Leia Skywalker"), -/// ]); -/// -/// assert_eq!( -/// users.get(&"u_42").map(|u| u.name()), -/// Some("Satoshi Nakamoto") -/// ); -/// assert_eq!( -/// users.get_at_index(1).map(|u| u.name()), -/// Some("Leia Skywalker") -/// ); -/// users.append(User::new("u_237", "Alan Turing")); -/// assert_eq!( -/// users.elements(), -/// [ -/// User::new("u_42", "Satoshi Nakamoto"), -/// User::new("u_1337", "Leia Skywalker"), -/// User::new("u_237", "Alan Turing"), -/// ] -/// .iter() -/// .collect::>() -/// ); -/// -/// // Self::Element with same Self::ID is not appended: -/// users.append(User::new("u_42", "Tom Mervolo Dolder")); -/// assert_eq!( -/// users.elements(), -/// [ -/// User::new("u_42", "Satoshi Nakamoto"), -/// User::new("u_1337", "Leia Skywalker"), -/// User::new("u_237", "Alan Turing"), -/// ] -/// .iter() -/// .collect::>() -/// ); -/// -/// // Self::Element with same Self::ID replaces existing if an `update_*` method is used: -/// // e.g. `update_or_insert`: -/// users.update_or_insert(User::new("u_42", "Tom Mervolo Dolder"), 0); -/// assert_eq!( -/// users.elements(), -/// [ -/// User::new("u_42", "Tom Mervolo Dolder"), -/// User::new("u_1337", "Leia Skywalker"), -/// User::new("u_237", "Alan Turing"), -/// ] -/// .iter() -/// .collect::>() -/// ); -/// -/// // or `update_or_append` -/// users.update_or_append(User::new("u_237", "Marie Curie")); -/// assert_eq!( -/// users.elements(), -/// [ -/// User::new("u_42", "Tom Mervolo Dolder"), -/// User::new("u_1337", "Leia Skywalker"), -/// User::new("u_237", "Marie Curie"), -/// ] -/// .iter() -/// .collect::>() -/// ); -/// -/// // or mutate with `get_mut(id)` -/// *users.get_mut(&"u_1337").unwrap().name.get_mut() = "Yoda"; -/// assert_eq!( -/// users.elements(), -/// [ -/// User::new("u_42", "Tom Mervolo Dolder"), -/// User::new("u_1337", "Yoda"), -/// User::new("u_237", "Marie Curie"), -/// ] -/// .iter() -/// .collect::>() -/// ); -/// ``` -/// -/// Or you can provide a closure that describes an element's identity: -/// -/// ``` -/// /// extern crate identified_vec; -/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf, IsIdentifiableVec}; -/// -/// let numbers = IdentifiedVec::::new_identifying_element(|e| *e); -/// ``` -/// -/// # Motivation -/// None of the std collections `BTreeSet` and `HashSet` retain insertion order, `Vec` retains -/// insertion order, however, it allows for duplicates. So if you want a collection of unique -/// elements (Set-like) that does retain insertion order, `IdentifiedVec` suits your needs. -/// Even better, the elements does not need to be to impl `Hash` nor `Ord``. -/// -/// # Performance -/// -/// Like the standard `HashMap` type, the performance of hashing operations in -/// `IdentifiedVec` is highly sensitive to the quality of hashing implemented by the `Self::ID` -/// type. Failing to correctly implement hashing can easily lead to unacceptable performance, with -/// the severity of the effect increasing with the size of the underlying hash table. -/// -/// In particular, if a certain set of elements all produce the same hash value, then hash table -/// lookups regress to searching an element in an unsorted array, i.e., a linear operation. To -/// ensure hashed collection types exhibit their target performance, it is important to ensure that -/// such collisions cannot be induced merely by adding a particular list of members to the set. -/// -/// When `Self::ID` implements `Hash` correctly, testing for membership in an ordered set is expected -/// to take O(1) equality checks on average. Hash collisions can still occur organically, so the -/// worst-case lookup performance is technically still O(*n*) (where *n* is the size of the set); -/// however, long lookup chains are unlikely to occur in practice. -/// -/// ## Implementation Details -/// -/// An identified vec consists of a Vec and a HashMap of id-element pairs. An element's id -/// should not be mutated in place, as it will drift from its associated dictionary key. Identified -/// bec is designed to avoid this invariant. Mutating an element's id will result in a runtime error. -#[derive(Debug, Clone)] -pub struct IdentifiedVec -where - I: Eq + Hash + Clone + Debug, -{ - /// The holder of the insertion order - pub(crate) order: Vec, - - /// The storage of elements. - pub(crate) elements: HashMap, - - /// Function which extracts the Self::ID of an Self::Element. - pub(crate) _id_of_element: fn(&E) -> I, -} - -impl IsIdentifiableVec for IdentifiedVec -where - I: Eq + Hash + Clone + Debug, -{ - //////////////////// - // Constructors // - //////////////////// - - type ID = I; - type Element = E; - - /// Constructs a new, empty `IdentifiedVec` with the specified - /// `id_of_element` closure - #[inline] - fn new_identifying_element(id_of_element: fn(&E) -> I) -> Self { - Self { - order: Vec::new(), - elements: HashMap::new(), - _id_of_element: id_of_element, - } - } - - /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to - /// determine the element for any elements with duplicate identity. - /// - /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements - /// that may not have unique ids. This initializer calls the `combine` closure with the current - /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element - /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them - /// to produce a new element, or even throw an error. - /// - /// - Parameters: - /// - elements: A sequence of elements to use for the new `identified_vec`. - /// - id_of_element: The function which extracts the identifier for an element, - /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. - /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` - /// implements high-quality hashing. - #[cfg(not(tarpaulin_include))] // false negative - #[inline] - fn try_from_iter_select_unique_ids_with( - elements: It, - id_of_element: fn(&Self::Element) -> Self::ID, - combine: fn( - (usize, &Self::Element, &Self::Element), - ) -> Result, - ) -> Result - where - It: IntoIterator, - { - let mut _order = Vec::::new(); - let mut _elements = HashMap::::new(); - - for element in elements.into_iter() { - let id = id_of_element(&element); - match _elements.remove(&id) { - Some(existing) => match combine((_order.len(), &existing, &element)) { - Err(e) => return Err(e), - Ok(choice) => match choice { - ConflictResolutionChoice::ChooseFirst => { - _elements.insert(id.clone(), existing) - } - ConflictResolutionChoice::ChooseLast => { - _elements.insert(id.clone(), element) - } - }, - }, - None => { - _elements.insert(id.clone(), element); - _order.push(id); - None - } - }; - } - - Ok(Self { - order: _order, - _id_of_element: id_of_element, - elements: _elements, - }) - } - - /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to - /// determine the element for any elements with duplicate identity. - /// - /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements - /// that may not have unique ids. This initializer calls the `combine` closure with the current - /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element - /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them - /// to produce a new element, or even throw an error. - /// - /// - Parameters: - /// - elements: A sequence of elements to use for the new `identified_vec`. - /// - id_of_element: The function which extracts the identifier for an element, - /// - combine: Closure used combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`) - /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` - /// implements high-quality hashing. - #[cfg(not(tarpaulin_include))] // false negative - #[inline] - fn from_iter_select_unique_ids_with( - elements: It, - id_of_element: fn(&Self::Element) -> Self::ID, - combine: fn((usize, &Self::Element, &Self::Element)) -> ConflictResolutionChoice, - ) -> Self - where - It: IntoIterator, - { - let mut _order = Vec::::new(); - let mut _elements = HashMap::::new(); - - for element in elements.into_iter() { - let id = id_of_element(&element); - match _elements.remove(&id) { - Some(existing) => match combine((_order.len(), &existing, &element)) { - ConflictResolutionChoice::ChooseFirst => _elements.insert(id.clone(), existing), - ConflictResolutionChoice::ChooseLast => _elements.insert(id.clone(), element), - }, - None => { - _elements.insert(id.clone(), element); - _order.push(id); - None - } - }; - } - - Self { - order: _order, - _id_of_element: id_of_element, - elements: _elements, - } - } - - //////////////////// - // Public Get // - //////////////////// - - /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. - /// - /// - Complexity: O(1) - #[inline] - fn ids(&self) -> &Vec { - &self.order - } - - /// Returns the number of elements in the `identified_vec`, also referred to as its 'length'. - #[inline] - fn len(&self) -> usize { - if cfg!(debug_assertions) { - assert_eq!(self.order.len(), self.elements.len()); - } - self.order.len() - } - - /// Returns the index for the given id. - /// - /// If an element identified by the given id is found in the `identified_vec`, this method returns an index - /// into the array that corresponds to the element. - /// - /// ``` - /// extern crate identified_vec; - /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; - /// - /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] - /// struct User { - /// id: &'static str, - /// } - /// - /// impl User { - /// fn new(id: &'static str) -> Self { - /// Self { id } - /// } - /// } - /// - /// impl Identifiable for User { - /// type ID = &'static str; - /// fn id(&self) -> Self::ID { - /// self.id - /// } - /// } - /// - /// let mut users = - /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); - /// - /// assert_eq!(users.index_of_id(&"u_1729"), Some(1)); - /// assert_eq!(users.index_of_id(&"u_1337"), None); - /// ``` - /// - /// - Parameter id: The id to find in the `identified_vec`. - /// - Returns: The index for the element identified by `id` if found in the `identified_vec`; otherwise, - /// `None`. - /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. - #[inline] - fn index_of_id(&self, id: &Self::ID) -> Option { - self.order.iter().position(|i| i == id) - } - - /// Returns a mutable reference to the element identified by `id` if any, else None. - /// - /// - Parameter id: The id to find in the `identified_vec`. - /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, - /// `None`. - /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. - #[inline] - fn get_mut(&mut self, id: &Self::ID) -> Option<&mut Self::Element> { - self.elements.get_mut(id) - } - - //////////////////// - // Public Get // - //////////////////// - - /// A read-only collection of references to the elements contained in this array, as a `Vec<&Elements>`. - /// - /// N.B. that this method is not constant time. - /// - /// If `Self::Element` implements `Clone` you can use `self.items()` which returns a `Vec`, of cloned elements. - /// - /// - Complexity: O(n) - #[inline] - fn elements(&self) -> Vec<&Self::Element> { - self.iter().collect() - } - - /// Returns `true` if the `identified_vec` contains the `element.` - #[inline] - fn contains(&self, element: &Self::Element) -> bool { - self.elements.contains_key(&self.id(&element)) - } - - /// Returns `true if the `identified_vec` contains an element for the specified `id` - #[inline] - fn contains_id(&self, id: &Self::ID) -> bool { - self.elements.contains_key(id) - } - - /// Returns a reference to the element corresponding to the `id`, if found, else `None`. - #[inline] - fn get(&self, id: &Self::ID) -> Option<&Self::Element> { - self.elements.get(id) - } - - /// Returns a reference to the element at index if found, else `None`. - #[inline] - fn get_at_index(&self, index: usize) -> Option<&Self::Element> { - self.order.get(index).and_then(|id| self.get(id)) - } - - /// Append a new member to the end of the `identified_vec`, if the `identified_vec` doesn't already contain it. - /// - /// - Parameter item: The element to add to the `identified_vec`. - /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether - /// the operation added a new element, and `index` is the index of `item` in the resulting - /// `identified_vec`. - /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on - /// the `Self::ID` type, if it implements high-quality hashing. - #[inline] - fn append(&mut self, element: E) -> (bool, usize) { - self.insert(element, self.end_index()) - } - - /// Append the contents of an iterator to the end of the set, excluding elements that are already - /// members. - /// - /// - Parameter elements: A finite sequence of elements to append. - /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::Element` type, if it implements high-quality hashing. - #[inline] - fn append_other(&mut self, other: It) - where - It: IntoIterator, - { - other.into_iter().for_each(|i| _ = self.append(i)) - } - - /// Adds the given element to the `identified_vec` unconditionally, either appending it to the `identified_vec``, or - /// replacing an existing value if it's already present. - /// - /// - Parameter item: The value to append or replace. - /// - Returns: The original element that was replaced by this operation, or `None` if the value was - /// appended to the end of the collection. - /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::ID` type, if it implements high-quality hashing. - #[inline] - fn update_or_append(&mut self, element: E) -> Option { - let id = self.id(&element); - self._update_value(element, id) - } - - /// Replace the member at the given index with a new value of the same identity. - /// - /// - Parameter item: The new value that should replace the original element. `item` must match - /// the identity of the original value. - /// - Parameter index: The index of the element to be replaced. - /// - Returns: The original element that was replaced. - /// - Complexity: Amortized O(1). - #[inline] - fn update_at(&mut self, element: E, index: usize) -> E { - let old_id = self - .order - .get(index) - .expect("Expected element at index {index}"); - let id = self.id(&element); - assert_eq!( - &id, old_id, - "The replacement item must match the identity of the original" - ); - return self - ._update_value_inserting_at(element, id, index) - .0 - .expect("Replaced old value"); - } - - /// Insert a new member to this identified_vec at the specified index, if the identified_vec doesn't already contain - /// it. - /// - /// - Parameter element: The element to insert. - /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether - /// the operation added a new element, and `index` is the index of `element` in the resulting - /// identified_vec. If `inserted` is true, then the returned `index` may be different from the index - /// requested. - /// - /// - Complexity: The operation is expected to perform amortized O(`self.count`) copy, hash, and - /// compare operations on the `Self::ID` type, if it implements high-quality hashing. (Insertions need - /// to make room in the storage identified_vec to add the inserted element.) - #[cfg(not(tarpaulin_include))] // false negative - #[inline] - fn insert(&mut self, element: E, at: usize) -> (bool, usize) { - let id = self.id(&element); - if let Some(existing) = self.index_of_id(&id) { - return (false, existing.clone()); - } - self._update_value_inserting_at(element, id, at); - (true, at) - } - - /// Adds the given element into the set unconditionally, either inserting it at the specified - /// index, or replacing an existing value if it's already present. - /// - /// - Parameter item: The value to append or replace. - /// - Parameter index: The index at which to insert the new member if `item` isn't already in the - /// set. - /// - Returns: The original element that was replaced by this operation, or `None` if the value was - /// newly inserted into the collection. - /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::ID` type, if it implements high-quality hashing. - #[inline] - fn update_or_insert(&mut self, element: E, index: usize) -> (Option, usize) { - let id = self.id(&element); - self._update_value_inserting_at(element, id, index) - } - - //////////////////// - // Public Remove // - //////////////////// - /// Removes the element identified by the given id from the `identified_vec`. - /// - /// ``` - /// extern crate identified_vec; - /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; - /// - /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] - /// struct User { - /// id: &'static str, - /// } - /// - /// impl User { - /// fn new(id: &'static str) -> Self { - /// Self { id } - /// } - /// } - /// - /// impl Identifiable for User { - /// type ID = &'static str; - /// fn id(&self) -> Self::ID { - /// self.id - /// } - /// } - /// - /// let mut users = - /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); - /// - /// assert_eq!(users.remove_by_id(&"u_1729"), Some(User::new("u_1729"))); - /// assert_eq!(users.elements(), [&User::new("u_42")]); - /// assert_eq!(users.remove_by_id(&"u_1337"), None); - /// assert_eq!(users.len(), 1); - /// ``` - /// - /// - Parameter id: The id of the element to be removed from the `identified_vec`. - /// - Returns: The element that was removed, or `None` if the element was not present in the array. - /// - Complexity: O(`count`) - #[cfg(not(tarpaulin_include))] // false negative - #[inline] - fn remove_by_id(&mut self, id: &I) -> Option { - match self.index_of_id(id) { - Some(index) => { - self.order.remove(index); - return self.elements.remove(id); - } - None => { - assert!(!self.elements.contains_key(id)); - return None; - } - } - } - - /// Removes the given element from the `identified_vec`. - /// - /// If the element is found in the `identified_vec`, this method returns the element. - /// - /// If the element isn't found in the `identified_vec`, `remove` returns `None`. - /// - /// - Parameter element: The element to remove. - /// - Returns: The value that was removed, or `None` if the element was not present in the `identified_vec`. - /// - Complexity: O(`count`) - #[inline] - fn remove(&mut self, element: &E) -> Option { - self.remove_by_id(&self.id(element)) - } - - /// Removes and returns the element at the specified position. - /// - /// All the elements following the specified position are moved to close the resulting gap. - /// - /// - Parameter index: The position of the element to remove. - /// - Returns: The removed element. - /// - Precondition: `index` must be a valid index of the collection that is not equal to the - /// collection's end index. - /// - Complexity: O(`count`) - #[inline] - fn remove_at(&mut self, index: usize) -> E { - let id = self - .order - .get(index) - .expect("Precondition failure, index out of bounds"); - let removed = self - .elements - .remove(id) - .expect("Self::Element of existing id"); - self.order.remove(index); - return removed; - } - - /// Removes all the elements at the specified `offsets` from the `identified_vec`. - /// - /// - Parameter offsets: The offsets of all elements to be removed. - /// - Complexity: O(*n*) where *n* is the length of the `identified_vec`. - #[inline] - fn remove_at_offsets(&mut self, offsets: It) - where - It: IntoIterator, - { - let mut internal_offset = 0; - offsets.into_iter().for_each(|i| { - _ = self.remove_at(i - internal_offset); - internal_offset += 1; - }) - } -} - -impl IdentifiedVec -where - E: Clone, - I: Eq + Hash + Clone + Debug, -{ - /// A read-only collection of clones of the elements contained in this array, as a `Vec`. - /// - /// N.B. that this method is not constant time. - /// - /// Use `self.elements()` if you are looking for a collection of references. - /// - /// - Complexity: O(n) - #[inline] - pub fn items(&self) -> Vec { - self.iter().map(|e| e.clone()).collect() - } -} - -/// An iterator over the items of an `IdentifiedVec`. -pub struct IdentifiedVecIterator<'a, I, E> -where - I: Eq + Hash + Clone + Debug, -{ - identified_vec: &'a IdentifiedVec, - index: usize, -} - -impl<'a, I, E> Iterator for IdentifiedVecIterator<'a, I, E> -where - I: Eq + Hash + Clone + Debug, -{ - type Item = &'a E; - - fn next(&mut self) -> Option { - if self.index < self.identified_vec.len() { - let id = Some(&self.identified_vec.order[self.index]).unwrap(); - self.index += 1; - return self.identified_vec.get(id); - } else { - None - } - } -} - -impl IdentifiedVec -where - I: Eq + Hash + Clone + Debug, -{ - pub fn iter(&self) -> IdentifiedVecIterator { - IdentifiedVecIterator { - identified_vec: self, - index: 0, - } - } -} - -/// An owning iterator over the items of an `IdentifiedVec`. -pub struct IdentifiedVecIntoIterator -where - I: Eq + Hash + Clone + Debug, -{ - identified_vec: IdentifiedVec, -} - -impl Iterator for IdentifiedVecIntoIterator -where - I: Eq + Hash + Clone + Debug, -{ - type Item = E; - - fn next(&mut self) -> Option { - if self.identified_vec.len() == 0 { - return None; - } - let result = self.identified_vec.remove_at(0); - Some(result) - } -} - -impl IntoIterator for IdentifiedVec -where - I: Eq + Hash + Clone + Debug, -{ - type Item = E; - type IntoIter = IdentifiedVecIntoIterator; - - fn into_iter(self) -> Self::IntoIter { - Self::IntoIter { - identified_vec: self, - } - } -} - -impl IdentifiedVec where I: Eq + Hash + Clone + Debug {} - -/////////////////////// -//// Eq /// -/////////////////////// -impl PartialEq for IdentifiedVec -where - E: PartialEq, - I: Eq + Hash + Clone + Debug, -{ - fn eq(&self, other: &Self) -> bool { - self.elements() == other.elements() - } -} - -impl Eq for IdentifiedVec -where - E: Eq, - I: Eq + Hash + Clone + Debug, -{ -} - -/////////////////////// -//// Hash /// -/////////////////////// -impl Hash for IdentifiedVec -where - E: Hash, - I: Eq + Hash + Clone + Debug, -{ - fn hash(&self, state: &mut H) { - self.elements().hash(state); - } -} - -/////////////////////// -//// Display /// -/////////////////////// -impl Display for IdentifiedVec -where - E: Debug, - I: Eq + Hash + Clone + Debug, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.elements().fmt(f) - } -} - -/////////////////////// -//// PRIVATE /// -/////////////////////// -impl IdentifiedVec -where - I: Eq + Hash + Clone + Debug, -{ - /// Next index for element appended - #[inline] - fn end_index(&self) -> usize { - self.len() - } - - /// Returns the Self::ID of an Self::Element - #[inline] - fn id(&self, of: &E) -> I { - (self._id_of_element)(of) - } - - /// Inserting Self::ID at an index, returning if it did, if not, the index of the existing. - #[cfg(not(tarpaulin_include))] // false negative - #[inline] - fn _insert_id_at(&mut self, id: I, index: usize) -> (bool, usize) { - match self.index_of_id(&id) { - Some(existing) => (false, existing), - None => { - self.order.insert(index, id); - (true, index) - } - } - } - - #[inline] - fn _update_value(&mut self, element: E, for_key: I) -> Option { - let value = element; - let key = for_key; - - let maybe_old = self.elements.remove(&key); - self.elements.insert(key.clone(), value); - - if maybe_old.is_some() { - return maybe_old; - } else { - self.order.push(key); - None - } - } - - #[inline] - fn _update_value_inserting_at( - &mut self, - element: E, - for_key: I, - index: usize, - ) -> (Option, usize) { - let id = for_key; - let value = element; - - let (inserted, offset) = self._insert_id_at(id.clone(), index); - if inserted { - assert_eq!(offset, index); - self.elements.insert(id.clone(), value); - return (None, offset); - } - let old = self.elements.remove(&id).expect("existing element"); - self.elements.insert(id, value); - return (Some(old), offset); - } -} - -/////////////////////// -//// DEBUG /// -/////////////////////// -impl IdentifiedVec -where - E: Debug, - I: Eq + Hash + Clone + Debug, -{ - #[cfg(not(tarpaulin_include))] - #[cfg(debug_assertions)] - pub fn debug(&self) { - println!("{}", self.debug_str()); - } - - #[cfg(debug_assertions)] - pub fn debug_str(&self) -> String { - format!("order: {:?}\nelements: {:?}", self.order, self.elements) - } -} diff --git a/src/vec_of.rs b/src/vec_of.rs deleted file mode 100644 index f915195..0000000 --- a/src/vec_of.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::collections::HashMap; - -#[cfg(feature = "serde")] -use std::fmt::Debug; - -#[cfg(feature = "serde")] -use crate::serde_error::IdentifiedVecOfSerdeFailure; -use crate::{ - identified_vec_of::Identifiable, - vec::{ConflictResolutionChoice, IdentifiedVec, IsIdentifiableVec}, -}; - -#[cfg(feature = "serde")] -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; - -/////////////////////// -/// IdentifiedVecOf /// -/////////////////////// - -/// A type alias for `IdentifiedVec`, this is the -/// preferred and most powerful collection type of this crate, requires -/// that your `Element`s impl the `Identifiable` trait. Using this collection -/// allows you to skip passing the `id_of_element: fn(&Element) -> ID` closure -/// which you otherwise need to pass when initializing an `IdentifiedVec`. Using -/// `IdentifiedVecOf` together with feature "serde" also gives serde -/// serialization/deserialization as if it were a `Vec`, given that -/// `Element` implements serde serialization/deserialization of course. -pub type IdentifiedVecOf = IdentifiedVec<::ID, Element>; - -////////////////////////////////////////////// -/// Identifiable Element Constructors /// -////////////////////////////////////////////// -impl IdentifiedVec -where - Element: Identifiable, -{ - /// Constructs a new, empty `IdentifiedVec`, using `id()` on `Element` - /// as id function. - pub fn new() -> Self { - Self { - order: Vec::new(), - elements: HashMap::new(), - _id_of_element: |i| i.id(), - } - } - - /// Creates a new `IdentifiedVec` from the elements in the given sequence. - /// - /// You use this initializer to create an `IdentifiedVec` when you have a sequence of elements with unique - /// ids. Passing a sequence with duplicate ids to this initializer results in a runtime error. - /// - /// - Parameter elements: A sequence of elements to use for the new `IdentifiedVec`. Every element in - /// `elements` must have a unique id. - /// - Returns: A new `IdentifiedVec` initialized with the elements of `elements`. - /// - Precondition: The sequence must not have duplicate ids. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` - /// implements high-quality hashing. - #[inline] - pub fn from_iter(unique_elements: I) -> Self - where - I: IntoIterator, - { - let mut _self = Self::new(); - unique_elements - .into_iter() - .for_each(|e| _ = _self.append(e)); - return _self; - } - - /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to - /// determine the element for any elements with duplicate ids. - /// - /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements - /// that may not have unique ids. This initializer calls the `combine` closure with the current - /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element - /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them - /// to produce a new element, or even throw an error. - /// - /// - Parameters: - /// - elements: A sequence of elements to use for the new `identified_vec`. - /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. - /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` - /// implements high-quality hashing. - #[inline] - pub fn try_from_iter_select_unique_with( - elements: I, - combine: fn((usize, &Element, &Element)) -> Result, - ) -> Result - where - I: IntoIterator, - { - Self::try_from_iter_select_unique_ids_with(elements, |e| e.id(), combine) - } - - /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to - /// determine the element for any elements with duplicate ids. - /// - /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements - /// that may not have unique ids. This initializer calls the `combine` closure with the current - /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element - /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them - /// to produce a new element, or even throw an error. - /// - /// - Parameters: - /// - elements: A sequence of elements to use for the new `identified_vec`. - /// - combine: Closure used combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`) - /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` - /// implements high-quality hashing. - #[inline] - pub fn from_iter_select_unique_with( - elements: I, - combine: fn((usize, &Element, &Element)) -> ConflictResolutionChoice, - ) -> Self - where - I: IntoIterator, - { - Self::from_iter_select_unique_ids_with(elements, |e| e.id(), combine) - } -} - -/////////////////////// -//// SERDE /// -/////////////////////// -#[cfg(feature = "serde")] -impl Serialize for IdentifiedVecOf -where - Element: Serialize + Identifiable + Debug + Clone, -{ - fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> - where - S: Serializer, - { - Vec::serialize(&self.elements(), serializer) - } -} - -#[cfg(feature = "serde")] -impl<'de, Element> Deserialize<'de> for IdentifiedVecOf -where - Element: Deserialize<'de> + Identifiable + Debug + Clone, -{ - #[cfg(not(tarpaulin_include))] // false negative - fn deserialize>( - deserializer: D, - ) -> Result, D::Error> { - let elements = Vec::::deserialize(deserializer)?; - IdentifiedVecOf::::try_from_iter_select_unique_with(elements, |(idx, _, _)| { - Err(IdentifiedVecOfSerdeFailure::DuplicateElementsAtIndex(idx)) - }) - .map_err(de::Error::custom) - } -} From 813ce09d76c3c9740f21cd769ae5c0540c7e0fdf Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 13 Dec 2023 22:10:50 +0100 Subject: [PATCH 03/24] WIP --- Cargo.lock | 16 +- Cargo.toml | 2 +- identified_vec/Cargo.lock | 184 +++ identified_vec/Cargo.toml | 20 + identified_vec/src/identifiable_trait.rs | 18 + identified_vec/src/lib.rs | 161 +++ identified_vec/src/primitives_identifiable.rs | 26 + identified_vec/src/serde_error.rs | 6 + identified_vec/src/vec.rs | 1133 +++++++++++++++++ identified_vec/src/vec_of.rs | 154 +++ identified_vec_macros/Cargo.toml | 14 + identified_vec_macros/src/lib.rs | 10 + tests/tests.rs | 15 +- 13 files changed, 1752 insertions(+), 7 deletions(-) create mode 100644 identified_vec/Cargo.lock create mode 100644 identified_vec/Cargo.toml create mode 100644 identified_vec/src/identifiable_trait.rs create mode 100644 identified_vec/src/lib.rs create mode 100644 identified_vec/src/primitives_identifiable.rs create mode 100644 identified_vec/src/serde_error.rs create mode 100644 identified_vec/src/vec.rs create mode 100644 identified_vec/src/vec_of.rs create mode 100644 identified_vec_macros/Cargo.toml create mode 100644 identified_vec_macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 0a90669..ba5a6d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,11 +23,7 @@ dependencies = [ name = "identified_vec" version = "0.1.4" dependencies = [ - "identified_vec", - "maplit", - "rand", "serde", - "serde_json", "thiserror", ] @@ -161,6 +157,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tests" +version = "0.1.0" +dependencies = [ + "identified_vec", + "identified_vec_macros", + "maplit", + "rand", + "serde", + "serde_json", +] + [[package]] name = "thiserror" version = "1.0.50" diff --git a/Cargo.toml b/Cargo.toml index 7a5579a..eaa688f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,4 +2,4 @@ resolver = "2" -members = ["identified_vec", "identified_vec_macros"] +members = ["identified_vec", "identified_vec_macros", "tests"] diff --git a/identified_vec/Cargo.lock b/identified_vec/Cargo.lock new file mode 100644 index 0000000..12f4005 --- /dev/null +++ b/identified_vec/Cargo.lock @@ -0,0 +1,184 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "identified_vec" +version = "0.1.4" +dependencies = [ + "identified_vec", + "maplit", + "rand", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/identified_vec/Cargo.toml b/identified_vec/Cargo.toml new file mode 100644 index 0000000..58e38de --- /dev/null +++ b/identified_vec/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "identified_vec" +version = "0.1.4" +edition = "2021" +authors = ["Alexander Cyon "] +description = "Like HashSet but retaining INSERTION order and without `Hash` requirement on the Element type." +license = "MIT" +readme = "README.md" +repository = "https://github.com/Sajjon/identified_vec" +keywords = ["identifiable", "vec", "orderset", "set", "hashset"] +categories = ["data-structures"] + +[features] +default = ["id_prim"] +serde = ["dep:serde"] +id_prim = [] + +[dependencies] +serde = { version = "1.0.193", optional = true } +thiserror = "1.0.50" \ No newline at end of file diff --git a/identified_vec/src/identifiable_trait.rs b/identified_vec/src/identifiable_trait.rs new file mode 100644 index 0000000..db68826 --- /dev/null +++ b/identified_vec/src/identifiable_trait.rs @@ -0,0 +1,18 @@ +use std::fmt::Debug; +use std::hash::Hash; + +/// The `Identifiable` trait allows you to use the +/// `IdentifiedVecOf instead of the more verbose +/// `IdentifiedVec` but also allows you to +/// skip the `id_of_element: fn(&Element) -> ID` closure when +/// initializing a new identified vec. +pub trait Identifiable { + /// The type that your `Element` will use as its globally unique and stable ID, + /// must impl `Hash` since it is used as a key in `IdentifiedVecOf`'s internal + /// `HashMap`. Must impl `Clone` since we need to be able to clone it as a key + type ID: Eq + Hash + Clone + Debug; + + /// Return `Element`'s globally unique and stable ID, used to uniquely identify + /// the `Element` in the `IdentifiedVecOf` collection of elements. + fn id(&self) -> Self::ID; +} diff --git a/identified_vec/src/lib.rs b/identified_vec/src/lib.rs new file mode 100644 index 0000000..c5b436d --- /dev/null +++ b/identified_vec/src/lib.rs @@ -0,0 +1,161 @@ +//! A collection of unique identifiable elements which retains **insertion** order, inspired by [Pointfree's Swift Identified Collections](https://github.com/pointfreeco/swift-identified-collections). +//! +//! Similar to the standard `Vec`, the `IdentifiedVec` maintain their elements in a particular user-specified order. However, unlike `Vec`, the `IdentifiedVec` introduce the ability to uniquely identify elements, using a hash table to ensure that no two elements have the same identity, and to efficiently look up elements corresponding to specific identifiers. +//! +//! `IdentifiedVec` is a useful alternative to `Vec` when you need to be able to efficiently access unique elements by a stable identifier. It is also a useful alternative to `BTreeSet`, where the `Ord` trait requirement may be too strict, an a useful alternative to `HashSet` where `Hash` trait requirement may be too strict. +//! +//! You can create an identified vec with any element type that implements the `Identifiable` trait. +//! +//! # Example +//! +//! ``` +//! extern crate identified_vec; +//! use identified_vec::{IsIdentifiableVec, IdentifiedVec, IdentifiedVecOf, Identifiable}; +//! use std::cell::RefCell; +//! +//! #[derive(Eq, PartialEq, Clone, Debug)] +//! struct User { +//! id: &'static str, +//! name: RefCell<&'static str>, +//! } +//! +//! impl User { +//! fn new(id: &'static str, name: &'static str) -> Self { +//! Self { +//! id, +//! name: RefCell::new(name), +//! } +//! } +//! fn name(&self) -> &'static str { +//! *self.name.borrow() +//! } +//! } +//! +//! impl Identifiable for User { +//! type ID = &'static str; +//! fn id(&self) -> Self::ID { +//! self.id +//! } +//! } +//! +//! let mut users = IdentifiedVecOf::::from_iter([ +//! User::new("u_42", "Satoshi Nakamoto"), +//! User::new("u_1337", "Leia Skywalker"), +//! ]); +//! +//! assert_eq!( +//! users.get(&"u_42").map(|u| u.name()), +//! Some("Satoshi Nakamoto") +//! ); +//! +//! assert_eq!( +//! users.get_at_index(1).map(|u| u.name()), +//! Some("Leia Skywalker") +//! ); +//! +//! users.append(User::new("u_237", "Alan Turing")); +//! assert_eq!( +//! users.elements(), +//! [ +//! User::new("u_42", "Satoshi Nakamoto"), +//! User::new("u_1337", "Leia Skywalker"), +//! User::new("u_237", "Alan Turing"), +//! ] +//! .iter() +//! .collect::>() +//! ); +//! +//! // Element with same ID is not appended: +//! users.append(User::new("u_42", "Tom Mervolo Dolder")); +//! assert_eq!( +//! users.elements(), +//! [ +//! User::new("u_42", "Satoshi Nakamoto"), +//! User::new("u_1337", "Leia Skywalker"), +//! User::new("u_237", "Alan Turing"), +//! ] +//! .iter() +//! .collect::>() +//! ); +//! +//! // Element with same ID replaces existing if an `update_*` method is used: +//! // e.g. `update_or_insert`: +//! users.update_or_insert(User::new("u_42", "Tom Mervolo Dolder"), 0); +//! assert_eq!( +//! users.elements(), +//! [ +//! User::new("u_42", "Tom Mervolo Dolder"), +//! User::new("u_1337", "Leia Skywalker"), +//! User::new("u_237", "Alan Turing"), +//! ] +//! .iter() +//! .collect::>() +//! ); +//! +//! // or `update_or_append` +//! users.update_or_append(User::new("u_237", "Marie Curie")); +//! assert_eq!( +//! users.elements(), +//! [ +//! User::new("u_42", "Tom Mervolo Dolder"), +//! User::new("u_1337", "Leia Skywalker"), +//! User::new("u_237", "Marie Curie"), +//! ] +//! .iter() +//! .collect::>() +//! ); +//! +//! // or mutate with `get_mut(id)` +//! *users.get_mut(&"u_1337").unwrap().name.get_mut() = "Yoda"; +//! assert_eq!( +//! users.elements(), +//! [ +//! User::new("u_42", "Tom Mervolo Dolder"), +//! User::new("u_1337", "Yoda"), +//! User::new("u_237", "Marie Curie"), +//! ] +//! .iter() +//! .collect::>() +//! ); +//! ``` +//! +//! Or you can provide a closure that describes an element's identity: +//! +//! ``` +//! extern crate identified_vec; +//! use identified_vec::{IsIdentifiableVec, IdentifiedVec, IdentifiedVecOf, Identifiable}; +//! +//! // closure which plucks out an ID from an element. +//! let numbers = IdentifiedVec::::new_identifying_element(|e| *e); +//! ``` + +mod identifiable_trait; +mod primitives_identifiable; +mod serde_error; +mod vec; +mod vec_of; + +pub mod identified_vec { + //! A collection of unique identifiable elements which retains **insertion** order. + pub use crate::vec::*; +} + +pub mod identified_vec_of { + //! The `Identifiable` trait allows you to use the + //! `IdentifiedVecOf instead of the more verbose + //! `IdentifiedVec` but also allows you to + //! skip the `id_of_element: fn(&Element) -> ID` closure when + //! initializing a new identified vec. + pub use crate::identifiable_trait::*; + pub use crate::vec_of::*; + + #[cfg(feature = "id_prim")] + pub use crate::primitives_identifiable::*; + + #[cfg(feature = "serde")] + pub use crate::serde_error::*; +} + +pub use crate::identified_vec::*; +pub use crate::identified_vec_of::*; +pub use crate::vec::IsIdentifiableVec; diff --git a/identified_vec/src/primitives_identifiable.rs b/identified_vec/src/primitives_identifiable.rs new file mode 100644 index 0000000..1081c11 --- /dev/null +++ b/identified_vec/src/primitives_identifiable.rs @@ -0,0 +1,26 @@ +#![cfg(feature = "id_prim")] + +use crate::identified_vec_of::Identifiable; + +macro_rules! impl_id { + ($primitive_type:ident) => { + impl Identifiable for $primitive_type { + type ID = $primitive_type; + fn id(&self) -> Self::ID { + *self + } + } + }; +} + +impl_id!(i8); +impl_id!(i16); +impl_id!(i32); +impl_id!(i64); +impl_id!(i128); +impl_id!(u8); +impl_id!(u16); +impl_id!(u32); +impl_id!(u64); +impl_id!(u128); +impl_id!(bool); diff --git a/identified_vec/src/serde_error.rs b/identified_vec/src/serde_error.rs new file mode 100644 index 0000000..5a25995 --- /dev/null +++ b/identified_vec/src/serde_error.rs @@ -0,0 +1,6 @@ +#[cfg(feature = "serde")] +#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)] +pub enum IdentifiedVecOfSerdeFailure { + #[error("Duplicate element at offset {0}")] + DuplicateElementsAtIndex(usize), +} diff --git a/identified_vec/src/vec.rs b/identified_vec/src/vec.rs new file mode 100644 index 0000000..38c0808 --- /dev/null +++ b/identified_vec/src/vec.rs @@ -0,0 +1,1133 @@ +use std::collections::HashMap; +use std::fmt::{Debug, Display}; +use std::hash::{Hash, Hasher}; + +pub trait IsIdentifiableVec: Sized { + type Element; + type ID: Eq + Hash + Clone + Debug; + + /// Constructs a new, empty `IdentifiedVec` with the specified + /// `id_of_element` closure + fn new_identifying_element(id_of_element: fn(&Self::Element) -> Self::ID) -> Self; + + /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to + /// determine the element for any elements with duplicate identity. + /// + /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements + /// that may not have unique ids. This initializer calls the `combine` closure with the current + /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element + /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them + /// to produce a new element, or even throw an error. + /// + /// - Parameters: + /// - elements: A sequence of elements to use for the new `identified_vec`. + /// - id_of_element: The function which extracts the identifier for an element, + /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. + /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` + /// implements high-quality hashing. + fn try_from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Self::Element) -> Self::ID, + combine: fn( + (usize, &Self::Element, &Self::Element), + ) -> Result, + ) -> Result + where + It: IntoIterator; + + fn from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Self::Element) -> Self::ID, + combine: fn((usize, &Self::Element, &Self::Element)) -> ConflictResolutionChoice, + ) -> Self + where + It: IntoIterator; + + /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. + fn ids(&self) -> &Vec; + + /// Returns the number of elements in the `identified_vec`, also referred to as its 'length'. + fn len(&self) -> usize; + + /// Returns the index for the given id. + /// + /// If an element identified by the given id is found in the `identified_vec`, this method returns an index + /// into the array that corresponds to the element. + /// + /// ``` + /// extern crate identified_vec; + /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// + /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] + /// struct User { + /// id: &'static str, + /// } + /// + /// impl User { + /// fn new(id: &'static str) -> Self { + /// Self { id } + /// } + /// } + /// + /// impl Identifiable for User { + /// type ID = &'static str; + /// fn id(&self) -> Self::ID { + /// self.id + /// } + /// } + /// + /// let mut users = + /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); + /// + /// assert_eq!(users.index_of_id(&"u_1729"), Some(1)); + /// assert_eq!(users.index_of_id(&"u_1337"), None); + /// ``` + /// + /// - Parameter id: The id to find in the `identified_vec`. + /// - Returns: The index for the element identified by `id` if found in the `identified_vec`; otherwise, + /// `None`. + /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. + fn index_of_id(&self, id: &Self::ID) -> Option; + + /// Returns a mutable reference to the element identified by `id` if any, else None. + /// + /// - Parameter id: The id to find in the `identified_vec`. + /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, + /// `None`. + /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. + fn get_mut(&mut self, id: &Self::ID) -> Option<&mut Self::Element>; + + fn elements(&self) -> Vec<&Self::Element>; + + /// Returns `true` if the `identified_vec` contains the `element.` + fn contains(&self, element: &Self::Element) -> bool; + + /// Returns `true if the `identified_vec` contains an element for the specified `id` + fn contains_id(&self, id: &Self::ID) -> bool; + + /// Returns a reference to the element corresponding to the `id`, if found, else `None`. + fn get(&self, id: &Self::ID) -> Option<&Self::Element>; + + /// Returns a reference to the element at index if found, else `None`. + fn get_at_index(&self, index: usize) -> Option<&Self::Element>; + + /// Append a new member to the end of the `identified_vec`, if the `identified_vec` doesn't already contain it. + /// + /// - Parameter item: The element to add to the `identified_vec`. + /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether + /// the operation added a new element, and `index` is the index of `item` in the resulting + /// `identified_vec`. + /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on + /// the `Self::ID` type, if it implements high-quality hashing. + fn append(&mut self, element: Self::Element) -> (bool, usize); + + /// Append the contents of an iterator to the end of the set, excluding elements that are already + /// members. + /// + /// - Parameter elements: A finite sequence of elements to append. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::Element` type, if it implements high-quality hashing. + fn append_other(&mut self, other: It) + where + It: IntoIterator; + + /// Adds the given element to the `identified_vec` unconditionally, either appending it to the `identified_vec``, or + /// replacing an existing value if it's already present. + /// + /// - Parameter item: The value to append or replace. + /// - Returns: The original element that was replaced by this operation, or `None` if the value was + /// appended to the end of the collection. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::ID` type, if it implements high-quality hashing. + fn update_or_append(&mut self, element: Self::Element) -> Option; + + /// Replace the member at the given index with a new value of the same identity. + /// + /// - Parameter item: The new value that should replace the original element. `item` must match + /// the identity of the original value. + /// - Parameter index: The index of the element to be replaced. + /// - Returns: The original element that was replaced. + fn update_at(&mut self, element: Self::Element, index: usize) -> Self::Element; + + /// Insert a new member to this identified_vec at the specified index, if the identified_vec doesn't already contain + /// it. + /// + /// - Parameter element: The element to insert. + /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether + /// the operation added a new element, and `index` is the index of `element` in the resulting + /// identified_vec. If `inserted` is true, then the returned `index` may be different from the index + /// requested. + /// + /// - Complexity: The operation is expected to perform amortized O(`self.count`) copy, hash, and + /// compare operations on the `Self::ID` type, if it implements high-quality hashing. (Insertions need + /// to make room in the storage identified_vec to add the inserted element.) + fn insert(&mut self, element: Self::Element, at: usize) -> (bool, usize); + + /// Adds the given element into the set unconditionally, either inserting it at the specified + /// index, or replacing an existing value if it's already present. + /// + /// - Parameter item: The value to append or replace. + /// - Parameter index: The index at which to insert the new member if `item` isn't already in the + /// set. + /// - Returns: The original element that was replaced by this operation, or `None` if the value was + /// newly inserted into the collection. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::ID` type, if it implements high-quality hashing. + fn update_or_insert( + &mut self, + element: Self::Element, + index: usize, + ) -> (Option, usize); + + ///////////// + // Remove // + ///////////// + + /// Removes the element identified by the given id from the `identified_vec`. + /// + /// ``` + /// extern crate identified_vec; + /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// + /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] + /// struct User { + /// id: &'static str, + /// } + /// + /// impl User { + /// fn new(id: &'static str) -> Self { + /// Self { id } + /// } + /// } + /// + /// impl Identifiable for User { + /// type ID = &'static str; + /// fn id(&self) -> Self::ID { + /// self.id + /// } + /// } + /// + /// let mut users = + /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); + /// + /// assert_eq!(users.remove_by_id(&"u_1729"), Some(User::new("u_1729"))); + /// assert_eq!(users.elements(), [&User::new("u_42")]); + /// assert_eq!(users.remove_by_id(&"u_1337"), None); + /// assert_eq!(users.len(), 1); + /// ``` + /// + /// - Parameter id: The id of the element to be removed from the `identified_vec`. + /// - Returns: The element that was removed, or `None` if the element was not present in the array. + /// - Complexity: O(`count`) + fn remove_by_id(&mut self, id: &Self::ID) -> Option; + + /// Removes the given element from the `identified_vec`. + /// + /// If the element is found in the `identified_vec`, this method returns the element. + /// + /// If the element isn't found in the `identified_vec`, `remove` returns `None`. + /// + /// - Parameter element: The element to remove. + /// - Returns: The value that was removed, or `None` if the element was not present in the `identified_vec`. + /// - Complexity: O(`count`) + fn remove(&mut self, element: &Self::Element) -> Option; + + /// Removes and returns the element at the specified position. + /// + /// All the elements following the specified position are moved to close the resulting gap. + /// + /// - Parameter index: The position of the element to remove. + /// - Returns: The removed element. + /// - Precondition: `index` must be a valid index of the collection that is not equal to the + /// collection's end index. + /// - Complexity: O(`count`) + fn remove_at(&mut self, index: usize) -> Self::Element; + + /// Removes all the elements at the specified `offsets` from the `identified_vec`. + /// + /// - Parameter offsets: The offsets of all elements to be removed. + /// - Complexity: O(*n*) where *n* is the length of the `identified_vec`. + fn remove_at_offsets(&mut self, offsets: It) + where + It: IntoIterator; +} + +/// Representation of a choice in a conflict resolution +/// where two elements with the same Self::ID exists, if `ChooseFirst`, +/// is specified the first/current/existing value will be used, but +/// if `ChooseLast` is specified then the new/last will be replace +/// the first/current/existing. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ConflictResolutionChoice { + ChooseFirst, + ChooseLast, +} + +/// An ordered collection of identifiable elements. +/// +/// Similar to the standard `Vec`, identified vecs maintain their elements in a particular +/// user-specified order, and they support efficient random access traversal of their members. +/// However, unlike `Vec`, identified vecs introduce the ability to uniquely identify elements, +/// using a hash table to ensure that no two elements have the same identity, and to efficiently +/// look up elements corresponding to specific identifiers. +/// +/// `IdentifiedVec` is a useful alternative to `Vec` when you need to be able to efficiently +/// access unique elements by a stable identifier. It is also a useful alternative to `BTreeSet`, +/// where the `Ord` requirement may be too strict, an a useful alternative to `HashSet` where +/// `Hash` requirement may be too strict. +/// +/// You can create an identified vec with any element type that implements the `Identifiable` +/// trait. +/// +/// ``` +/// extern crate identified_vec; +/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf, IsIdentifiableVec}; +/// use std::cell::RefCell; +/// +/// #[derive(Eq, PartialEq, Clone, Debug)] +/// struct User { +/// id: &'static str, +/// name: RefCell<&'static str>, +/// } +/// +/// impl User { +/// fn new(id: &'static str, name: &'static str) -> Self { +/// Self { +/// id, +/// name: RefCell::new(name), +/// } +/// } +/// fn name(&self) -> &'static str { +/// *self.name.borrow() +/// } +/// } +/// +/// impl Identifiable for User { +/// type ID = &'static str; +/// fn id(&self) -> Self::ID { +/// self.id +/// } +/// } +/// +/// let mut users = IdentifiedVecOf::::from_iter([ +/// User::new("u_42", "Satoshi Nakamoto"), +/// User::new("u_1337", "Leia Skywalker"), +/// ]); +/// +/// assert_eq!( +/// users.get(&"u_42").map(|u| u.name()), +/// Some("Satoshi Nakamoto") +/// ); +/// assert_eq!( +/// users.get_at_index(1).map(|u| u.name()), +/// Some("Leia Skywalker") +/// ); +/// users.append(User::new("u_237", "Alan Turing")); +/// assert_eq!( +/// users.elements(), +/// [ +/// User::new("u_42", "Satoshi Nakamoto"), +/// User::new("u_1337", "Leia Skywalker"), +/// User::new("u_237", "Alan Turing"), +/// ] +/// .iter() +/// .collect::>() +/// ); +/// +/// // Self::Element with same Self::ID is not appended: +/// users.append(User::new("u_42", "Tom Mervolo Dolder")); +/// assert_eq!( +/// users.elements(), +/// [ +/// User::new("u_42", "Satoshi Nakamoto"), +/// User::new("u_1337", "Leia Skywalker"), +/// User::new("u_237", "Alan Turing"), +/// ] +/// .iter() +/// .collect::>() +/// ); +/// +/// // Self::Element with same Self::ID replaces existing if an `update_*` method is used: +/// // e.g. `update_or_insert`: +/// users.update_or_insert(User::new("u_42", "Tom Mervolo Dolder"), 0); +/// assert_eq!( +/// users.elements(), +/// [ +/// User::new("u_42", "Tom Mervolo Dolder"), +/// User::new("u_1337", "Leia Skywalker"), +/// User::new("u_237", "Alan Turing"), +/// ] +/// .iter() +/// .collect::>() +/// ); +/// +/// // or `update_or_append` +/// users.update_or_append(User::new("u_237", "Marie Curie")); +/// assert_eq!( +/// users.elements(), +/// [ +/// User::new("u_42", "Tom Mervolo Dolder"), +/// User::new("u_1337", "Leia Skywalker"), +/// User::new("u_237", "Marie Curie"), +/// ] +/// .iter() +/// .collect::>() +/// ); +/// +/// // or mutate with `get_mut(id)` +/// *users.get_mut(&"u_1337").unwrap().name.get_mut() = "Yoda"; +/// assert_eq!( +/// users.elements(), +/// [ +/// User::new("u_42", "Tom Mervolo Dolder"), +/// User::new("u_1337", "Yoda"), +/// User::new("u_237", "Marie Curie"), +/// ] +/// .iter() +/// .collect::>() +/// ); +/// ``` +/// +/// Or you can provide a closure that describes an element's identity: +/// +/// ``` +/// /// extern crate identified_vec; +/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf, IsIdentifiableVec}; +/// +/// let numbers = IdentifiedVec::::new_identifying_element(|e| *e); +/// ``` +/// +/// # Motivation +/// None of the std collections `BTreeSet` and `HashSet` retain insertion order, `Vec` retains +/// insertion order, however, it allows for duplicates. So if you want a collection of unique +/// elements (Set-like) that does retain insertion order, `IdentifiedVec` suits your needs. +/// Even better, the elements does not need to be to impl `Hash` nor `Ord``. +/// +/// # Performance +/// +/// Like the standard `HashMap` type, the performance of hashing operations in +/// `IdentifiedVec` is highly sensitive to the quality of hashing implemented by the `Self::ID` +/// type. Failing to correctly implement hashing can easily lead to unacceptable performance, with +/// the severity of the effect increasing with the size of the underlying hash table. +/// +/// In particular, if a certain set of elements all produce the same hash value, then hash table +/// lookups regress to searching an element in an unsorted array, i.e., a linear operation. To +/// ensure hashed collection types exhibit their target performance, it is important to ensure that +/// such collisions cannot be induced merely by adding a particular list of members to the set. +/// +/// When `Self::ID` implements `Hash` correctly, testing for membership in an ordered set is expected +/// to take O(1) equality checks on average. Hash collisions can still occur organically, so the +/// worst-case lookup performance is technically still O(*n*) (where *n* is the size of the set); +/// however, long lookup chains are unlikely to occur in practice. +/// +/// ## Implementation Details +/// +/// An identified vec consists of a Vec and a HashMap of id-element pairs. An element's id +/// should not be mutated in place, as it will drift from its associated dictionary key. Identified +/// bec is designed to avoid this invariant. Mutating an element's id will result in a runtime error. +#[derive(Debug, Clone)] +pub struct IdentifiedVec +where + I: Eq + Hash + Clone + Debug, +{ + /// The holder of the insertion order + pub(crate) order: Vec, + + /// The storage of elements. + pub(crate) elements: HashMap, + + /// Function which extracts the Self::ID of an Self::Element. + pub(crate) _id_of_element: fn(&E) -> I, +} + +impl IsIdentifiableVec for IdentifiedVec +where + I: Eq + Hash + Clone + Debug, +{ + //////////////////// + // Constructors // + //////////////////// + + type ID = I; + type Element = E; + + /// Constructs a new, empty `IdentifiedVec` with the specified + /// `id_of_element` closure + #[inline] + fn new_identifying_element(id_of_element: fn(&E) -> I) -> Self { + Self { + order: Vec::new(), + elements: HashMap::new(), + _id_of_element: id_of_element, + } + } + + /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to + /// determine the element for any elements with duplicate identity. + /// + /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements + /// that may not have unique ids. This initializer calls the `combine` closure with the current + /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element + /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them + /// to produce a new element, or even throw an error. + /// + /// - Parameters: + /// - elements: A sequence of elements to use for the new `identified_vec`. + /// - id_of_element: The function which extracts the identifier for an element, + /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. + /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` + /// implements high-quality hashing. + #[cfg(not(tarpaulin_include))] // false negative + #[inline] + fn try_from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Self::Element) -> Self::ID, + combine: fn( + (usize, &Self::Element, &Self::Element), + ) -> Result, + ) -> Result + where + It: IntoIterator, + { + let mut _order = Vec::::new(); + let mut _elements = HashMap::::new(); + + for element in elements.into_iter() { + let id = id_of_element(&element); + match _elements.remove(&id) { + Some(existing) => match combine((_order.len(), &existing, &element)) { + Err(e) => return Err(e), + Ok(choice) => match choice { + ConflictResolutionChoice::ChooseFirst => { + _elements.insert(id.clone(), existing) + } + ConflictResolutionChoice::ChooseLast => { + _elements.insert(id.clone(), element) + } + }, + }, + None => { + _elements.insert(id.clone(), element); + _order.push(id); + None + } + }; + } + + Ok(Self { + order: _order, + _id_of_element: id_of_element, + elements: _elements, + }) + } + + /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to + /// determine the element for any elements with duplicate identity. + /// + /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements + /// that may not have unique ids. This initializer calls the `combine` closure with the current + /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element + /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them + /// to produce a new element, or even throw an error. + /// + /// - Parameters: + /// - elements: A sequence of elements to use for the new `identified_vec`. + /// - id_of_element: The function which extracts the identifier for an element, + /// - combine: Closure used combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`) + /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` + /// implements high-quality hashing. + #[cfg(not(tarpaulin_include))] // false negative + #[inline] + fn from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Self::Element) -> Self::ID, + combine: fn((usize, &Self::Element, &Self::Element)) -> ConflictResolutionChoice, + ) -> Self + where + It: IntoIterator, + { + let mut _order = Vec::::new(); + let mut _elements = HashMap::::new(); + + for element in elements.into_iter() { + let id = id_of_element(&element); + match _elements.remove(&id) { + Some(existing) => match combine((_order.len(), &existing, &element)) { + ConflictResolutionChoice::ChooseFirst => _elements.insert(id.clone(), existing), + ConflictResolutionChoice::ChooseLast => _elements.insert(id.clone(), element), + }, + None => { + _elements.insert(id.clone(), element); + _order.push(id); + None + } + }; + } + + Self { + order: _order, + _id_of_element: id_of_element, + elements: _elements, + } + } + + //////////////////// + // Public Get // + //////////////////// + + /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. + /// + /// - Complexity: O(1) + #[inline] + fn ids(&self) -> &Vec { + &self.order + } + + /// Returns the number of elements in the `identified_vec`, also referred to as its 'length'. + #[inline] + fn len(&self) -> usize { + if cfg!(debug_assertions) { + assert_eq!(self.order.len(), self.elements.len()); + } + self.order.len() + } + + /// Returns the index for the given id. + /// + /// If an element identified by the given id is found in the `identified_vec`, this method returns an index + /// into the array that corresponds to the element. + /// + /// ``` + /// extern crate identified_vec; + /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// + /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] + /// struct User { + /// id: &'static str, + /// } + /// + /// impl User { + /// fn new(id: &'static str) -> Self { + /// Self { id } + /// } + /// } + /// + /// impl Identifiable for User { + /// type ID = &'static str; + /// fn id(&self) -> Self::ID { + /// self.id + /// } + /// } + /// + /// let mut users = + /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); + /// + /// assert_eq!(users.index_of_id(&"u_1729"), Some(1)); + /// assert_eq!(users.index_of_id(&"u_1337"), None); + /// ``` + /// + /// - Parameter id: The id to find in the `identified_vec`. + /// - Returns: The index for the element identified by `id` if found in the `identified_vec`; otherwise, + /// `None`. + /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. + #[inline] + fn index_of_id(&self, id: &Self::ID) -> Option { + self.order.iter().position(|i| i == id) + } + + /// Returns a mutable reference to the element identified by `id` if any, else None. + /// + /// - Parameter id: The id to find in the `identified_vec`. + /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, + /// `None`. + /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. + #[inline] + fn get_mut(&mut self, id: &Self::ID) -> Option<&mut Self::Element> { + self.elements.get_mut(id) + } + + //////////////////// + // Public Get // + //////////////////// + + /// A read-only collection of references to the elements contained in this array, as a `Vec<&Elements>`. + /// + /// N.B. that this method is not constant time. + /// + /// If `Self::Element` implements `Clone` you can use `self.items()` which returns a `Vec`, of cloned elements. + /// + /// - Complexity: O(n) + #[inline] + fn elements(&self) -> Vec<&Self::Element> { + self.iter().collect() + } + + /// Returns `true` if the `identified_vec` contains the `element.` + #[inline] + fn contains(&self, element: &Self::Element) -> bool { + self.elements.contains_key(&self.id(&element)) + } + + /// Returns `true if the `identified_vec` contains an element for the specified `id` + #[inline] + fn contains_id(&self, id: &Self::ID) -> bool { + self.elements.contains_key(id) + } + + /// Returns a reference to the element corresponding to the `id`, if found, else `None`. + #[inline] + fn get(&self, id: &Self::ID) -> Option<&Self::Element> { + self.elements.get(id) + } + + /// Returns a reference to the element at index if found, else `None`. + #[inline] + fn get_at_index(&self, index: usize) -> Option<&Self::Element> { + self.order.get(index).and_then(|id| self.get(id)) + } + + /// Append a new member to the end of the `identified_vec`, if the `identified_vec` doesn't already contain it. + /// + /// - Parameter item: The element to add to the `identified_vec`. + /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether + /// the operation added a new element, and `index` is the index of `item` in the resulting + /// `identified_vec`. + /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on + /// the `Self::ID` type, if it implements high-quality hashing. + #[inline] + fn append(&mut self, element: E) -> (bool, usize) { + self.insert(element, self.end_index()) + } + + /// Append the contents of an iterator to the end of the set, excluding elements that are already + /// members. + /// + /// - Parameter elements: A finite sequence of elements to append. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::Element` type, if it implements high-quality hashing. + #[inline] + fn append_other(&mut self, other: It) + where + It: IntoIterator, + { + other.into_iter().for_each(|i| _ = self.append(i)) + } + + /// Adds the given element to the `identified_vec` unconditionally, either appending it to the `identified_vec``, or + /// replacing an existing value if it's already present. + /// + /// - Parameter item: The value to append or replace. + /// - Returns: The original element that was replaced by this operation, or `None` if the value was + /// appended to the end of the collection. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::ID` type, if it implements high-quality hashing. + #[inline] + fn update_or_append(&mut self, element: E) -> Option { + let id = self.id(&element); + self._update_value(element, id) + } + + /// Replace the member at the given index with a new value of the same identity. + /// + /// - Parameter item: The new value that should replace the original element. `item` must match + /// the identity of the original value. + /// - Parameter index: The index of the element to be replaced. + /// - Returns: The original element that was replaced. + /// - Complexity: Amortized O(1). + #[inline] + fn update_at(&mut self, element: E, index: usize) -> E { + let old_id = self + .order + .get(index) + .expect("Expected element at index {index}"); + let id = self.id(&element); + assert_eq!( + &id, old_id, + "The replacement item must match the identity of the original" + ); + return self + ._update_value_inserting_at(element, id, index) + .0 + .expect("Replaced old value"); + } + + /// Insert a new member to this identified_vec at the specified index, if the identified_vec doesn't already contain + /// it. + /// + /// - Parameter element: The element to insert. + /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether + /// the operation added a new element, and `index` is the index of `element` in the resulting + /// identified_vec. If `inserted` is true, then the returned `index` may be different from the index + /// requested. + /// + /// - Complexity: The operation is expected to perform amortized O(`self.count`) copy, hash, and + /// compare operations on the `Self::ID` type, if it implements high-quality hashing. (Insertions need + /// to make room in the storage identified_vec to add the inserted element.) + #[cfg(not(tarpaulin_include))] // false negative + #[inline] + fn insert(&mut self, element: E, at: usize) -> (bool, usize) { + let id = self.id(&element); + if let Some(existing) = self.index_of_id(&id) { + return (false, existing.clone()); + } + self._update_value_inserting_at(element, id, at); + (true, at) + } + + /// Adds the given element into the set unconditionally, either inserting it at the specified + /// index, or replacing an existing value if it's already present. + /// + /// - Parameter item: The value to append or replace. + /// - Parameter index: The index at which to insert the new member if `item` isn't already in the + /// set. + /// - Returns: The original element that was replaced by this operation, or `None` if the value was + /// newly inserted into the collection. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::ID` type, if it implements high-quality hashing. + #[inline] + fn update_or_insert(&mut self, element: E, index: usize) -> (Option, usize) { + let id = self.id(&element); + self._update_value_inserting_at(element, id, index) + } + + //////////////////// + // Public Remove // + //////////////////// + /// Removes the element identified by the given id from the `identified_vec`. + /// + /// ``` + /// extern crate identified_vec; + /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// + /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] + /// struct User { + /// id: &'static str, + /// } + /// + /// impl User { + /// fn new(id: &'static str) -> Self { + /// Self { id } + /// } + /// } + /// + /// impl Identifiable for User { + /// type ID = &'static str; + /// fn id(&self) -> Self::ID { + /// self.id + /// } + /// } + /// + /// let mut users = + /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); + /// + /// assert_eq!(users.remove_by_id(&"u_1729"), Some(User::new("u_1729"))); + /// assert_eq!(users.elements(), [&User::new("u_42")]); + /// assert_eq!(users.remove_by_id(&"u_1337"), None); + /// assert_eq!(users.len(), 1); + /// ``` + /// + /// - Parameter id: The id of the element to be removed from the `identified_vec`. + /// - Returns: The element that was removed, or `None` if the element was not present in the array. + /// - Complexity: O(`count`) + #[cfg(not(tarpaulin_include))] // false negative + #[inline] + fn remove_by_id(&mut self, id: &I) -> Option { + match self.index_of_id(id) { + Some(index) => { + self.order.remove(index); + return self.elements.remove(id); + } + None => { + assert!(!self.elements.contains_key(id)); + return None; + } + } + } + + /// Removes the given element from the `identified_vec`. + /// + /// If the element is found in the `identified_vec`, this method returns the element. + /// + /// If the element isn't found in the `identified_vec`, `remove` returns `None`. + /// + /// - Parameter element: The element to remove. + /// - Returns: The value that was removed, or `None` if the element was not present in the `identified_vec`. + /// - Complexity: O(`count`) + #[inline] + fn remove(&mut self, element: &E) -> Option { + self.remove_by_id(&self.id(element)) + } + + /// Removes and returns the element at the specified position. + /// + /// All the elements following the specified position are moved to close the resulting gap. + /// + /// - Parameter index: The position of the element to remove. + /// - Returns: The removed element. + /// - Precondition: `index` must be a valid index of the collection that is not equal to the + /// collection's end index. + /// - Complexity: O(`count`) + #[inline] + fn remove_at(&mut self, index: usize) -> E { + let id = self + .order + .get(index) + .expect("Precondition failure, index out of bounds"); + let removed = self + .elements + .remove(id) + .expect("Self::Element of existing id"); + self.order.remove(index); + return removed; + } + + /// Removes all the elements at the specified `offsets` from the `identified_vec`. + /// + /// - Parameter offsets: The offsets of all elements to be removed. + /// - Complexity: O(*n*) where *n* is the length of the `identified_vec`. + #[inline] + fn remove_at_offsets(&mut self, offsets: It) + where + It: IntoIterator, + { + let mut internal_offset = 0; + offsets.into_iter().for_each(|i| { + _ = self.remove_at(i - internal_offset); + internal_offset += 1; + }) + } +} + +impl IdentifiedVec +where + E: Clone, + I: Eq + Hash + Clone + Debug, +{ + /// A read-only collection of clones of the elements contained in this array, as a `Vec`. + /// + /// N.B. that this method is not constant time. + /// + /// Use `self.elements()` if you are looking for a collection of references. + /// + /// - Complexity: O(n) + #[inline] + pub fn items(&self) -> Vec { + self.iter().map(|e| e.clone()).collect() + } +} + +/// An iterator over the items of an `IdentifiedVec`. +pub struct IdentifiedVecIterator<'a, I, E> +where + I: Eq + Hash + Clone + Debug, +{ + identified_vec: &'a IdentifiedVec, + index: usize, +} + +impl<'a, I, E> Iterator for IdentifiedVecIterator<'a, I, E> +where + I: Eq + Hash + Clone + Debug, +{ + type Item = &'a E; + + fn next(&mut self) -> Option { + if self.index < self.identified_vec.len() { + let id = Some(&self.identified_vec.order[self.index]).unwrap(); + self.index += 1; + return self.identified_vec.get(id); + } else { + None + } + } +} + +impl IdentifiedVec +where + I: Eq + Hash + Clone + Debug, +{ + pub fn iter(&self) -> IdentifiedVecIterator { + IdentifiedVecIterator { + identified_vec: self, + index: 0, + } + } +} + +/// An owning iterator over the items of an `IdentifiedVec`. +pub struct IdentifiedVecIntoIterator +where + I: Eq + Hash + Clone + Debug, +{ + identified_vec: IdentifiedVec, +} + +impl Iterator for IdentifiedVecIntoIterator +where + I: Eq + Hash + Clone + Debug, +{ + type Item = E; + + fn next(&mut self) -> Option { + if self.identified_vec.len() == 0 { + return None; + } + let result = self.identified_vec.remove_at(0); + Some(result) + } +} + +impl IntoIterator for IdentifiedVec +where + I: Eq + Hash + Clone + Debug, +{ + type Item = E; + type IntoIter = IdentifiedVecIntoIterator; + + fn into_iter(self) -> Self::IntoIter { + Self::IntoIter { + identified_vec: self, + } + } +} + +impl IdentifiedVec where I: Eq + Hash + Clone + Debug {} + +/////////////////////// +//// Eq /// +/////////////////////// +impl PartialEq for IdentifiedVec +where + E: PartialEq, + I: Eq + Hash + Clone + Debug, +{ + fn eq(&self, other: &Self) -> bool { + self.elements() == other.elements() + } +} + +impl Eq for IdentifiedVec +where + E: Eq, + I: Eq + Hash + Clone + Debug, +{ +} + +/////////////////////// +//// Hash /// +/////////////////////// +impl Hash for IdentifiedVec +where + E: Hash, + I: Eq + Hash + Clone + Debug, +{ + fn hash(&self, state: &mut H) { + self.elements().hash(state); + } +} + +/////////////////////// +//// Display /// +/////////////////////// +impl Display for IdentifiedVec +where + E: Debug, + I: Eq + Hash + Clone + Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.elements().fmt(f) + } +} + +/////////////////////// +//// PRIVATE /// +/////////////////////// +impl IdentifiedVec +where + I: Eq + Hash + Clone + Debug, +{ + /// Next index for element appended + #[inline] + fn end_index(&self) -> usize { + self.len() + } + + /// Returns the Self::ID of an Self::Element + #[inline] + fn id(&self, of: &E) -> I { + (self._id_of_element)(of) + } + + /// Inserting Self::ID at an index, returning if it did, if not, the index of the existing. + #[cfg(not(tarpaulin_include))] // false negative + #[inline] + fn _insert_id_at(&mut self, id: I, index: usize) -> (bool, usize) { + match self.index_of_id(&id) { + Some(existing) => (false, existing), + None => { + self.order.insert(index, id); + (true, index) + } + } + } + + #[inline] + fn _update_value(&mut self, element: E, for_key: I) -> Option { + let value = element; + let key = for_key; + + let maybe_old = self.elements.remove(&key); + self.elements.insert(key.clone(), value); + + if maybe_old.is_some() { + return maybe_old; + } else { + self.order.push(key); + None + } + } + + #[inline] + fn _update_value_inserting_at( + &mut self, + element: E, + for_key: I, + index: usize, + ) -> (Option, usize) { + let id = for_key; + let value = element; + + let (inserted, offset) = self._insert_id_at(id.clone(), index); + if inserted { + assert_eq!(offset, index); + self.elements.insert(id.clone(), value); + return (None, offset); + } + let old = self.elements.remove(&id).expect("existing element"); + self.elements.insert(id, value); + return (Some(old), offset); + } +} + +/////////////////////// +//// DEBUG /// +/////////////////////// +impl IdentifiedVec +where + E: Debug, + I: Eq + Hash + Clone + Debug, +{ + #[cfg(not(tarpaulin_include))] + #[cfg(debug_assertions)] + pub fn debug(&self) { + println!("{}", self.debug_str()); + } + + #[cfg(debug_assertions)] + pub fn debug_str(&self) -> String { + format!("order: {:?}\nelements: {:?}", self.order, self.elements) + } +} diff --git a/identified_vec/src/vec_of.rs b/identified_vec/src/vec_of.rs new file mode 100644 index 0000000..f915195 --- /dev/null +++ b/identified_vec/src/vec_of.rs @@ -0,0 +1,154 @@ +use std::collections::HashMap; + +#[cfg(feature = "serde")] +use std::fmt::Debug; + +#[cfg(feature = "serde")] +use crate::serde_error::IdentifiedVecOfSerdeFailure; +use crate::{ + identified_vec_of::Identifiable, + vec::{ConflictResolutionChoice, IdentifiedVec, IsIdentifiableVec}, +}; + +#[cfg(feature = "serde")] +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +/////////////////////// +/// IdentifiedVecOf /// +/////////////////////// + +/// A type alias for `IdentifiedVec`, this is the +/// preferred and most powerful collection type of this crate, requires +/// that your `Element`s impl the `Identifiable` trait. Using this collection +/// allows you to skip passing the `id_of_element: fn(&Element) -> ID` closure +/// which you otherwise need to pass when initializing an `IdentifiedVec`. Using +/// `IdentifiedVecOf` together with feature "serde" also gives serde +/// serialization/deserialization as if it were a `Vec`, given that +/// `Element` implements serde serialization/deserialization of course. +pub type IdentifiedVecOf = IdentifiedVec<::ID, Element>; + +////////////////////////////////////////////// +/// Identifiable Element Constructors /// +////////////////////////////////////////////// +impl IdentifiedVec +where + Element: Identifiable, +{ + /// Constructs a new, empty `IdentifiedVec`, using `id()` on `Element` + /// as id function. + pub fn new() -> Self { + Self { + order: Vec::new(), + elements: HashMap::new(), + _id_of_element: |i| i.id(), + } + } + + /// Creates a new `IdentifiedVec` from the elements in the given sequence. + /// + /// You use this initializer to create an `IdentifiedVec` when you have a sequence of elements with unique + /// ids. Passing a sequence with duplicate ids to this initializer results in a runtime error. + /// + /// - Parameter elements: A sequence of elements to use for the new `IdentifiedVec`. Every element in + /// `elements` must have a unique id. + /// - Returns: A new `IdentifiedVec` initialized with the elements of `elements`. + /// - Precondition: The sequence must not have duplicate ids. + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` + /// implements high-quality hashing. + #[inline] + pub fn from_iter(unique_elements: I) -> Self + where + I: IntoIterator, + { + let mut _self = Self::new(); + unique_elements + .into_iter() + .for_each(|e| _ = _self.append(e)); + return _self; + } + + /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to + /// determine the element for any elements with duplicate ids. + /// + /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements + /// that may not have unique ids. This initializer calls the `combine` closure with the current + /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element + /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them + /// to produce a new element, or even throw an error. + /// + /// - Parameters: + /// - elements: A sequence of elements to use for the new `identified_vec`. + /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. + /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` + /// implements high-quality hashing. + #[inline] + pub fn try_from_iter_select_unique_with( + elements: I, + combine: fn((usize, &Element, &Element)) -> Result, + ) -> Result + where + I: IntoIterator, + { + Self::try_from_iter_select_unique_ids_with(elements, |e| e.id(), combine) + } + + /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to + /// determine the element for any elements with duplicate ids. + /// + /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements + /// that may not have unique ids. This initializer calls the `combine` closure with the current + /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element + /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them + /// to produce a new element, or even throw an error. + /// + /// - Parameters: + /// - elements: A sequence of elements to use for the new `identified_vec`. + /// - combine: Closure used combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`) + /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` + /// implements high-quality hashing. + #[inline] + pub fn from_iter_select_unique_with( + elements: I, + combine: fn((usize, &Element, &Element)) -> ConflictResolutionChoice, + ) -> Self + where + I: IntoIterator, + { + Self::from_iter_select_unique_ids_with(elements, |e| e.id(), combine) + } +} + +/////////////////////// +//// SERDE /// +/////////////////////// +#[cfg(feature = "serde")] +impl Serialize for IdentifiedVecOf +where + Element: Serialize + Identifiable + Debug + Clone, +{ + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> + where + S: Serializer, + { + Vec::serialize(&self.elements(), serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de, Element> Deserialize<'de> for IdentifiedVecOf +where + Element: Deserialize<'de> + Identifiable + Debug + Clone, +{ + #[cfg(not(tarpaulin_include))] // false negative + fn deserialize>( + deserializer: D, + ) -> Result, D::Error> { + let elements = Vec::::deserialize(deserializer)?; + IdentifiedVecOf::::try_from_iter_select_unique_with(elements, |(idx, _, _)| { + Err(IdentifiedVecOfSerdeFailure::DuplicateElementsAtIndex(idx)) + }) + .map_err(de::Error::custom) + } +} diff --git a/identified_vec_macros/Cargo.toml b/identified_vec_macros/Cargo.toml new file mode 100644 index 0000000..debd4a8 --- /dev/null +++ b/identified_vec_macros/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "identified_vec_macros" +version = "0.1.0" +edition = "2021" +authors = ["Alexander Cyon "] + +[dependencies] +identified_vec = { path = "../identified_vec" } +proc-macro2 = "1.0.70" +quote = "1.0" +syn = { version = "2.0", features = ["extra-traits", "full"] } + +[lib] +proc-macro = true diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs new file mode 100644 index 0000000..acfc0b8 --- /dev/null +++ b/identified_vec_macros/src/lib.rs @@ -0,0 +1,10 @@ +// use proc_macro2::TokenStream; +extern crate proc_macro; +use proc_macro::TokenStream; + +#[proc_macro_attribute] +pub fn show_streams(attr: TokenStream, item: TokenStream) -> TokenStream { + println!("attributes: \"{}\"", attr.to_string()); + println!("item= \"{}\"", item.to_string()); + item +} diff --git a/tests/tests.rs b/tests/tests.rs index 5b07a44..de32eeb 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "id_prim")] - use std::{cell::RefCell, collections::HashSet, fmt::Debug, ops::Deref}; use identified_vec::{ @@ -7,6 +5,8 @@ use identified_vec::{ IdentifiedVecOfSerdeFailure, IsIdentifiableVec, }; +use identified_vec_macros::show_streams; + #[derive(Eq, PartialEq, Clone)] pub struct User { pub id: u16, @@ -535,3 +535,14 @@ fn hash() { HashSet::from_iter([identified_vec.clone(), identified_vec]) ) } + +// Test macro + +#[test] +fn test_macro() { + // Example: Attribute with input + #[show_streams(biz)] + fn invoke2() {} + // printed: attributes: "biz" + // printed: item= "fn invoke2() {}" +} From 8e75ca8015a54052dc67900b75bff6227dc861a2 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Wed, 13 Dec 2023 22:10:56 +0100 Subject: [PATCH 04/24] WIP --- tests/Cargo.toml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/Cargo.toml diff --git a/tests/Cargo.toml b/tests/Cargo.toml new file mode 100644 index 0000000..5d3732e --- /dev/null +++ b/tests/Cargo.toml @@ -0,0 +1,18 @@ +# https://stackoverflow.com/a/71461114/1311272 +[package] +name = "tests" +version = "0.1.0" +edition = "2021" +publish = false + +[dev-dependencies] +identified_vec = { path = "../identified_vec", features = ["id_prim", "serde"] } +identified_vec_macros = { path = "../identified_vec_macros" } +serde = "1.0.193" +serde_json = "1.0.108" +rand = "0.8.5" +maplit = "1.0.2" + +[[test]] +name = "integration_tests" +path = "tests.rs" From d6fa8dfe02574fcf6582de5fa3c237bcef9405cd Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Thu, 14 Dec 2023 20:51:11 +0100 Subject: [PATCH 05/24] WIP --- identified_vec_macros/Cargo.toml | 6 +++--- identified_vec_macros/src/lib.rs | 20 ++++++++++++-------- tests/tests.rs | 11 +++++------ 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/identified_vec_macros/Cargo.toml b/identified_vec_macros/Cargo.toml index debd4a8..1589419 100644 --- a/identified_vec_macros/Cargo.toml +++ b/identified_vec_macros/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Alexander Cyon "] identified_vec = { path = "../identified_vec" } proc-macro2 = "1.0.70" quote = "1.0" -syn = { version = "2.0", features = ["extra-traits", "full"] } +syn = { version = "2.0", features = ["extra-traits", "full", "parsing"] } -[lib] -proc-macro = true +# [lib] +# proc-macro = true diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index acfc0b8..926b53a 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -1,10 +1,14 @@ -// use proc_macro2::TokenStream; -extern crate proc_macro; -use proc_macro::TokenStream; +#[macro_export] +macro_rules! new_identified_vec { + (of: $item_ty: ty, named: $struct_name: ident) => { + pub struct $struct_name(IdentifiedVecOf<$item_ty>); -#[proc_macro_attribute] -pub fn show_streams(attr: TokenStream, item: TokenStream) -> TokenStream { - println!("attributes: \"{}\"", attr.to_string()); - println!("item= \"{}\"", item.to_string()); - item + impl $struct_name { + /// Constructs a new, empty `IdentifiedVec`, using `id()` on `Element` + /// as id function. + pub fn new() -> Self { + Self(IdentifiedVecOf::new()) + } + } + }; } diff --git a/tests/tests.rs b/tests/tests.rs index de32eeb..9bda95e 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -5,7 +5,7 @@ use identified_vec::{ IdentifiedVecOfSerdeFailure, IsIdentifiableVec, }; -use identified_vec_macros::show_streams; +use identified_vec_macros::new_identified_vec; #[derive(Eq, PartialEq, Clone)] pub struct User { @@ -540,9 +540,8 @@ fn hash() { #[test] fn test_macro() { - // Example: Attribute with input - #[show_streams(biz)] - fn invoke2() {} - // printed: attributes: "biz" - // printed: item= "fn invoke2() {}" + new_identified_vec!(of: User, named: CollectionOfUsers); + let mut users = CollectionOfUsers::new(); + users.0.append(User::blob()); + assert_eq!(users.0.items(), [User::blob()]) } From d8702ac00386371508495b25161ddce2c14c22a4 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Thu, 14 Dec 2023 20:51:50 +0100 Subject: [PATCH 06/24] WIP --- identified_vec_macros/src/lib.rs | 2 +- tests/tests.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index 926b53a..0e2043c 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -1,5 +1,5 @@ #[macro_export] -macro_rules! new_identified_vec { +macro_rules! newtype_identified_vec { (of: $item_ty: ty, named: $struct_name: ident) => { pub struct $struct_name(IdentifiedVecOf<$item_ty>); diff --git a/tests/tests.rs b/tests/tests.rs index 9bda95e..e51b40a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -5,7 +5,7 @@ use identified_vec::{ IdentifiedVecOfSerdeFailure, IsIdentifiableVec, }; -use identified_vec_macros::new_identified_vec; +use identified_vec_macros::newtype_identified_vec; #[derive(Eq, PartialEq, Clone)] pub struct User { @@ -540,7 +540,7 @@ fn hash() { #[test] fn test_macro() { - new_identified_vec!(of: User, named: CollectionOfUsers); + newtype_identified_vec!(of: User, named: CollectionOfUsers); let mut users = CollectionOfUsers::new(); users.0.append(User::blob()); assert_eq!(users.0.items(), [User::blob()]) From 3c35236f315c68d81643414e5b3e8d1869259bb2 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 15 Dec 2023 10:06:34 +0100 Subject: [PATCH 07/24] restructure is_identified_vec_of moved to seperate file --- .../src/conflict_resolution_choice.rs | 10 + identified_vec/src/is_identifiable_vec.rs | 254 +++++++++++++++++ identified_vec/src/lib.rs | 5 +- identified_vec/src/vec.rs | 263 +----------------- identified_vec/src/vec_of.rs | 4 +- 5 files changed, 272 insertions(+), 264 deletions(-) create mode 100644 identified_vec/src/conflict_resolution_choice.rs create mode 100644 identified_vec/src/is_identifiable_vec.rs diff --git a/identified_vec/src/conflict_resolution_choice.rs b/identified_vec/src/conflict_resolution_choice.rs new file mode 100644 index 0000000..4d2f1c4 --- /dev/null +++ b/identified_vec/src/conflict_resolution_choice.rs @@ -0,0 +1,10 @@ +/// Representation of a choice in a conflict resolution +/// where two elements with the same Self::ID exists, if `ChooseFirst`, +/// is specified the first/current/existing value will be used, but +/// if `ChooseLast` is specified then the new/last will be replace +/// the first/current/existing. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ConflictResolutionChoice { + ChooseFirst, + ChooseLast, +} diff --git a/identified_vec/src/is_identifiable_vec.rs b/identified_vec/src/is_identifiable_vec.rs new file mode 100644 index 0000000..a6871a2 --- /dev/null +++ b/identified_vec/src/is_identifiable_vec.rs @@ -0,0 +1,254 @@ +use crate::conflict_resolution_choice::ConflictResolutionChoice; +use std::fmt::Debug; +use std::hash::Hash; + +pub trait IsIdentifiableVec: Sized { + type Element; + type ID: Eq + Hash + Clone + Debug; + + /// Constructs a new, empty `IdentifiedVec` with the specified + /// `id_of_element` closure + fn new_identifying_element(id_of_element: fn(&Self::Element) -> Self::ID) -> Self; + + /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to + /// determine the element for any elements with duplicate identity. + /// + /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements + /// that may not have unique ids. This initializer calls the `combine` closure with the current + /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element + /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them + /// to produce a new element, or even throw an error. + /// + /// - Parameters: + /// - elements: A sequence of elements to use for the new `identified_vec`. + /// - id_of_element: The function which extracts the identifier for an element, + /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. + /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` + /// implements high-quality hashing. + fn try_from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Self::Element) -> Self::ID, + combine: fn( + (usize, &Self::Element, &Self::Element), + ) -> Result, + ) -> Result + where + It: IntoIterator; + + fn from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Self::Element) -> Self::ID, + combine: fn((usize, &Self::Element, &Self::Element)) -> ConflictResolutionChoice, + ) -> Self + where + It: IntoIterator; + + /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. + fn ids(&self) -> &Vec; + + /// Returns the number of elements in the `identified_vec`, also referred to as its 'length'. + fn len(&self) -> usize; + + /// Returns the index for the given id. + /// + /// If an element identified by the given id is found in the `identified_vec`, this method returns an index + /// into the array that corresponds to the element. + /// + /// ``` + /// extern crate identified_vec; + /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// + /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] + /// struct User { + /// id: &'static str, + /// } + /// + /// impl User { + /// fn new(id: &'static str) -> Self { + /// Self { id } + /// } + /// } + /// + /// impl Identifiable for User { + /// type ID = &'static str; + /// fn id(&self) -> Self::ID { + /// self.id + /// } + /// } + /// + /// let mut users = + /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); + /// + /// assert_eq!(users.index_of_id(&"u_1729"), Some(1)); + /// assert_eq!(users.index_of_id(&"u_1337"), None); + /// ``` + /// + /// - Parameter id: The id to find in the `identified_vec`. + /// - Returns: The index for the element identified by `id` if found in the `identified_vec`; otherwise, + /// `None`. + /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. + fn index_of_id(&self, id: &Self::ID) -> Option; + + /// Returns a mutable reference to the element identified by `id` if any, else None. + /// + /// - Parameter id: The id to find in the `identified_vec`. + /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, + /// `None`. + /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. + fn get_mut(&mut self, id: &Self::ID) -> Option<&mut Self::Element>; + + fn elements(&self) -> Vec<&Self::Element>; + + /// Returns `true` if the `identified_vec` contains the `element.` + fn contains(&self, element: &Self::Element) -> bool; + + /// Returns `true if the `identified_vec` contains an element for the specified `id` + fn contains_id(&self, id: &Self::ID) -> bool; + + /// Returns a reference to the element corresponding to the `id`, if found, else `None`. + fn get(&self, id: &Self::ID) -> Option<&Self::Element>; + + /// Returns a reference to the element at index if found, else `None`. + fn get_at_index(&self, index: usize) -> Option<&Self::Element>; + + /// Append a new member to the end of the `identified_vec`, if the `identified_vec` doesn't already contain it. + /// + /// - Parameter item: The element to add to the `identified_vec`. + /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether + /// the operation added a new element, and `index` is the index of `item` in the resulting + /// `identified_vec`. + /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on + /// the `Self::ID` type, if it implements high-quality hashing. + fn append(&mut self, element: Self::Element) -> (bool, usize); + + /// Append the contents of an iterator to the end of the set, excluding elements that are already + /// members. + /// + /// - Parameter elements: A finite sequence of elements to append. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::Element` type, if it implements high-quality hashing. + fn append_other(&mut self, other: It) + where + It: IntoIterator; + + /// Adds the given element to the `identified_vec` unconditionally, either appending it to the `identified_vec``, or + /// replacing an existing value if it's already present. + /// + /// - Parameter item: The value to append or replace. + /// - Returns: The original element that was replaced by this operation, or `None` if the value was + /// appended to the end of the collection. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::ID` type, if it implements high-quality hashing. + fn update_or_append(&mut self, element: Self::Element) -> Option; + + /// Replace the member at the given index with a new value of the same identity. + /// + /// - Parameter item: The new value that should replace the original element. `item` must match + /// the identity of the original value. + /// - Parameter index: The index of the element to be replaced. + /// - Returns: The original element that was replaced. + fn update_at(&mut self, element: Self::Element, index: usize) -> Self::Element; + + /// Insert a new member to this identified_vec at the specified index, if the identified_vec doesn't already contain + /// it. + /// + /// - Parameter element: The element to insert. + /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether + /// the operation added a new element, and `index` is the index of `element` in the resulting + /// identified_vec. If `inserted` is true, then the returned `index` may be different from the index + /// requested. + /// + /// - Complexity: The operation is expected to perform amortized O(`self.count`) copy, hash, and + /// compare operations on the `Self::ID` type, if it implements high-quality hashing. (Insertions need + /// to make room in the storage identified_vec to add the inserted element.) + fn insert(&mut self, element: Self::Element, at: usize) -> (bool, usize); + + /// Adds the given element into the set unconditionally, either inserting it at the specified + /// index, or replacing an existing value if it's already present. + /// + /// - Parameter item: The value to append or replace. + /// - Parameter index: The index at which to insert the new member if `item` isn't already in the + /// set. + /// - Returns: The original element that was replaced by this operation, or `None` if the value was + /// newly inserted into the collection. + /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare + /// operations on the `Self::ID` type, if it implements high-quality hashing. + fn update_or_insert( + &mut self, + element: Self::Element, + index: usize, + ) -> (Option, usize); + + ///////////// + // Remove // + ///////////// + + /// Removes the element identified by the given id from the `identified_vec`. + /// + /// ``` + /// extern crate identified_vec; + /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// + /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] + /// struct User { + /// id: &'static str, + /// } + /// + /// impl User { + /// fn new(id: &'static str) -> Self { + /// Self { id } + /// } + /// } + /// + /// impl Identifiable for User { + /// type ID = &'static str; + /// fn id(&self) -> Self::ID { + /// self.id + /// } + /// } + /// + /// let mut users = + /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); + /// + /// assert_eq!(users.remove_by_id(&"u_1729"), Some(User::new("u_1729"))); + /// assert_eq!(users.elements(), [&User::new("u_42")]); + /// assert_eq!(users.remove_by_id(&"u_1337"), None); + /// assert_eq!(users.len(), 1); + /// ``` + /// + /// - Parameter id: The id of the element to be removed from the `identified_vec`. + /// - Returns: The element that was removed, or `None` if the element was not present in the array. + /// - Complexity: O(`count`) + fn remove_by_id(&mut self, id: &Self::ID) -> Option; + + /// Removes the given element from the `identified_vec`. + /// + /// If the element is found in the `identified_vec`, this method returns the element. + /// + /// If the element isn't found in the `identified_vec`, `remove` returns `None`. + /// + /// - Parameter element: The element to remove. + /// - Returns: The value that was removed, or `None` if the element was not present in the `identified_vec`. + /// - Complexity: O(`count`) + fn remove(&mut self, element: &Self::Element) -> Option; + + /// Removes and returns the element at the specified position. + /// + /// All the elements following the specified position are moved to close the resulting gap. + /// + /// - Parameter index: The position of the element to remove. + /// - Returns: The removed element. + /// - Precondition: `index` must be a valid index of the collection that is not equal to the + /// collection's end index. + /// - Complexity: O(`count`) + fn remove_at(&mut self, index: usize) -> Self::Element; + + /// Removes all the elements at the specified `offsets` from the `identified_vec`. + /// + /// - Parameter offsets: The offsets of all elements to be removed. + /// - Complexity: O(*n*) where *n* is the length of the `identified_vec`. + fn remove_at_offsets(&mut self, offsets: It) + where + It: IntoIterator; +} diff --git a/identified_vec/src/lib.rs b/identified_vec/src/lib.rs index c5b436d..b24beb0 100644 --- a/identified_vec/src/lib.rs +++ b/identified_vec/src/lib.rs @@ -129,7 +129,9 @@ //! let numbers = IdentifiedVec::::new_identifying_element(|e| *e); //! ``` +mod conflict_resolution_choice; mod identifiable_trait; +mod is_identifiable_vec; mod primitives_identifiable; mod serde_error; mod vec; @@ -137,6 +139,8 @@ mod vec_of; pub mod identified_vec { //! A collection of unique identifiable elements which retains **insertion** order. + pub use crate::conflict_resolution_choice::*; + pub use crate::is_identifiable_vec::*; pub use crate::vec::*; } @@ -158,4 +162,3 @@ pub mod identified_vec_of { pub use crate::identified_vec::*; pub use crate::identified_vec_of::*; -pub use crate::vec::IsIdentifiableVec; diff --git a/identified_vec/src/vec.rs b/identified_vec/src/vec.rs index 38c0808..dbc3391 100644 --- a/identified_vec/src/vec.rs +++ b/identified_vec/src/vec.rs @@ -1,268 +1,9 @@ +use crate::conflict_resolution_choice::ConflictResolutionChoice; use std::collections::HashMap; use std::fmt::{Debug, Display}; use std::hash::{Hash, Hasher}; -pub trait IsIdentifiableVec: Sized { - type Element; - type ID: Eq + Hash + Clone + Debug; - - /// Constructs a new, empty `IdentifiedVec` with the specified - /// `id_of_element` closure - fn new_identifying_element(id_of_element: fn(&Self::Element) -> Self::ID) -> Self; - - /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to - /// determine the element for any elements with duplicate identity. - /// - /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements - /// that may not have unique ids. This initializer calls the `combine` closure with the current - /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element - /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them - /// to produce a new element, or even throw an error. - /// - /// - Parameters: - /// - elements: A sequence of elements to use for the new `identified_vec`. - /// - id_of_element: The function which extracts the identifier for an element, - /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. - /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` - /// implements high-quality hashing. - fn try_from_iter_select_unique_ids_with( - elements: It, - id_of_element: fn(&Self::Element) -> Self::ID, - combine: fn( - (usize, &Self::Element, &Self::Element), - ) -> Result, - ) -> Result - where - It: IntoIterator; - - fn from_iter_select_unique_ids_with( - elements: It, - id_of_element: fn(&Self::Element) -> Self::ID, - combine: fn((usize, &Self::Element, &Self::Element)) -> ConflictResolutionChoice, - ) -> Self - where - It: IntoIterator; - - /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. - fn ids(&self) -> &Vec; - - /// Returns the number of elements in the `identified_vec`, also referred to as its 'length'. - fn len(&self) -> usize; - - /// Returns the index for the given id. - /// - /// If an element identified by the given id is found in the `identified_vec`, this method returns an index - /// into the array that corresponds to the element. - /// - /// ``` - /// extern crate identified_vec; - /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; - /// - /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] - /// struct User { - /// id: &'static str, - /// } - /// - /// impl User { - /// fn new(id: &'static str) -> Self { - /// Self { id } - /// } - /// } - /// - /// impl Identifiable for User { - /// type ID = &'static str; - /// fn id(&self) -> Self::ID { - /// self.id - /// } - /// } - /// - /// let mut users = - /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); - /// - /// assert_eq!(users.index_of_id(&"u_1729"), Some(1)); - /// assert_eq!(users.index_of_id(&"u_1337"), None); - /// ``` - /// - /// - Parameter id: The id to find in the `identified_vec`. - /// - Returns: The index for the element identified by `id` if found in the `identified_vec`; otherwise, - /// `None`. - /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. - fn index_of_id(&self, id: &Self::ID) -> Option; - - /// Returns a mutable reference to the element identified by `id` if any, else None. - /// - /// - Parameter id: The id to find in the `identified_vec`. - /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, - /// `None`. - /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. - fn get_mut(&mut self, id: &Self::ID) -> Option<&mut Self::Element>; - - fn elements(&self) -> Vec<&Self::Element>; - - /// Returns `true` if the `identified_vec` contains the `element.` - fn contains(&self, element: &Self::Element) -> bool; - - /// Returns `true if the `identified_vec` contains an element for the specified `id` - fn contains_id(&self, id: &Self::ID) -> bool; - - /// Returns a reference to the element corresponding to the `id`, if found, else `None`. - fn get(&self, id: &Self::ID) -> Option<&Self::Element>; - - /// Returns a reference to the element at index if found, else `None`. - fn get_at_index(&self, index: usize) -> Option<&Self::Element>; - - /// Append a new member to the end of the `identified_vec`, if the `identified_vec` doesn't already contain it. - /// - /// - Parameter item: The element to add to the `identified_vec`. - /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether - /// the operation added a new element, and `index` is the index of `item` in the resulting - /// `identified_vec`. - /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on - /// the `Self::ID` type, if it implements high-quality hashing. - fn append(&mut self, element: Self::Element) -> (bool, usize); - - /// Append the contents of an iterator to the end of the set, excluding elements that are already - /// members. - /// - /// - Parameter elements: A finite sequence of elements to append. - /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::Element` type, if it implements high-quality hashing. - fn append_other(&mut self, other: It) - where - It: IntoIterator; - - /// Adds the given element to the `identified_vec` unconditionally, either appending it to the `identified_vec``, or - /// replacing an existing value if it's already present. - /// - /// - Parameter item: The value to append or replace. - /// - Returns: The original element that was replaced by this operation, or `None` if the value was - /// appended to the end of the collection. - /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::ID` type, if it implements high-quality hashing. - fn update_or_append(&mut self, element: Self::Element) -> Option; - - /// Replace the member at the given index with a new value of the same identity. - /// - /// - Parameter item: The new value that should replace the original element. `item` must match - /// the identity of the original value. - /// - Parameter index: The index of the element to be replaced. - /// - Returns: The original element that was replaced. - fn update_at(&mut self, element: Self::Element, index: usize) -> Self::Element; - - /// Insert a new member to this identified_vec at the specified index, if the identified_vec doesn't already contain - /// it. - /// - /// - Parameter element: The element to insert. - /// - Returns: A pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether - /// the operation added a new element, and `index` is the index of `element` in the resulting - /// identified_vec. If `inserted` is true, then the returned `index` may be different from the index - /// requested. - /// - /// - Complexity: The operation is expected to perform amortized O(`self.count`) copy, hash, and - /// compare operations on the `Self::ID` type, if it implements high-quality hashing. (Insertions need - /// to make room in the storage identified_vec to add the inserted element.) - fn insert(&mut self, element: Self::Element, at: usize) -> (bool, usize); - - /// Adds the given element into the set unconditionally, either inserting it at the specified - /// index, or replacing an existing value if it's already present. - /// - /// - Parameter item: The value to append or replace. - /// - Parameter index: The index at which to insert the new member if `item` isn't already in the - /// set. - /// - Returns: The original element that was replaced by this operation, or `None` if the value was - /// newly inserted into the collection. - /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::ID` type, if it implements high-quality hashing. - fn update_or_insert( - &mut self, - element: Self::Element, - index: usize, - ) -> (Option, usize); - - ///////////// - // Remove // - ///////////// - - /// Removes the element identified by the given id from the `identified_vec`. - /// - /// ``` - /// extern crate identified_vec; - /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; - /// - /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] - /// struct User { - /// id: &'static str, - /// } - /// - /// impl User { - /// fn new(id: &'static str) -> Self { - /// Self { id } - /// } - /// } - /// - /// impl Identifiable for User { - /// type ID = &'static str; - /// fn id(&self) -> Self::ID { - /// self.id - /// } - /// } - /// - /// let mut users = - /// IdentifiedVecOf::::from_iter([User::new("u_42"), User::new("u_1729")]); - /// - /// assert_eq!(users.remove_by_id(&"u_1729"), Some(User::new("u_1729"))); - /// assert_eq!(users.elements(), [&User::new("u_42")]); - /// assert_eq!(users.remove_by_id(&"u_1337"), None); - /// assert_eq!(users.len(), 1); - /// ``` - /// - /// - Parameter id: The id of the element to be removed from the `identified_vec`. - /// - Returns: The element that was removed, or `None` if the element was not present in the array. - /// - Complexity: O(`count`) - fn remove_by_id(&mut self, id: &Self::ID) -> Option; - - /// Removes the given element from the `identified_vec`. - /// - /// If the element is found in the `identified_vec`, this method returns the element. - /// - /// If the element isn't found in the `identified_vec`, `remove` returns `None`. - /// - /// - Parameter element: The element to remove. - /// - Returns: The value that was removed, or `None` if the element was not present in the `identified_vec`. - /// - Complexity: O(`count`) - fn remove(&mut self, element: &Self::Element) -> Option; - - /// Removes and returns the element at the specified position. - /// - /// All the elements following the specified position are moved to close the resulting gap. - /// - /// - Parameter index: The position of the element to remove. - /// - Returns: The removed element. - /// - Precondition: `index` must be a valid index of the collection that is not equal to the - /// collection's end index. - /// - Complexity: O(`count`) - fn remove_at(&mut self, index: usize) -> Self::Element; - - /// Removes all the elements at the specified `offsets` from the `identified_vec`. - /// - /// - Parameter offsets: The offsets of all elements to be removed. - /// - Complexity: O(*n*) where *n* is the length of the `identified_vec`. - fn remove_at_offsets(&mut self, offsets: It) - where - It: IntoIterator; -} - -/// Representation of a choice in a conflict resolution -/// where two elements with the same Self::ID exists, if `ChooseFirst`, -/// is specified the first/current/existing value will be used, but -/// if `ChooseLast` is specified then the new/last will be replace -/// the first/current/existing. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum ConflictResolutionChoice { - ChooseFirst, - ChooseLast, -} +use crate::is_identifiable_vec::IsIdentifiableVec; /// An ordered collection of identifiable elements. /// diff --git a/identified_vec/src/vec_of.rs b/identified_vec/src/vec_of.rs index f915195..78b4a63 100644 --- a/identified_vec/src/vec_of.rs +++ b/identified_vec/src/vec_of.rs @@ -6,9 +6,9 @@ use std::fmt::Debug; #[cfg(feature = "serde")] use crate::serde_error::IdentifiedVecOfSerdeFailure; use crate::{ - identified_vec_of::Identifiable, - vec::{ConflictResolutionChoice, IdentifiedVec, IsIdentifiableVec}, + conflict_resolution_choice::ConflictResolutionChoice, is_identifiable_vec::IsIdentifiableVec, }; +use crate::{identified_vec_of::Identifiable, vec::IdentifiedVec}; #[cfg(feature = "serde")] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; From 3dd010c7dda63f5e161e5cf07e51c4edc8f6f74f Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 15 Dec 2023 13:01:00 +0100 Subject: [PATCH 08/24] WIP --- identified_vec/src/is_identifiable_vec.rs | 84 ++++++++++----------- identified_vec/src/lib.rs | 4 + identified_vec/src/vec.rs | 92 +++++++++++------------ tests/tests.rs | 22 +++++- 4 files changed, 105 insertions(+), 97 deletions(-) diff --git a/identified_vec/src/is_identifiable_vec.rs b/identified_vec/src/is_identifiable_vec.rs index a6871a2..6ee0fff 100644 --- a/identified_vec/src/is_identifiable_vec.rs +++ b/identified_vec/src/is_identifiable_vec.rs @@ -2,13 +2,13 @@ use crate::conflict_resolution_choice::ConflictResolutionChoice; use std::fmt::Debug; use std::hash::Hash; -pub trait IsIdentifiableVec: Sized { - type Element; - type ID: Eq + Hash + Clone + Debug; - +pub trait IsIdentifiableVec: Sized +where + ID: Eq + Hash + Clone + Debug, +{ /// Constructs a new, empty `IdentifiedVec` with the specified /// `id_of_element` closure - fn new_identifying_element(id_of_element: fn(&Self::Element) -> Self::ID) -> Self; + fn new_identifying_element(id_of_element: fn(&Element) -> ID) -> Self; /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to /// determine the element for any elements with duplicate identity. @@ -24,28 +24,26 @@ pub trait IsIdentifiableVec: Sized { /// - id_of_element: The function which extracts the identifier for an element, /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` /// implements high-quality hashing. fn try_from_iter_select_unique_ids_with( elements: It, - id_of_element: fn(&Self::Element) -> Self::ID, - combine: fn( - (usize, &Self::Element, &Self::Element), - ) -> Result, + id_of_element: fn(&Element) -> ID, + combine: fn((usize, &Element, &Element)) -> Result, ) -> Result where - It: IntoIterator; + It: IntoIterator; fn from_iter_select_unique_ids_with( elements: It, - id_of_element: fn(&Self::Element) -> Self::ID, - combine: fn((usize, &Self::Element, &Self::Element)) -> ConflictResolutionChoice, + id_of_element: fn(&Element) -> ID, + combine: fn((usize, &Element, &Element)) -> ConflictResolutionChoice, ) -> Self where - It: IntoIterator; + It: IntoIterator; - /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. - fn ids(&self) -> &Vec; + ///The ids contained in this `identified_vec`, as an `Vec` (cloned) + fn ids(&self) -> Vec; /// Returns the number of elements in the `identified_vec`, also referred to as its 'length'. fn len(&self) -> usize; @@ -72,7 +70,7 @@ pub trait IsIdentifiableVec: Sized { /// /// impl Identifiable for User { /// type ID = &'static str; - /// fn id(&self) -> Self::ID { + /// fn id(&self) -> ID { /// self.id /// } /// } @@ -87,30 +85,30 @@ pub trait IsIdentifiableVec: Sized { /// - Parameter id: The id to find in the `identified_vec`. /// - Returns: The index for the element identified by `id` if found in the `identified_vec`; otherwise, /// `None`. - /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. - fn index_of_id(&self, id: &Self::ID) -> Option; + /// - Complexity: Expected to be O(1) on average, if `ID` implements high-quality hashing. + fn index_of_id(&self, id: &ID) -> Option; /// Returns a mutable reference to the element identified by `id` if any, else None. /// /// - Parameter id: The id to find in the `identified_vec`. /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, /// `None`. - /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. - fn get_mut(&mut self, id: &Self::ID) -> Option<&mut Self::Element>; + /// - Complexity: Expected to be O(1) on average, if `ID` implements high-quality hashing. + fn get_mut(&mut self, id: &ID) -> Option<&mut Element>; - fn elements(&self) -> Vec<&Self::Element>; + fn elements(&self) -> Vec<&Element>; /// Returns `true` if the `identified_vec` contains the `element.` - fn contains(&self, element: &Self::Element) -> bool; + fn contains(&self, element: &Element) -> bool; /// Returns `true if the `identified_vec` contains an element for the specified `id` - fn contains_id(&self, id: &Self::ID) -> bool; + fn contains_id(&self, id: &ID) -> bool; /// Returns a reference to the element corresponding to the `id`, if found, else `None`. - fn get(&self, id: &Self::ID) -> Option<&Self::Element>; + fn get(&self, id: &ID) -> Option<&Element>; /// Returns a reference to the element at index if found, else `None`. - fn get_at_index(&self, index: usize) -> Option<&Self::Element>; + fn get_at_index(&self, index: usize) -> Option<&Element>; /// Append a new member to the end of the `identified_vec`, if the `identified_vec` doesn't already contain it. /// @@ -119,18 +117,18 @@ pub trait IsIdentifiableVec: Sized { /// the operation added a new element, and `index` is the index of `item` in the resulting /// `identified_vec`. /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on - /// the `Self::ID` type, if it implements high-quality hashing. - fn append(&mut self, element: Self::Element) -> (bool, usize); + /// the `ID` type, if it implements high-quality hashing. + fn append(&mut self, element: Element) -> (bool, usize); /// Append the contents of an iterator to the end of the set, excluding elements that are already /// members. /// /// - Parameter elements: A finite sequence of elements to append. /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::Element` type, if it implements high-quality hashing. + /// operations on the `Element` type, if it implements high-quality hashing. fn append_other(&mut self, other: It) where - It: IntoIterator; + It: IntoIterator; /// Adds the given element to the `identified_vec` unconditionally, either appending it to the `identified_vec``, or /// replacing an existing value if it's already present. @@ -139,8 +137,8 @@ pub trait IsIdentifiableVec: Sized { /// - Returns: The original element that was replaced by this operation, or `None` if the value was /// appended to the end of the collection. /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::ID` type, if it implements high-quality hashing. - fn update_or_append(&mut self, element: Self::Element) -> Option; + /// operations on the `ID` type, if it implements high-quality hashing. + fn update_or_append(&mut self, element: Element) -> Option; /// Replace the member at the given index with a new value of the same identity. /// @@ -148,7 +146,7 @@ pub trait IsIdentifiableVec: Sized { /// the identity of the original value. /// - Parameter index: The index of the element to be replaced. /// - Returns: The original element that was replaced. - fn update_at(&mut self, element: Self::Element, index: usize) -> Self::Element; + fn update_at(&mut self, element: Element, index: usize) -> Element; /// Insert a new member to this identified_vec at the specified index, if the identified_vec doesn't already contain /// it. @@ -160,9 +158,9 @@ pub trait IsIdentifiableVec: Sized { /// requested. /// /// - Complexity: The operation is expected to perform amortized O(`self.count`) copy, hash, and - /// compare operations on the `Self::ID` type, if it implements high-quality hashing. (Insertions need + /// compare operations on the `ID` type, if it implements high-quality hashing. (Insertions need /// to make room in the storage identified_vec to add the inserted element.) - fn insert(&mut self, element: Self::Element, at: usize) -> (bool, usize); + fn insert(&mut self, element: Element, at: usize) -> (bool, usize); /// Adds the given element into the set unconditionally, either inserting it at the specified /// index, or replacing an existing value if it's already present. @@ -173,12 +171,8 @@ pub trait IsIdentifiableVec: Sized { /// - Returns: The original element that was replaced by this operation, or `None` if the value was /// newly inserted into the collection. /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::ID` type, if it implements high-quality hashing. - fn update_or_insert( - &mut self, - element: Self::Element, - index: usize, - ) -> (Option, usize); + /// operations on the `ID` type, if it implements high-quality hashing. + fn update_or_insert(&mut self, element: Element, index: usize) -> (Option, usize); ///////////// // Remove // @@ -203,7 +197,7 @@ pub trait IsIdentifiableVec: Sized { /// /// impl Identifiable for User { /// type ID = &'static str; - /// fn id(&self) -> Self::ID { + /// fn id(&self) -> ID { /// self.id /// } /// } @@ -220,7 +214,7 @@ pub trait IsIdentifiableVec: Sized { /// - Parameter id: The id of the element to be removed from the `identified_vec`. /// - Returns: The element that was removed, or `None` if the element was not present in the array. /// - Complexity: O(`count`) - fn remove_by_id(&mut self, id: &Self::ID) -> Option; + fn remove_by_id(&mut self, id: &ID) -> Option; /// Removes the given element from the `identified_vec`. /// @@ -231,7 +225,7 @@ pub trait IsIdentifiableVec: Sized { /// - Parameter element: The element to remove. /// - Returns: The value that was removed, or `None` if the element was not present in the `identified_vec`. /// - Complexity: O(`count`) - fn remove(&mut self, element: &Self::Element) -> Option; + fn remove(&mut self, element: &Element) -> Option; /// Removes and returns the element at the specified position. /// @@ -242,7 +236,7 @@ pub trait IsIdentifiableVec: Sized { /// - Precondition: `index` must be a valid index of the collection that is not equal to the /// collection's end index. /// - Complexity: O(`count`) - fn remove_at(&mut self, index: usize) -> Self::Element; + fn remove_at(&mut self, index: usize) -> Element; /// Removes all the elements at the specified `offsets` from the `identified_vec`. /// diff --git a/identified_vec/src/lib.rs b/identified_vec/src/lib.rs index b24beb0..5fa5140 100644 --- a/identified_vec/src/lib.rs +++ b/identified_vec/src/lib.rs @@ -132,6 +132,8 @@ mod conflict_resolution_choice; mod identifiable_trait; mod is_identifiable_vec; +mod is_identifiable_vec_of; +mod is_identified_vec_via; mod primitives_identifiable; mod serde_error; mod vec; @@ -151,6 +153,8 @@ pub mod identified_vec_of { //! skip the `id_of_element: fn(&Element) -> ID` closure when //! initializing a new identified vec. pub use crate::identifiable_trait::*; + pub use crate::is_identifiable_vec_of::*; + pub use crate::is_identified_vec_via::*; pub use crate::vec_of::*; #[cfg(feature = "id_prim")] diff --git a/identified_vec/src/vec.rs b/identified_vec/src/vec.rs index dbc3391..af6a604 100644 --- a/identified_vec/src/vec.rs +++ b/identified_vec/src/vec.rs @@ -46,7 +46,7 @@ use crate::is_identifiable_vec::IsIdentifiableVec; /// /// impl Identifiable for User { /// type ID = &'static str; -/// fn id(&self) -> Self::ID { +/// fn id(&self) -> I { /// self.id /// } /// } @@ -76,7 +76,7 @@ use crate::is_identifiable_vec::IsIdentifiableVec; /// .collect::>() /// ); /// -/// // Self::Element with same Self::ID is not appended: +/// // E with same I is not appended: /// users.append(User::new("u_42", "Tom Mervolo Dolder")); /// assert_eq!( /// users.elements(), @@ -89,7 +89,7 @@ use crate::is_identifiable_vec::IsIdentifiableVec; /// .collect::>() /// ); /// -/// // Self::Element with same Self::ID replaces existing if an `update_*` method is used: +/// // E with same I replaces existing if an `update_*` method is used: /// // e.g. `update_or_insert`: /// users.update_or_insert(User::new("u_42", "Tom Mervolo Dolder"), 0); /// assert_eq!( @@ -148,7 +148,7 @@ use crate::is_identifiable_vec::IsIdentifiableVec; /// # Performance /// /// Like the standard `HashMap` type, the performance of hashing operations in -/// `IdentifiedVec` is highly sensitive to the quality of hashing implemented by the `Self::ID` +/// `IdentifiedVec` is highly sensitive to the quality of hashing implemented by the `I` /// type. Failing to correctly implement hashing can easily lead to unacceptable performance, with /// the severity of the effect increasing with the size of the underlying hash table. /// @@ -157,7 +157,7 @@ use crate::is_identifiable_vec::IsIdentifiableVec; /// ensure hashed collection types exhibit their target performance, it is important to ensure that /// such collisions cannot be induced merely by adding a particular list of members to the set. /// -/// When `Self::ID` implements `Hash` correctly, testing for membership in an ordered set is expected +/// When `I` implements `Hash` correctly, testing for membership in an ordered set is expected /// to take O(1) equality checks on average. Hash collisions can still occur organically, so the /// worst-case lookup performance is technically still O(*n*) (where *n* is the size of the set); /// however, long lookup chains are unlikely to occur in practice. @@ -178,11 +178,11 @@ where /// The storage of elements. pub(crate) elements: HashMap, - /// Function which extracts the Self::ID of an Self::Element. + /// Function which extracts the I of an E. pub(crate) _id_of_element: fn(&E) -> I, } -impl IsIdentifiableVec for IdentifiedVec +impl IsIdentifiableVec for IdentifiedVec where I: Eq + Hash + Clone + Debug, { @@ -190,9 +190,6 @@ where // Constructors // //////////////////// - type ID = I; - type Element = E; - /// Constructs a new, empty `IdentifiedVec` with the specified /// `id_of_element` closure #[inline] @@ -218,22 +215,20 @@ where /// - id_of_element: The function which extracts the identifier for an element, /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `I` /// implements high-quality hashing. #[cfg(not(tarpaulin_include))] // false negative #[inline] fn try_from_iter_select_unique_ids_with( elements: It, - id_of_element: fn(&Self::Element) -> Self::ID, - combine: fn( - (usize, &Self::Element, &Self::Element), - ) -> Result, + id_of_element: fn(&E) -> I, + combine: fn((usize, &E, &E)) -> Result, ) -> Result where - It: IntoIterator, + It: IntoIterator, { - let mut _order = Vec::::new(); - let mut _elements = HashMap::::new(); + let mut _order = Vec::::new(); + let mut _elements = HashMap::::new(); for element in elements.into_iter() { let id = id_of_element(&element); @@ -278,20 +273,20 @@ where /// - id_of_element: The function which extracts the identifier for an element, /// - combine: Closure used combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`) /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. - /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `Self::ID` + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `I` /// implements high-quality hashing. #[cfg(not(tarpaulin_include))] // false negative #[inline] fn from_iter_select_unique_ids_with( elements: It, - id_of_element: fn(&Self::Element) -> Self::ID, - combine: fn((usize, &Self::Element, &Self::Element)) -> ConflictResolutionChoice, + id_of_element: fn(&E) -> I, + combine: fn((usize, &E, &E)) -> ConflictResolutionChoice, ) -> Self where - It: IntoIterator, + It: IntoIterator, { - let mut _order = Vec::::new(); - let mut _elements = HashMap::::new(); + let mut _order = Vec::::new(); + let mut _elements = HashMap::::new(); for element in elements.into_iter() { let id = id_of_element(&element); @@ -319,12 +314,12 @@ where // Public Get // //////////////////// - /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. + /// A read-only collection view for the ids contained in this `identified_vec`, as an `&Vec`. /// /// - Complexity: O(1) #[inline] - fn ids(&self) -> &Vec { - &self.order + fn ids(&self) -> Vec { + self.order.clone() } /// Returns the number of elements in the `identified_vec`, also referred to as its 'length'. @@ -358,7 +353,7 @@ where /// /// impl Identifiable for User { /// type ID = &'static str; - /// fn id(&self) -> Self::ID { + /// fn id(&self) -> I { /// self.id /// } /// } @@ -373,9 +368,9 @@ where /// - Parameter id: The id to find in the `identified_vec`. /// - Returns: The index for the element identified by `id` if found in the `identified_vec`; otherwise, /// `None`. - /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. + /// - Complexity: Expected to be O(1) on average, if `I` implements high-quality hashing. #[inline] - fn index_of_id(&self, id: &Self::ID) -> Option { + fn index_of_id(&self, id: &I) -> Option { self.order.iter().position(|i| i == id) } @@ -384,9 +379,9 @@ where /// - Parameter id: The id to find in the `identified_vec`. /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, /// `None`. - /// - Complexity: Expected to be O(1) on average, if `Self::ID` implements high-quality hashing. + /// - Complexity: Expected to be O(1) on average, if `I` implements high-quality hashing. #[inline] - fn get_mut(&mut self, id: &Self::ID) -> Option<&mut Self::Element> { + fn get_mut(&mut self, id: &I) -> Option<&mut E> { self.elements.get_mut(id) } @@ -398,35 +393,35 @@ where /// /// N.B. that this method is not constant time. /// - /// If `Self::Element` implements `Clone` you can use `self.items()` which returns a `Vec`, of cloned elements. + /// If `E` implements `Clone` you can use `self.items()` which returns a `Vec`, of cloned elements. /// /// - Complexity: O(n) #[inline] - fn elements(&self) -> Vec<&Self::Element> { + fn elements(&self) -> Vec<&E> { self.iter().collect() } /// Returns `true` if the `identified_vec` contains the `element.` #[inline] - fn contains(&self, element: &Self::Element) -> bool { + fn contains(&self, element: &E) -> bool { self.elements.contains_key(&self.id(&element)) } /// Returns `true if the `identified_vec` contains an element for the specified `id` #[inline] - fn contains_id(&self, id: &Self::ID) -> bool { + fn contains_id(&self, id: &I) -> bool { self.elements.contains_key(id) } /// Returns a reference to the element corresponding to the `id`, if found, else `None`. #[inline] - fn get(&self, id: &Self::ID) -> Option<&Self::Element> { + fn get(&self, id: &I) -> Option<&E> { self.elements.get(id) } /// Returns a reference to the element at index if found, else `None`. #[inline] - fn get_at_index(&self, index: usize) -> Option<&Self::Element> { + fn get_at_index(&self, index: usize) -> Option<&E> { self.order.get(index).and_then(|id| self.get(id)) } @@ -437,7 +432,7 @@ where /// the operation added a new element, and `index` is the index of `item` in the resulting /// `identified_vec`. /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on - /// the `Self::ID` type, if it implements high-quality hashing. + /// the `I` type, if it implements high-quality hashing. #[inline] fn append(&mut self, element: E) -> (bool, usize) { self.insert(element, self.end_index()) @@ -448,7 +443,7 @@ where /// /// - Parameter elements: A finite sequence of elements to append. /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::Element` type, if it implements high-quality hashing. + /// operations on the `E` type, if it implements high-quality hashing. #[inline] fn append_other(&mut self, other: It) where @@ -464,7 +459,7 @@ where /// - Returns: The original element that was replaced by this operation, or `None` if the value was /// appended to the end of the collection. /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::ID` type, if it implements high-quality hashing. + /// operations on the `I` type, if it implements high-quality hashing. #[inline] fn update_or_append(&mut self, element: E) -> Option { let id = self.id(&element); @@ -505,7 +500,7 @@ where /// requested. /// /// - Complexity: The operation is expected to perform amortized O(`self.count`) copy, hash, and - /// compare operations on the `Self::ID` type, if it implements high-quality hashing. (Insertions need + /// compare operations on the `I` type, if it implements high-quality hashing. (Insertions need /// to make room in the storage identified_vec to add the inserted element.) #[cfg(not(tarpaulin_include))] // false negative #[inline] @@ -527,7 +522,7 @@ where /// - Returns: The original element that was replaced by this operation, or `None` if the value was /// newly inserted into the collection. /// - Complexity: The operation is expected to perform amortized O(1) copy, hash, and compare - /// operations on the `Self::ID` type, if it implements high-quality hashing. + /// operations on the `I` type, if it implements high-quality hashing. #[inline] fn update_or_insert(&mut self, element: E, index: usize) -> (Option, usize) { let id = self.id(&element); @@ -556,7 +551,7 @@ where /// /// impl Identifiable for User { /// type ID = &'static str; - /// fn id(&self) -> Self::ID { + /// fn id(&self) -> I { /// self.id /// } /// } @@ -617,10 +612,7 @@ where .order .get(index) .expect("Precondition failure, index out of bounds"); - let removed = self - .elements - .remove(id) - .expect("Self::Element of existing id"); + let removed = self.elements.remove(id).expect("E of existing id"); self.order.remove(index); return removed; } @@ -796,13 +788,13 @@ where self.len() } - /// Returns the Self::ID of an Self::Element + /// Returns the I of an E #[inline] fn id(&self, of: &E) -> I { (self._id_of_element)(of) } - /// Inserting Self::ID at an index, returning if it did, if not, the index of the existing. + /// Inserting I at an index, returning if it did, if not, the index of the existing. #[cfg(not(tarpaulin_include))] // false negative #[inline] fn _insert_id_at(&mut self, id: I, index: usize) -> (bool, usize) { diff --git a/tests/tests.rs b/tests/tests.rs index e51b40a..294471a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -2,7 +2,8 @@ use std::{cell::RefCell, collections::HashSet, fmt::Debug, ops::Deref}; use identified_vec::{ ConflictResolutionChoice, Identifiable, IdentifiedVec, IdentifiedVecOf, - IdentifiedVecOfSerdeFailure, IsIdentifiableVec, + IdentifiedVecOfSerdeFailure, IsIdentifiableVec, IsIdentifiableVecOf, IsIdentifiableVecOfVia, + ViaMarker, }; use identified_vec_macros::newtype_identified_vec; @@ -536,7 +537,24 @@ fn hash() { ) } -// Test macro +// Test IsIdentifiedVecVia +struct CollectionOfUsersVia(IdentifiedVecOf); +impl ViaMarker for CollectionOfUsersVia {} +impl IsIdentifiableVecOf for CollectionOfUsersVia {} +impl IsIdentifiableVecOfVia for CollectionOfUsersVia { + fn via(&self) -> &mut IdentifiedVecOf { + todo!() + } + fn from_identified_vec_of(identified_vec_of: IdentifiedVecOf) -> Self { + Self(identified_vec_of) + } +} + +// impl IsIdentifiableVecOfVia for CollectionOfUsersVia { +// fn via(&self) -> &mut IdentifiedVecOf { +// todo!() +// } +// } #[test] fn test_macro() { From 131ef07d57e9c5bfc0e00ee71a72c586e3b99cfd Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 15 Dec 2023 13:01:35 +0100 Subject: [PATCH 09/24] WIP --- identified_vec/src/is_identifiable_vec_of.rs | 7 + identified_vec/src/is_identified_vec_via.rs | 141 +++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 identified_vec/src/is_identifiable_vec_of.rs create mode 100644 identified_vec/src/is_identified_vec_via.rs diff --git a/identified_vec/src/is_identifiable_vec_of.rs b/identified_vec/src/is_identifiable_vec_of.rs new file mode 100644 index 0000000..2790069 --- /dev/null +++ b/identified_vec/src/is_identifiable_vec_of.rs @@ -0,0 +1,7 @@ +use crate::identifiable_trait::Identifiable; +use crate::is_identifiable_vec::IsIdentifiableVec; + +pub trait IsIdentifiableVecOf: + IsIdentifiableVec +{ +} diff --git a/identified_vec/src/is_identified_vec_via.rs b/identified_vec/src/is_identified_vec_via.rs new file mode 100644 index 0000000..dbed1a3 --- /dev/null +++ b/identified_vec/src/is_identified_vec_via.rs @@ -0,0 +1,141 @@ +use crate::conflict_resolution_choice::ConflictResolutionChoice; +use crate::{Identifiable, IdentifiedVecOf, IsIdentifiableVec, IsIdentifiableVecOf}; +use std::fmt::Debug; +use std::hash::Hash; + +/// https://stackoverflow.com/a/66537661/1311272 +pub trait ViaMarker {} + +pub trait IsIdentifiableVecOfVia: IsIdentifiableVecOf + ViaMarker +where + Element: Identifiable, +{ + fn from_identified_vec_of(identified_vec_of: IdentifiedVecOf) -> Self; + fn via(&self) -> &mut IdentifiedVecOf; +} + +impl IsIdentifiableVec for U +where + U: ViaMarker, + Element: Identifiable, + U: IsIdentifiableVecOfVia, +{ + fn new_identifying_element( + id_of_element: fn(&Element) -> ::ID, + ) -> Self { + Self::from_identified_vec_of(IdentifiedVecOf::new_identifying_element(id_of_element)) + } + + fn try_from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Element) -> ::ID, + combine: fn((usize, &Element, &Element)) -> Result, + ) -> Result + where + It: IntoIterator, + { + IdentifiedVecOf::try_from_iter_select_unique_ids_with(elements, id_of_element, combine) + .map(|via| Self::from_identified_vec_of(via)) + } + + fn from_iter_select_unique_ids_with( + elements: It, + id_of_element: fn(&Element) -> ::ID, + combine: fn((usize, &Element, &Element)) -> ConflictResolutionChoice, + ) -> Self + where + It: IntoIterator, + { + Self::from_identified_vec_of(IdentifiedVecOf::from_iter_select_unique_ids_with( + elements, + id_of_element, + combine, + )) + } + + fn ids(&self) -> Vec<::ID> { + self.via().ids() + } + + fn len(&self) -> usize { + self.via().len() + } + + fn index_of_id(&self, id: &::ID) -> Option { + self.via().index_of_id(id) + } + + fn get_mut(&mut self, id: &::ID) -> Option<&mut Element> { + self.via().get_mut(id) + } + + fn elements(&self) -> Vec<&Element> { + self.via().elements() + } + + fn contains(&self, element: &Element) -> bool { + self.via().contains(element) + } + + fn contains_id(&self, id: &::ID) -> bool { + self.via().contains_id(id) + } + + fn get(&self, id: &::ID) -> Option<&Element> { + self.via().get(id) + } + + fn get_at_index(&self, index: usize) -> Option<&Element> { + self.via().get_at_index(index) + } + + fn append(&mut self, element: Element) -> (bool, usize) { + self.via().append(element) + } + + fn append_other(&mut self, other: It) + where + It: IntoIterator, + { + self.via().append_other(other) + } + + fn update_or_append(&mut self, element: Element) -> Option { + self.via().update_or_append(element) + } + + fn update_at(&mut self, element: Element, index: usize) -> Element { + self.via().update_at(element, index) + } + + fn insert(&mut self, element: Element, at: usize) -> (bool, usize) { + self.via().insert(element, at) + } + + fn update_or_insert(&mut self, element: Element, index: usize) -> (Option, usize) { + self.via().update_or_insert(element, index) + } + + ///////////// + // Remove // + ///////////// + + fn remove_by_id(&mut self, id: &::ID) -> Option { + self.via().remove_by_id(id) + } + + fn remove(&mut self, element: &Element) -> Option { + self.via().remove(element) + } + + fn remove_at(&mut self, index: usize) -> Element { + self.via().remove_at(index) + } + + fn remove_at_offsets(&mut self, offsets: It) + where + It: IntoIterator, + { + self.via().remove_at_offsets(offsets) + } +} From f6304411a7209ae14234225a684fc1f603df93cf Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 15 Dec 2023 13:05:26 +0100 Subject: [PATCH 10/24] WIP --- identified_vec/src/is_identifiable_vec.rs | 4 ++-- identified_vec/src/is_identified_vec_via.rs | 2 -- identified_vec/src/vec.rs | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/identified_vec/src/is_identifiable_vec.rs b/identified_vec/src/is_identifiable_vec.rs index 6ee0fff..a23368f 100644 --- a/identified_vec/src/is_identifiable_vec.rs +++ b/identified_vec/src/is_identifiable_vec.rs @@ -70,7 +70,7 @@ where /// /// impl Identifiable for User { /// type ID = &'static str; - /// fn id(&self) -> ID { + /// fn id(&self) -> Self::ID { /// self.id /// } /// } @@ -197,7 +197,7 @@ where /// /// impl Identifiable for User { /// type ID = &'static str; - /// fn id(&self) -> ID { + /// fn id(&self) -> Self::ID { /// self.id /// } /// } diff --git a/identified_vec/src/is_identified_vec_via.rs b/identified_vec/src/is_identified_vec_via.rs index dbed1a3..5364f15 100644 --- a/identified_vec/src/is_identified_vec_via.rs +++ b/identified_vec/src/is_identified_vec_via.rs @@ -1,7 +1,5 @@ use crate::conflict_resolution_choice::ConflictResolutionChoice; use crate::{Identifiable, IdentifiedVecOf, IsIdentifiableVec, IsIdentifiableVecOf}; -use std::fmt::Debug; -use std::hash::Hash; /// https://stackoverflow.com/a/66537661/1311272 pub trait ViaMarker {} diff --git a/identified_vec/src/vec.rs b/identified_vec/src/vec.rs index af6a604..54bf07a 100644 --- a/identified_vec/src/vec.rs +++ b/identified_vec/src/vec.rs @@ -46,7 +46,7 @@ use crate::is_identifiable_vec::IsIdentifiableVec; /// /// impl Identifiable for User { /// type ID = &'static str; -/// fn id(&self) -> I { +/// fn id(&self) -> Self::ID { /// self.id /// } /// } @@ -353,7 +353,7 @@ where /// /// impl Identifiable for User { /// type ID = &'static str; - /// fn id(&self) -> I { + /// fn id(&self) -> Self::ID { /// self.id /// } /// } @@ -551,7 +551,7 @@ where /// /// impl Identifiable for User { /// type ID = &'static str; - /// fn id(&self) -> I { + /// fn id(&self) -> Self::ID { /// self.id /// } /// } From 6a46e49a5e613a14ffcaaf87b7f8c5b8019ebd59 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 15 Dec 2023 19:32:40 +0100 Subject: [PATCH 11/24] WIP --- README.md | 17 --- identified_vec/src/is_identifiable_vec.rs | 8 -- identified_vec/src/is_identifiable_vec_of.rs | 58 +++++++++ identified_vec/src/is_identified_vec_via.rs | 49 +++++--- identified_vec/src/lib.rs | 13 -- identified_vec/src/vec.rs | 24 ---- identified_vec/src/vec_of.rs | 16 ++- identified_vec_macros/src/lib.rs | 20 ++-- tests/tests.rs | 120 +++++++++++-------- 9 files changed, 179 insertions(+), 146 deletions(-) diff --git a/README.md b/README.md index 607452f..637203b 100644 --- a/README.md +++ b/README.md @@ -133,23 +133,6 @@ assert_eq!( ); ``` -## `get_mut` - -```rust -// or mutate with `get_mut(id)` -*users.get_mut(&"u_1337").unwrap().name.get_mut() = "Yoda"; -assert_eq!( - users.elements(), - [ - User::new("u_42", "Tom Mervolo Dolder"), - User::new("u_1337", "Yoda"), - User::new("u_237", "Marie Curie"), - ] - .iter() - .collect::>() -); -``` - Or you can provide a closure that describes an element's identity: ```rust diff --git a/identified_vec/src/is_identifiable_vec.rs b/identified_vec/src/is_identifiable_vec.rs index a23368f..9d31d2c 100644 --- a/identified_vec/src/is_identifiable_vec.rs +++ b/identified_vec/src/is_identifiable_vec.rs @@ -88,14 +88,6 @@ where /// - Complexity: Expected to be O(1) on average, if `ID` implements high-quality hashing. fn index_of_id(&self, id: &ID) -> Option; - /// Returns a mutable reference to the element identified by `id` if any, else None. - /// - /// - Parameter id: The id to find in the `identified_vec`. - /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, - /// `None`. - /// - Complexity: Expected to be O(1) on average, if `ID` implements high-quality hashing. - fn get_mut(&mut self, id: &ID) -> Option<&mut Element>; - fn elements(&self) -> Vec<&Element>; /// Returns `true` if the `identified_vec` contains the `element.` diff --git a/identified_vec/src/is_identifiable_vec_of.rs b/identified_vec/src/is_identifiable_vec_of.rs index 2790069..247fe7a 100644 --- a/identified_vec/src/is_identifiable_vec_of.rs +++ b/identified_vec/src/is_identifiable_vec_of.rs @@ -1,7 +1,65 @@ use crate::identifiable_trait::Identifiable; use crate::is_identifiable_vec::IsIdentifiableVec; +use crate::ConflictResolutionChoice; pub trait IsIdentifiableVecOf: IsIdentifiableVec { + /// Constructs a new, empty `IdentifiedVec`, using `id()` on `Element` + /// as id function. + fn new() -> Self; + + fn from_iter(unique_elements: It) -> Self + where + It: IntoIterator; + + /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to + /// determine the element for any elements with duplicate ids. + /// + /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements + /// that may not have unique ids. This initializer calls the `combine` closure with the current + /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element + /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them + /// to produce a new element, or even throw an error. + /// + /// - Parameters: + /// - elements: A sequence of elements to use for the new `identified_vec`. + /// - combine: Closure trying to combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`), or `Err` if you prefer. + /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` + /// implements high-quality hashing. + fn try_from_iter_select_unique_with( + elements: I, + combine: fn((usize, &Element, &Element)) -> Result, + ) -> Result + where + I: IntoIterator, + { + Self::try_from_iter_select_unique_ids_with(elements, |e| e.id(), combine) + } + + /// Creates a new `identified_vec` from the elements in the given sequence, using a combining closure to + /// determine the element for any elements with duplicate ids. + /// + /// You use this initializer to create an `identified_vec` when you have an arbitrary sequence of elements + /// that may not have unique ids. This initializer calls the `combine` closure with the current + /// and new elements for any duplicate ids. Pass a closure as `combine` that returns the element + /// to use in the resulting `identified_vec`: The closure can choose between the two elements, combine them + /// to produce a new element, or even throw an error. + /// + /// - Parameters: + /// - elements: A sequence of elements to use for the new `identified_vec`. + /// - combine: Closure used combine elements `(index, first, last)` with duplicate ids, returning which element to use, by use of ConflictResolutionChoice (`ChooseFirst` or `ChooseLast`) + /// - Returns: A new `identified_vec` initialized with the unique elements of `elements`. + /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` + /// implements high-quality hashing. + fn from_iter_select_unique_with( + elements: I, + combine: fn((usize, &Element, &Element)) -> ConflictResolutionChoice, + ) -> Self + where + I: IntoIterator, + { + Self::from_iter_select_unique_ids_with(elements, |e| e.id(), combine) + } } diff --git a/identified_vec/src/is_identified_vec_via.rs b/identified_vec/src/is_identified_vec_via.rs index 5364f15..81f50b1 100644 --- a/identified_vec/src/is_identified_vec_via.rs +++ b/identified_vec/src/is_identified_vec_via.rs @@ -1,3 +1,7 @@ +use std::borrow::BorrowMut; +use std::cell::RefMut; +use std::ops::Deref; + use crate::conflict_resolution_choice::ConflictResolutionChoice; use crate::{Identifiable, IdentifiedVecOf, IsIdentifiableVec, IsIdentifiableVecOf}; @@ -9,7 +13,26 @@ where Element: Identifiable, { fn from_identified_vec_of(identified_vec_of: IdentifiedVecOf) -> Self; - fn via(&self) -> &mut IdentifiedVecOf; + fn via_mut(&mut self) -> &mut IdentifiedVecOf; + fn via(&self) -> &IdentifiedVecOf; +} + +impl IsIdentifiableVecOf for U +where + U: ViaMarker, + Element: Identifiable, + U: IsIdentifiableVecOfVia, +{ + fn new() -> Self { + Self::from_identified_vec_of(IdentifiedVecOf::new()) + } + + fn from_iter(unique_elements: It) -> Self + where + It: IntoIterator, + { + Self::from_identified_vec_of(IdentifiedVecOf::from_iter(unique_elements)) + } } impl IsIdentifiableVec for U @@ -63,10 +86,6 @@ where self.via().index_of_id(id) } - fn get_mut(&mut self, id: &::ID) -> Option<&mut Element> { - self.via().get_mut(id) - } - fn elements(&self) -> Vec<&Element> { self.via().elements() } @@ -88,30 +107,30 @@ where } fn append(&mut self, element: Element) -> (bool, usize) { - self.via().append(element) + self.via_mut().append(element) } fn append_other(&mut self, other: It) where It: IntoIterator, { - self.via().append_other(other) + self.via_mut().append_other(other) } fn update_or_append(&mut self, element: Element) -> Option { - self.via().update_or_append(element) + self.via_mut().update_or_append(element) } fn update_at(&mut self, element: Element, index: usize) -> Element { - self.via().update_at(element, index) + self.via_mut().update_at(element, index) } fn insert(&mut self, element: Element, at: usize) -> (bool, usize) { - self.via().insert(element, at) + self.via_mut().insert(element, at) } fn update_or_insert(&mut self, element: Element, index: usize) -> (Option, usize) { - self.via().update_or_insert(element, index) + self.via_mut().update_or_insert(element, index) } ///////////// @@ -119,21 +138,21 @@ where ///////////// fn remove_by_id(&mut self, id: &::ID) -> Option { - self.via().remove_by_id(id) + self.via_mut().remove_by_id(id) } fn remove(&mut self, element: &Element) -> Option { - self.via().remove(element) + self.via_mut().remove(element) } fn remove_at(&mut self, index: usize) -> Element { - self.via().remove_at(index) + self.via_mut().remove_at(index) } fn remove_at_offsets(&mut self, offsets: It) where It: IntoIterator, { - self.via().remove_at_offsets(offsets) + self.via_mut().remove_at_offsets(offsets) } } diff --git a/identified_vec/src/lib.rs b/identified_vec/src/lib.rs index 5fa5140..b53a130 100644 --- a/identified_vec/src/lib.rs +++ b/identified_vec/src/lib.rs @@ -104,19 +104,6 @@ //! .iter() //! .collect::>() //! ); -//! -//! // or mutate with `get_mut(id)` -//! *users.get_mut(&"u_1337").unwrap().name.get_mut() = "Yoda"; -//! assert_eq!( -//! users.elements(), -//! [ -//! User::new("u_42", "Tom Mervolo Dolder"), -//! User::new("u_1337", "Yoda"), -//! User::new("u_237", "Marie Curie"), -//! ] -//! .iter() -//! .collect::>() -//! ); //! ``` //! //! Or you can provide a closure that describes an element's identity: diff --git a/identified_vec/src/vec.rs b/identified_vec/src/vec.rs index 54bf07a..e171c2f 100644 --- a/identified_vec/src/vec.rs +++ b/identified_vec/src/vec.rs @@ -115,19 +115,6 @@ use crate::is_identifiable_vec::IsIdentifiableVec; /// .iter() /// .collect::>() /// ); -/// -/// // or mutate with `get_mut(id)` -/// *users.get_mut(&"u_1337").unwrap().name.get_mut() = "Yoda"; -/// assert_eq!( -/// users.elements(), -/// [ -/// User::new("u_42", "Tom Mervolo Dolder"), -/// User::new("u_1337", "Yoda"), -/// User::new("u_237", "Marie Curie"), -/// ] -/// .iter() -/// .collect::>() -/// ); /// ``` /// /// Or you can provide a closure that describes an element's identity: @@ -374,17 +361,6 @@ where self.order.iter().position(|i| i == id) } - /// Returns a mutable reference to the element identified by `id` if any, else None. - /// - /// - Parameter id: The id to find in the `identified_vec`. - /// - Returns: The mutable reference to the element identified by `id` if found in the `identified_vec`; otherwise, - /// `None`. - /// - Complexity: Expected to be O(1) on average, if `I` implements high-quality hashing. - #[inline] - fn get_mut(&mut self, id: &I) -> Option<&mut E> { - self.elements.get_mut(id) - } - //////////////////// // Public Get // //////////////////// diff --git a/identified_vec/src/vec_of.rs b/identified_vec/src/vec_of.rs index 78b4a63..a28b2aa 100644 --- a/identified_vec/src/vec_of.rs +++ b/identified_vec/src/vec_of.rs @@ -5,6 +5,7 @@ use std::fmt::Debug; #[cfg(feature = "serde")] use crate::serde_error::IdentifiedVecOfSerdeFailure; +use crate::IsIdentifiableVecOf; use crate::{ conflict_resolution_choice::ConflictResolutionChoice, is_identifiable_vec::IsIdentifiableVec, }; @@ -27,16 +28,13 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; /// `Element` implements serde serialization/deserialization of course. pub type IdentifiedVecOf = IdentifiedVec<::ID, Element>; -////////////////////////////////////////////// -/// Identifiable Element Constructors /// -////////////////////////////////////////////// -impl IdentifiedVec +impl IsIdentifiableVecOf for IdentifiedVecOf where Element: Identifiable, { /// Constructs a new, empty `IdentifiedVec`, using `id()` on `Element` /// as id function. - pub fn new() -> Self { + fn new() -> Self { Self { order: Vec::new(), elements: HashMap::new(), @@ -56,9 +54,9 @@ where /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` /// implements high-quality hashing. #[inline] - pub fn from_iter(unique_elements: I) -> Self + fn from_iter(unique_elements: It) -> Self where - I: IntoIterator, + It: IntoIterator, { let mut _self = Self::new(); unique_elements @@ -83,7 +81,7 @@ where /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` /// implements high-quality hashing. #[inline] - pub fn try_from_iter_select_unique_with( + fn try_from_iter_select_unique_with( elements: I, combine: fn((usize, &Element, &Element)) -> Result, ) -> Result @@ -109,7 +107,7 @@ where /// - Complexity: Expected O(*n*) on average, where *n* is the count of elements, if `ID` /// implements high-quality hashing. #[inline] - pub fn from_iter_select_unique_with( + fn from_iter_select_unique_with( elements: I, combine: fn((usize, &Element, &Element)) -> ConflictResolutionChoice, ) -> Self diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index 0e2043c..a038582 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -1,14 +1,20 @@ #[macro_export] macro_rules! newtype_identified_vec { (of: $item_ty: ty, named: $struct_name: ident) => { + use std::cell::RefMut; + // pub struct $struct_name<'a>(RefMut<'a, IdentifiedVecOf<$item_ty>>); pub struct $struct_name(IdentifiedVecOf<$item_ty>); - impl $struct_name { - /// Constructs a new, empty `IdentifiedVec`, using `id()` on `Element` - /// as id function. - pub fn new() -> Self { - Self(IdentifiedVecOf::new()) - } - } + // impl<'a> ViaMarker for $struct_name<'a> {} + // impl<'a> IsIdentifiableVecOf<$item_ty> for $struct_name<'a> {} + // impl<'a> IsIdentifiableVecOfVia<$item_ty> for $struct_name<'a> { + // fn via_mut(&self) -> RefMut<'a, IdentifiedVecOf<$item_ty>> { + // // &self.0 + // todo!() + // } + // fn from_identified_vec_of(identified_vec_of: IdentifiedVecOf<$item_ty>) -> Self { + // Self(RefMut::new(identified_vec_of)) + // } + // } }; } diff --git a/tests/tests.rs b/tests/tests.rs index 294471a..9731f29 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,4 +1,9 @@ -use std::{cell::RefCell, collections::HashSet, fmt::Debug, ops::Deref}; +use std::{ + cell::{Ref, RefCell, RefMut}, + collections::HashSet, + fmt::Debug, + ops::Deref, +}; use identified_vec::{ ConflictResolutionChoice, Identifiable, IdentifiedVec, IdentifiedVecOf, @@ -111,37 +116,37 @@ fn get() { assert_eq!(identified_vec.get(&3), Some(&User::blob_sr())); // 1 - let mut id: &u16 = &1; - identified_vec - .get_mut(id) - .unwrap() - .name - .borrow_mut() - .push_str(", Esq."); - assert_eq!( - identified_vec.get(id), - Some(&User::new(id.clone(), "Blob, Esq.")) - ); - - // 2 - id = &2; - identified_vec - .get_mut(id) - .unwrap() - .name - .borrow_mut() - .drain(4..9); - assert_eq!(identified_vec.get(id), Some(&User::new(id.clone(), "Blob"))); - - // 3 - id = &3; - identified_vec - .get_mut(id) - .unwrap() - .name - .borrow_mut() - .drain(4..9); - assert_eq!(identified_vec.get(id), Some(&User::new(id.clone(), "Blob"))); + let id: &u16 = &1; + // identified_vec + // .get_mut(id) + // .unwrap() + // .name + // .borrow_mut() + // .push_str(", Esq."); + // assert_eq!( + // identified_vec.get(id), + // Some(&User::new(id.clone(), "Blob, Esq.")) + // ); + + // // 2 + // id = &2; + // identified_vec + // .get_mut(id) + // .unwrap() + // .name + // .borrow_mut() + // .drain(4..9); + // assert_eq!(identified_vec.get(id), Some(&User::new(id.clone(), "Blob"))); + + // // 3 + // id = &3; + // identified_vec + // .get_mut(id) + // .unwrap() + // .name + // .borrow_mut() + // .drain(4..9); + // assert_eq!(identified_vec.get(id), Some(&User::new(id.clone(), "Blob"))); identified_vec.remove_by_id(id); assert_eq!(identified_vec.get(id), None); @@ -537,29 +542,38 @@ fn hash() { ) } -// Test IsIdentifiedVecVia -struct CollectionOfUsersVia(IdentifiedVecOf); -impl ViaMarker for CollectionOfUsersVia {} -impl IsIdentifiableVecOf for CollectionOfUsersVia {} -impl IsIdentifiableVecOfVia for CollectionOfUsersVia { - fn via(&self) -> &mut IdentifiedVecOf { - todo!() +#[test] +fn isid() { + struct CollectionOfUsersVia(IdentifiedVecOf); + impl ViaMarker for CollectionOfUsersVia {} + impl IsIdentifiableVecOfVia for CollectionOfUsersVia { + fn via_mut(&mut self) -> &mut IdentifiedVecOf { + &mut self.0 + } + fn via(&self) -> &IdentifiedVecOf { + &self.0 + } + fn from_identified_vec_of(identified_vec_of: IdentifiedVecOf) -> Self { + Self(identified_vec_of) + } } - fn from_identified_vec_of(identified_vec_of: IdentifiedVecOf) -> Self { - Self(identified_vec_of) + + impl CollectionOfUsersVia { + #[inline] + pub fn items(&self) -> Vec { + self.0.items() + } } + + let mut sut = CollectionOfUsersVia::new(); + sut.append(User::blob_jr()); + assert_eq!(sut.items(), [User::blob_jr()]) } -// impl IsIdentifiableVecOfVia for CollectionOfUsersVia { -// fn via(&self) -> &mut IdentifiedVecOf { -// todo!() -// } +// #[test] +// fn test_macro() { +// newtype_identified_vec!(of: User, named: CollectionOfUsers); +// let mut users = CollectionOfUsers::new(); +// users.0.append(User::blob()); +// assert_eq!(users.0.items(), [User::blob()]) // } - -#[test] -fn test_macro() { - newtype_identified_vec!(of: User, named: CollectionOfUsers); - let mut users = CollectionOfUsers::new(); - users.0.append(User::blob()); - assert_eq!(users.0.items(), [User::blob()]) -} From 0957a3b4569b702cf4104728d18cd871b6f6a8a6 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 15 Dec 2023 19:36:05 +0100 Subject: [PATCH 12/24] WIP --- tests/tests.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/tests.rs b/tests/tests.rs index 9731f29..857bb11 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -567,7 +567,14 @@ fn isid() { let mut sut = CollectionOfUsersVia::new(); sut.append(User::blob_jr()); - assert_eq!(sut.items(), [User::blob_jr()]) + assert_eq!(sut.items(), [User::blob_jr()]); + sut.remove_at(0); + assert_eq!(sut.len(), 0); + sut.update_or_append(User::blob_sr()); + sut.update_or_append(User::blob_sr()); + assert_eq!(sut.items(), [User::blob_sr()]); + sut.update_or_append(User::blob_jr()); + assert_eq!(sut.items(), [User::blob_sr(), User::blob_jr()]); } // #[test] From 4843b0fd3fd23fdbe0e768ac483fdb9edb2d6347 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 15 Dec 2023 19:38:58 +0100 Subject: [PATCH 13/24] WIP --- identified_vec_macros/src/lib.rs | 34 ++++++++++++++++++++------------ tests/tests.rs | 17 +++++++++++++++- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index a038582..56a6915 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -1,20 +1,28 @@ #[macro_export] macro_rules! newtype_identified_vec { (of: $item_ty: ty, named: $struct_name: ident) => { - use std::cell::RefMut; - // pub struct $struct_name<'a>(RefMut<'a, IdentifiedVecOf<$item_ty>>); pub struct $struct_name(IdentifiedVecOf<$item_ty>); - // impl<'a> ViaMarker for $struct_name<'a> {} - // impl<'a> IsIdentifiableVecOf<$item_ty> for $struct_name<'a> {} - // impl<'a> IsIdentifiableVecOfVia<$item_ty> for $struct_name<'a> { - // fn via_mut(&self) -> RefMut<'a, IdentifiedVecOf<$item_ty>> { - // // &self.0 - // todo!() - // } - // fn from_identified_vec_of(identified_vec_of: IdentifiedVecOf<$item_ty>) -> Self { - // Self(RefMut::new(identified_vec_of)) - // } - // } + impl ViaMarker for $struct_name {} + impl IsIdentifiableVecOfVia<$item_ty> for $struct_name { + fn via_mut(&mut self) -> &mut IdentifiedVecOf<$item_ty> { + &mut self.0 + } + + fn via(&self) -> &IdentifiedVecOf<$item_ty> { + &self.0 + } + + fn from_identified_vec_of(identified_vec_of: IdentifiedVecOf<$item_ty>) -> Self { + Self(identified_vec_of) + } + } + + impl $struct_name { + #[inline] + pub fn items(&self) -> Vec<$item_ty> { + self.0.items() + } + } }; } diff --git a/tests/tests.rs b/tests/tests.rs index 857bb11..1b11a15 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -577,9 +577,24 @@ fn isid() { assert_eq!(sut.items(), [User::blob_sr(), User::blob_jr()]); } +#[test] +fn test_macro() { + newtype_identified_vec!(of: User, named: CollectionOfUsers); + + let mut sut = CollectionOfUsers::new(); + sut.append(User::blob_jr()); + assert_eq!(sut.items(), [User::blob_jr()]); + sut.remove_at(0); + assert_eq!(sut.len(), 0); + sut.update_or_append(User::blob_sr()); + sut.update_or_append(User::blob_sr()); + assert_eq!(sut.items(), [User::blob_sr()]); + sut.update_or_append(User::blob_jr()); + assert_eq!(sut.items(), [User::blob_sr(), User::blob_jr()]); +} + // #[test] // fn test_macro() { -// newtype_identified_vec!(of: User, named: CollectionOfUsers); // let mut users = CollectionOfUsers::new(); // users.0.append(User::blob()); // assert_eq!(users.0.items(), [User::blob()]) From bdff6911776297737fc9e3fac4231c3d2c412717 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 15 Dec 2023 20:02:09 +0100 Subject: [PATCH 14/24] WIP --- identified_vec/src/is_identifiable_vec.rs | 4 +- identified_vec/src/is_identified_vec_via.rs | 4 -- identified_vec/src/lib.rs | 2 +- identified_vec/src/vec.rs | 8 ++-- tests/tests.rs | 41 ++++++++------------- 5 files changed, 22 insertions(+), 37 deletions(-) diff --git a/identified_vec/src/is_identifiable_vec.rs b/identified_vec/src/is_identifiable_vec.rs index 9d31d2c..4ab6957 100644 --- a/identified_vec/src/is_identifiable_vec.rs +++ b/identified_vec/src/is_identifiable_vec.rs @@ -55,7 +55,7 @@ where /// /// ``` /// extern crate identified_vec; - /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// use identified_vec::{IsIdentifiableVec, IsIdentifiableVecOf, IdentifiedVec, Identifiable, IdentifiedVecOf}; /// /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] /// struct User { @@ -174,7 +174,7 @@ where /// /// ``` /// extern crate identified_vec; - /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// use identified_vec::{IsIdentifiableVec, IsIdentifiableVecOf, IdentifiedVec, Identifiable, IdentifiedVecOf}; /// /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] /// struct User { diff --git a/identified_vec/src/is_identified_vec_via.rs b/identified_vec/src/is_identified_vec_via.rs index 81f50b1..1e4238d 100644 --- a/identified_vec/src/is_identified_vec_via.rs +++ b/identified_vec/src/is_identified_vec_via.rs @@ -1,7 +1,3 @@ -use std::borrow::BorrowMut; -use std::cell::RefMut; -use std::ops::Deref; - use crate::conflict_resolution_choice::ConflictResolutionChoice; use crate::{Identifiable, IdentifiedVecOf, IsIdentifiableVec, IsIdentifiableVecOf}; diff --git a/identified_vec/src/lib.rs b/identified_vec/src/lib.rs index b53a130..04b1485 100644 --- a/identified_vec/src/lib.rs +++ b/identified_vec/src/lib.rs @@ -10,7 +10,7 @@ //! //! ``` //! extern crate identified_vec; -//! use identified_vec::{IsIdentifiableVec, IdentifiedVec, IdentifiedVecOf, Identifiable}; +//! use identified_vec::{IsIdentifiableVec, IsIdentifiableVecOf, IdentifiedVec, IdentifiedVecOf, Identifiable}; //! use std::cell::RefCell; //! //! #[derive(Eq, PartialEq, Clone, Debug)] diff --git a/identified_vec/src/vec.rs b/identified_vec/src/vec.rs index e171c2f..a730536 100644 --- a/identified_vec/src/vec.rs +++ b/identified_vec/src/vec.rs @@ -23,7 +23,7 @@ use crate::is_identifiable_vec::IsIdentifiableVec; /// /// ``` /// extern crate identified_vec; -/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf, IsIdentifiableVec}; +/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf, IsIdentifiableVec, IsIdentifiableVecOf}; /// use std::cell::RefCell; /// /// #[derive(Eq, PartialEq, Clone, Debug)] @@ -121,7 +121,7 @@ use crate::is_identifiable_vec::IsIdentifiableVec; /// /// ``` /// /// extern crate identified_vec; -/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf, IsIdentifiableVec}; +/// use identified_vec::{IdentifiedVec, Identifiable, IdentifiedVecOf, IsIdentifiableVec, IsIdentifiableVecOf}; /// /// let numbers = IdentifiedVec::::new_identifying_element(|e| *e); /// ``` @@ -325,7 +325,7 @@ where /// /// ``` /// extern crate identified_vec; - /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// use identified_vec::{IsIdentifiableVec, IsIdentifiableVecOf, IdentifiedVec, Identifiable, IdentifiedVecOf}; /// /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] /// struct User { @@ -512,7 +512,7 @@ where /// /// ``` /// extern crate identified_vec; - /// use identified_vec::{IsIdentifiableVec, IdentifiedVec, Identifiable, IdentifiedVecOf}; + /// use identified_vec::{IsIdentifiableVec, IsIdentifiableVecOf, IdentifiedVec, Identifiable, IdentifiedVecOf}; /// /// #[derive(Eq, PartialEq, Clone, Debug, Hash)] /// struct User { diff --git a/tests/tests.rs b/tests/tests.rs index 1b11a15..f88302a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,9 +1,4 @@ -use std::{ - cell::{Ref, RefCell, RefMut}, - collections::HashSet, - fmt::Debug, - ops::Deref, -}; +use std::{cell::RefCell, collections::HashSet, fmt::Debug, ops::Deref}; use identified_vec::{ ConflictResolutionChoice, Identifiable, IdentifiedVec, IdentifiedVecOf, @@ -127,6 +122,7 @@ fn get() { // identified_vec.get(id), // Some(&User::new(id.clone(), "Blob, Esq.")) // ); + // identified_vec.upd // // 2 // id = &2; @@ -148,19 +144,19 @@ fn get() { // .drain(4..9); // assert_eq!(identified_vec.get(id), Some(&User::new(id.clone(), "Blob"))); - identified_vec.remove_by_id(id); - assert_eq!(identified_vec.get(id), None); - identified_vec.append(User::new(4, "Blob, Sr.")); - assert_eq!( - identified_vec.elements(), - [ - User::new(1, "Blob, Esq."), - User::new(2, "Blob"), - User::new(4, "Blob, Sr."), - ] - .iter() - .collect::>() - ); + // identified_vec.remove_by_id(id); + // assert_eq!(identified_vec.get(id), None); + // identified_vec.append(User::new(4, "Blob, Sr.")); + // assert_eq!( + // identified_vec.elements(), + // [ + // User::new(1, "Blob, Esq."), + // User::new(2, "Blob"), + // User::new(4, "Blob, Sr."), + // ] + // .iter() + // .collect::>() + // ); } #[test] @@ -592,10 +588,3 @@ fn test_macro() { sut.update_or_append(User::blob_jr()); assert_eq!(sut.items(), [User::blob_sr(), User::blob_jr()]); } - -// #[test] -// fn test_macro() { -// let mut users = CollectionOfUsers::new(); -// users.0.append(User::blob()); -// assert_eq!(users.0.items(), [User::blob()]) -// } From 71f1659e93cc483d055132e59401c24b931d133a Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 15 Dec 2023 20:36:43 +0100 Subject: [PATCH 15/24] WIP --- tests/tests.rs | 77 ++++++++++++++++++++------------------------------ 1 file changed, 31 insertions(+), 46 deletions(-) diff --git a/tests/tests.rs b/tests/tests.rs index 6025c6a..e7ba4c9 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -111,52 +111,37 @@ fn get() { assert_eq!(identified_vec.get(&3), Some(&User::blob_sr())); // 1 - let id: &u16 = &1; - // identified_vec - // .get_mut(id) - // .unwrap() - // .name - // .borrow_mut() - // .push_str(", Esq."); - // assert_eq!( - // identified_vec.get(id), - // Some(&User::new(id.clone(), "Blob, Esq.")) - // ); - // identified_vec.upd - - // // 2 - // id = &2; - // identified_vec - // .get_mut(id) - // .unwrap() - // .name - // .borrow_mut() - // .drain(4..9); - // assert_eq!(identified_vec.get(id), Some(&User::new(id.clone(), "Blob"))); - - // // 3 - // id = &3; - // identified_vec - // .get_mut(id) - // .unwrap() - // .name - // .borrow_mut() - // .drain(4..9); - // assert_eq!(identified_vec.get(id), Some(&User::new(id.clone(), "Blob"))); - - // identified_vec.remove_by_id(id); - // assert_eq!(identified_vec.get(id), None); - // identified_vec.append(User::new(4, "Blob, Sr.")); - // assert_eq!( - // identified_vec.elements(), - // [ - // User::new(1, "Blob, Esq."), - // User::new(2, "Blob"), - // User::new(4, "Blob, Sr."), - // ] - // .iter() - // .collect::>() - // ); + let mut id: &u16 = &1; + identified_vec.update_with(id, |u| u.name.borrow_mut().push_str(", Esq.")); + + assert_eq!( + identified_vec.get(id), + Some(&User::new(id.clone(), "Blob, Esq.")) + ); + + // 2 + id = &2; + identified_vec.update_with(id, |u| _ = u.name.borrow_mut().drain(4..9)); + assert_eq!(identified_vec.get(id), Some(&User::new(id.clone(), "Blob"))); + + // 3 + id = &3; + identified_vec.update_with(id, |u| _ = u.name.borrow_mut().drain(4..9)); + assert_eq!(identified_vec.get(id), Some(&User::new(id.clone(), "Blob"))); + + identified_vec.remove_by_id(id); + assert_eq!(identified_vec.get(id), None); + identified_vec.append(User::new(4, "Blob, Sr.")); + assert_eq!( + identified_vec.elements(), + [ + User::new(1, "Blob, Esq."), + User::new(2, "Blob"), + User::new(4, "Blob, Sr."), + ] + .iter() + .collect::>() + ); } #[test] From f9da7dc59f1cbd7c9607373044e81859fa0b317f Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Fri, 15 Dec 2023 20:50:24 +0100 Subject: [PATCH 16/24] WIP --- identified_vec/src/is_identified_vec_via.rs | 15 ++++++++++++++- identified_vec/src/lib.rs | 1 - identified_vec/src/vec.rs | 11 +++++++++-- identified_vec_macros/src/lib.rs | 7 ------- tests/tests.rs | 2 +- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/identified_vec/src/is_identified_vec_via.rs b/identified_vec/src/is_identified_vec_via.rs index 7383c83..fea11b6 100644 --- a/identified_vec/src/is_identified_vec_via.rs +++ b/identified_vec/src/is_identified_vec_via.rs @@ -1,5 +1,7 @@ use crate::conflict_resolution_choice::ConflictResolutionChoice; -use crate::{Error, Identifiable, IdentifiedVecOf, IsIdentifiableVec, IsIdentifiableVecOf}; +use crate::{ + Error, Identifiable, IdentifiedVecOf, IsIdentifiableVec, IsIdentifiableVecOf, ItemsCloned, +}; /// https://stackoverflow.com/a/66537661/1311272 pub trait ViaMarker {} @@ -33,6 +35,17 @@ where } } +impl ItemsCloned for U +where + U: ViaMarker, + Element: Identifiable + Clone, + U: IsIdentifiableVecOfVia, +{ + fn items(&self) -> Vec { + self.via().items() + } +} + impl IsIdentifiableVec for U where U: ViaMarker, diff --git a/identified_vec/src/lib.rs b/identified_vec/src/lib.rs index 91ba1ad..aa673c4 100644 --- a/identified_vec/src/lib.rs +++ b/identified_vec/src/lib.rs @@ -116,7 +116,6 @@ //! let numbers = IdentifiedVec::::new_identifying_element(|e| *e); //! ``` -mod Is_identifiable_vec; mod conflict_resolution_choice; mod errors; mod identifiable_trait; diff --git a/identified_vec/src/vec.rs b/identified_vec/src/vec.rs index b49566e..5af645e 100644 --- a/identified_vec/src/vec.rs +++ b/identified_vec/src/vec.rs @@ -646,7 +646,14 @@ where } } -impl IdentifiedVec +pub trait ItemsCloned +where + Element: Clone, +{ + fn items(&self) -> Vec; +} + +impl ItemsCloned for IdentifiedVec where E: Clone, I: Eq + Hash + Clone + Debug, @@ -659,7 +666,7 @@ where /// /// - Complexity: O(n) #[inline] - pub fn items(&self) -> Vec { + fn items(&self) -> Vec { self.iter().map(|e| e.clone()).collect() } } diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index 56a6915..350e8e6 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -17,12 +17,5 @@ macro_rules! newtype_identified_vec { Self(identified_vec_of) } } - - impl $struct_name { - #[inline] - pub fn items(&self) -> Vec<$item_ty> { - self.0.items() - } - } }; } diff --git a/tests/tests.rs b/tests/tests.rs index e7ba4c9..defbc62 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -3,7 +3,7 @@ use std::{cell::RefCell, collections::HashSet, fmt::Debug, ops::Deref}; use identified_vec::{ ConflictResolutionChoice, Error, Identifiable, IdentifiedVec, IdentifiedVecOf, IdentifiedVecOfSerdeFailure, IsIdentifiableVec, IsIdentifiableVecOf, IsIdentifiableVecOfVia, - ViaMarker, + ItemsCloned, ViaMarker, }; use identified_vec_macros::newtype_identified_vec; From c03d872ebd4d1b61ec0ee5f4cb0467e927320648 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 16 Dec 2023 12:15:00 +0100 Subject: [PATCH 17/24] WIP --- identified_vec/src/is_identifiable_vec.rs | 13 +++ identified_vec/src/is_identified_vec_via.rs | 17 ++- identified_vec/src/lib.rs | 4 + identified_vec/src/vec.rs | 115 +++++--------------- identified_vec_macros/src/lib.rs | 29 +++++ tests/tests.rs | 18 +-- 6 files changed, 98 insertions(+), 98 deletions(-) diff --git a/identified_vec/src/is_identifiable_vec.rs b/identified_vec/src/is_identifiable_vec.rs index 814ed5b..04a5a9c 100644 --- a/identified_vec/src/is_identifiable_vec.rs +++ b/identified_vec/src/is_identifiable_vec.rs @@ -1,5 +1,6 @@ use crate::conflict_resolution_choice::ConflictResolutionChoice; use crate::Error; +use crate::IdentifiedVecIterator; use std::fmt::Debug; use std::hash::Hash; @@ -250,4 +251,16 @@ where fn remove_at_offsets(&mut self, offsets: It) where It: IntoIterator; + + /// Try append a new member to the end of the `identified_vec`, if the `identified_vec` already contains the element a Error will be returned. + /// + /// - Parameter item: The element to add to the `identified_vec`. + /// - Returns: Either a Ok() with a pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether + /// the operation added a new element, and `index` is the index of `item` in the resulting + /// `identified_vec`. If the given ID pre-exists within the collection the function call returns `Error::ElementWithSameIDFound`. + /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on + /// the `ID` type, if it implements high-quality hashing. + fn try_append_new(&mut self, element: Element) -> Result<(bool, usize), Error>; + + fn iter(&self) -> IdentifiedVecIterator; } diff --git a/identified_vec/src/is_identified_vec_via.rs b/identified_vec/src/is_identified_vec_via.rs index fea11b6..9ada762 100644 --- a/identified_vec/src/is_identified_vec_via.rs +++ b/identified_vec/src/is_identified_vec_via.rs @@ -1,12 +1,15 @@ use crate::conflict_resolution_choice::ConflictResolutionChoice; +use crate::vec::ItemsCloned; use crate::{ - Error, Identifiable, IdentifiedVecOf, IsIdentifiableVec, IsIdentifiableVecOf, ItemsCloned, + Error, Identifiable, IdentifiedVecIterator, IdentifiedVecOf, IsIdentifiableVec, + IsIdentifiableVecOf, }; /// https://stackoverflow.com/a/66537661/1311272 pub trait ViaMarker {} -pub trait IsIdentifiableVecOfVia: IsIdentifiableVecOf + ViaMarker +pub trait IsIdentifiableVecOfVia: + IsIdentifiableVecOf + IntoIterator + ViaMarker where Element: Identifiable, { @@ -174,6 +177,11 @@ where self.via_mut().update_with(id, mutate) } + #[inline] + fn try_append_new(&mut self, element: Element) -> Result<(bool, usize), Error> { + self.via_mut().try_append_new(element) + } + ///////////// // Remove // ///////////// @@ -199,4 +207,9 @@ where { self.via_mut().remove_at_offsets(offsets) } + + #[inline] + fn iter(&self) -> IdentifiedVecIterator<::ID, Element> { + self.via().iter() + } } diff --git a/identified_vec/src/lib.rs b/identified_vec/src/lib.rs index aa673c4..6dd5419 100644 --- a/identified_vec/src/lib.rs +++ b/identified_vec/src/lib.rs @@ -119,6 +119,8 @@ mod conflict_resolution_choice; mod errors; mod identifiable_trait; +mod identified_vec_into_iterator; +mod identified_vec_iterator; mod is_identifiable_vec; mod is_identifiable_vec_of; mod is_identified_vec_via; @@ -129,6 +131,8 @@ mod vec_of; pub mod identified_vec { //! A collection of unique identifiable elements which retains **insertion** order. pub use crate::conflict_resolution_choice::*; + pub use crate::identified_vec_into_iterator::*; + pub use crate::identified_vec_iterator::*; pub use crate::is_identifiable_vec::*; pub use crate::vec::*; } diff --git a/identified_vec/src/vec.rs b/identified_vec/src/vec.rs index 5af645e..7d80c9b 100644 --- a/identified_vec/src/vec.rs +++ b/identified_vec/src/vec.rs @@ -1,5 +1,7 @@ use crate::conflict_resolution_choice::ConflictResolutionChoice; use crate::errors::Error; +use crate::identified_vec_into_iterator::IdentifiedVecIntoIterator; +use crate::identified_vec_iterator::IdentifiedVecIterator; use std::collections::HashMap; use std::fmt::{Debug, Display}; use std::hash::{Hash, Hasher}; @@ -644,6 +646,30 @@ where internal_offset += 1; }) } + + /// Try append a new member to the end of the `identified_vec`, if the `identified_vec` already contains the element a Error will be returned. + /// + /// - Parameter item: The element to add to the `identified_vec`. + /// - Returns: Either a Ok() with a pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether + /// the operation added a new element, and `index` is the index of `item` in the resulting + /// `identified_vec`. If the given ID pre-exists within the collection the function call returns `Error::ElementWithSameIDFound`. + /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on + /// the `ID` type, if it implements high-quality hashing. + #[inline] + fn try_append_new(&mut self, element: E) -> Result<(bool, usize), Error> { + let id = self.id(&element); + + if self.contains_id(&id) { + return Err(Error::ElementWithSameIDFound(format!("{:#?}", id))); + } + + Ok(self.append(element)) + } + + #[inline] + fn iter(&self) -> IdentifiedVecIterator { + IdentifiedVecIterator::new(self) + } } pub trait ItemsCloned @@ -671,67 +697,6 @@ where } } -/// An iterator over the items of an `IdentifiedVec`. -pub struct IdentifiedVecIterator<'a, I, E> -where - I: Eq + Hash + Clone + Debug, -{ - identified_vec: &'a IdentifiedVec, - index: usize, -} - -impl<'a, I, E> Iterator for IdentifiedVecIterator<'a, I, E> -where - I: Eq + Hash + Clone + Debug, -{ - type Item = &'a E; - - fn next(&mut self) -> Option { - if self.index < self.identified_vec.len() { - let id = Some(&self.identified_vec.order[self.index]).unwrap(); - self.index += 1; - return self.identified_vec.get(id); - } else { - None - } - } -} - -impl IdentifiedVec -where - I: Eq + Hash + Clone + Debug, -{ - pub fn iter(&self) -> IdentifiedVecIterator { - IdentifiedVecIterator { - identified_vec: self, - index: 0, - } - } -} - -/// An owning iterator over the items of an `IdentifiedVec`. -pub struct IdentifiedVecIntoIterator -where - I: Eq + Hash + Clone + Debug, -{ - identified_vec: IdentifiedVec, -} - -impl Iterator for IdentifiedVecIntoIterator -where - I: Eq + Hash + Clone + Debug, -{ - type Item = E; - - fn next(&mut self) -> Option { - if self.identified_vec.len() == 0 { - return None; - } - let result = self.identified_vec.remove_at(0); - Some(result) - } -} - impl IntoIterator for IdentifiedVec where I: Eq + Hash + Clone + Debug, @@ -740,9 +705,7 @@ where type IntoIter = IdentifiedVecIntoIterator; fn into_iter(self) -> Self::IntoIter { - Self::IntoIter { - identified_vec: self, - } + Self::IntoIter::new(self) } } @@ -912,27 +875,3 @@ where Ok(self.append(element)) } } - -impl IdentifiedVec -where - ID: Eq + Hash + Clone + Debug, -{ - /// Try append a new member to the end of the `identified_vec`, if the `identified_vec` already contains the element a Error will be returned. - /// - /// - Parameter item: The element to add to the `identified_vec`. - /// - Returns: Either a Ok() with a pair `(inserted, index)`, where `inserted` is a Boolean value indicating whether - /// the operation added a new element, and `index` is the index of `item` in the resulting - /// `identified_vec`. If the given ID pre-exists within the collection the function call returns `Error::ElementWithSameIDFound`. - /// - Complexity: The operation is expected to perform O(1) copy, hash, and compare operations on - /// the `ID` type, if it implements high-quality hashing. - #[inline] - pub fn try_append_new(&mut self, element: Element) -> Result<(bool, usize), Error> { - let id = self.id(&element); - - if self.contains_id(&id) { - return Err(Error::ElementWithSameIDFound(format!("{:#?}", id))); - } - - Ok(self.append(element)) - } -} diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index 350e8e6..60a06a5 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -1,6 +1,8 @@ #[macro_export] macro_rules! newtype_identified_vec { (of: $item_ty: ty, named: $struct_name: ident) => { + use identified_vec::IdentifiedVecIntoIterator; + pub struct $struct_name(IdentifiedVecOf<$item_ty>); impl ViaMarker for $struct_name {} @@ -17,5 +19,32 @@ macro_rules! newtype_identified_vec { Self(identified_vec_of) } } + + impl IntoIterator for $struct_name { + type Item = $item_ty; + type IntoIter = IdentifiedVecIntoIterator<<$item_ty as Identifiable>::ID, $item_ty>; + + fn into_iter(self) -> Self::IntoIter { + Self::IntoIter::new(self.0) + } + } + + // impl $struct_name + // where + // I: Eq + Hash + Clone + Debug, + // { + // pub fn iter(&self) -> IdentifiedVecIterator { + // IdentifiedVecIterator::new(self) + // } + // } + + // impl IntoIterator for $struct_name { + // type Item = $item_ty; + // type IntoIter = IdentifiedVecIntoIterator<<$item_ty as Identifiable>::ID, $item_ty>; + + // fn into_iter(self) -> Self::IntoIter { + // Self::IntoIter::new(self) + // } + // } }; } diff --git a/tests/tests.rs b/tests/tests.rs index defbc62..eeedaac 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -53,7 +53,7 @@ impl Identifiable for User { } type SUT = IdentifiedVecOf; -type Users = IdentifiedVecOf; +newtype_identified_vec!(of: User, named: Users); #[test] fn new_is_empty() { @@ -334,7 +334,7 @@ fn try_append_new_unique_element() { assert_eq!(result.unwrap().1, 3); assert_eq!(identified_vec.items(), [1, 2, 3, 4]); - let mut identified_vec: Users = IdentifiedVecOf::new(); + let mut identified_vec = Users::new(); identified_vec.append(User::blob()); identified_vec.append(User::blob_jr()); identified_vec.append(User::blob_sr()); @@ -354,7 +354,7 @@ fn try_append_new_unique_element() { #[test] fn try_append_element_with_existing_id() { - let mut identified_vec: Users = IdentifiedVecOf::new(); + let mut identified_vec = Users::new(); identified_vec.append(User::blob()); identified_vec.append(User::blob_jr()); identified_vec.append(User::blob_sr()); @@ -442,7 +442,7 @@ fn try_update() { ); assert_eq!(identified_vec.items(), [1, 2, 3]); - let mut identified_vec: Users = IdentifiedVecOf::new(); + let mut identified_vec = Users::new(); identified_vec.append(User::blob()); identified_vec.append(User::blob_jr()); identified_vec.append(User::blob_sr()); @@ -634,11 +634,13 @@ fn isid() { Self(identified_vec_of) } } + impl IntoIterator for CollectionOfUsersVia { + type Item = User; - impl CollectionOfUsersVia { - #[inline] - pub fn items(&self) -> Vec { - self.0.items() + type IntoIter = IdentifiedVecIntoIterator<::ID, User>; + + fn into_iter(self) -> Self::IntoIter { + todo!() } } From 659aefbee1950104fe43c9c860bca5f3b82f712a Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 16 Dec 2023 13:16:28 +0100 Subject: [PATCH 18/24] WIP --- identified_vec_macros/src/lib.rs | 50 +++++++++++++++++++++----------- tests/Cargo.toml | 4 +-- tests/tests.rs | 35 ++++++++++++++++++++-- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index 60a06a5..fcf72cb 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -3,6 +3,7 @@ macro_rules! newtype_identified_vec { (of: $item_ty: ty, named: $struct_name: ident) => { use identified_vec::IdentifiedVecIntoIterator; + #[derive(Debug, Clone, Eq, PartialEq)] pub struct $struct_name(IdentifiedVecOf<$item_ty>); impl ViaMarker for $struct_name {} @@ -29,22 +30,37 @@ macro_rules! newtype_identified_vec { } } - // impl $struct_name - // where - // I: Eq + Hash + Clone + Debug, - // { - // pub fn iter(&self) -> IdentifiedVecIterator { - // IdentifiedVecIterator::new(self) - // } - // } - - // impl IntoIterator for $struct_name { - // type Item = $item_ty; - // type IntoIter = IdentifiedVecIntoIterator<<$item_ty as Identifiable>::ID, $item_ty>; - - // fn into_iter(self) -> Self::IntoIter { - // Self::IntoIter::new(self) - // } - // } + impl Serialize for $struct_name + where + $item_ty: Serialize + Identifiable + Debug + Clone, + { + fn serialize( + &self, + serializer: S, + ) -> Result<::Ok, ::Error> + where + S: Serializer, + { + Vec::serialize(&self.elements(), serializer) + } + } + + impl<'de> Deserialize<'de> for $struct_name + where + $item_ty: Deserialize<'de> + Identifiable + Debug + Clone, + { + #[cfg(not(tarpaulin_include))] // false negative + fn deserialize>( + deserializer: D, + ) -> Result<$struct_name, D::Error> { + let elements = Vec::<$item_ty>::deserialize(deserializer)?; + IdentifiedVecOf::<$item_ty>::try_from_iter_select_unique_with( + elements, + |(idx, _, _)| Err(IdentifiedVecOfSerdeFailure::DuplicateElementsAtIndex(idx)), + ) + .map(|id_vec_of| Self::from_identified_vec_of(id_vec_of)) + .map_err(de::Error::custom) + } + } }; } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 5d3732e..f6327db 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -5,10 +5,10 @@ version = "0.1.0" edition = "2021" publish = false -[dev-dependencies] +[dependencies] identified_vec = { path = "../identified_vec", features = ["id_prim", "serde"] } identified_vec_macros = { path = "../identified_vec_macros" } -serde = "1.0.193" +serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.108" rand = "0.8.5" maplit = "1.0.2" diff --git a/tests/tests.rs b/tests/tests.rs index eeedaac..f99933f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -6,14 +6,15 @@ use identified_vec::{ ItemsCloned, ViaMarker, }; +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + use identified_vec_macros::newtype_identified_vec; -#[derive(Eq, PartialEq, Clone)] +#[derive(Eq, PartialEq, Clone, Serialize, Deserialize)] pub struct User { pub id: u16, pub name: RefCell, } - impl User { fn new(id: u16, name: &str) -> Self { if name.is_empty() { @@ -483,7 +484,7 @@ fn remove_at_out_of_bounds() { } #[test] -fn serde() { +fn serde_identified_vec_of() { let identified_vec = SUT::from_iter([1, 2, 3]); assert_eq!( serde_json::to_value(identified_vec.clone()) @@ -506,6 +507,34 @@ fn serde() { assert!(serde_json::from_str::("invalid").is_err(),); } +#[test] +fn serde_is_identified_vec() { + newtype_identified_vec!(of: u32, named: Ints); + + let identified_vec = Ints::from_iter([1, 2, 3]); + let cloned = identified_vec.clone(); + assert_eq!(&cloned, &identified_vec); + assert_eq!( + serde_json::to_value(identified_vec.clone()) + .and_then(|j| serde_json::from_value::(j)) + .unwrap(), + identified_vec + ); + assert_eq!( + serde_json::from_str::("[1,2,3]").unwrap(), + identified_vec + ); + assert_eq!(serde_json::to_string(&identified_vec).unwrap(), "[1,2,3]"); + assert_eq!( + serde_json::from_str::("[1,1,1]") + .expect_err("should fail") + .to_string(), + "Duplicate element at offset 1" + ); + + assert!(serde_json::from_str::("invalid").is_err(),); +} + #[test] fn serde_via_vec() { let vec = vec![1, 2, 3]; From af4ba32e1cb2cf3cb9bc1c4852ee4691b6aa789a Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 16 Dec 2023 13:28:19 +0100 Subject: [PATCH 19/24] WIP --- identified_vec_macros/src/lib.rs | 2 ++ tests/Cargo.toml | 6 +++++- tests/tests.rs | 7 +++++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index fcf72cb..6d59bc5 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -30,6 +30,7 @@ macro_rules! newtype_identified_vec { } } + #[cfg(any(test, feature = "serde"))] impl Serialize for $struct_name where $item_ty: Serialize + Identifiable + Debug + Clone, @@ -45,6 +46,7 @@ macro_rules! newtype_identified_vec { } } + #[cfg(any(test, feature = "serde"))] impl<'de> Deserialize<'de> for $struct_name where $item_ty: Deserialize<'de> + Identifiable + Debug + Clone, diff --git a/tests/Cargo.toml b/tests/Cargo.toml index f6327db..bee30e4 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -5,10 +5,14 @@ version = "0.1.0" edition = "2021" publish = false +[features] +default = ["serde"] +serde = ["dep:serde"] + [dependencies] identified_vec = { path = "../identified_vec", features = ["id_prim", "serde"] } identified_vec_macros = { path = "../identified_vec_macros" } -serde = { version = "1.0.193", features = ["derive"] } +serde = { version = "1.0.193", features = ["derive"], optional = true } serde_json = "1.0.108" rand = "0.8.5" maplit = "1.0.2" diff --git a/tests/tests.rs b/tests/tests.rs index f99933f..a4f8c4f 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -6,11 +6,13 @@ use identified_vec::{ ItemsCloned, ViaMarker, }; +#[cfg(any(test, feature = "serde"))] use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use identified_vec_macros::newtype_identified_vec; -#[derive(Eq, PartialEq, Clone, Serialize, Deserialize)] +#[derive(Eq, PartialEq, Clone)] +#[cfg_attr(any(test, feature = "serde"), derive(Serialize, Deserialize))] pub struct User { pub id: u16, pub name: RefCell, @@ -507,6 +509,7 @@ fn serde_identified_vec_of() { assert!(serde_json::from_str::("invalid").is_err(),); } +#[cfg(any(test, feature = "serde"))] #[test] fn serde_is_identified_vec() { newtype_identified_vec!(of: u32, named: Ints); @@ -536,7 +539,7 @@ fn serde_is_identified_vec() { } #[test] -fn serde_via_vec() { +fn serde_using_vec() { let vec = vec![1, 2, 3]; let json_from_vec = serde_json::to_value(vec).unwrap(); let mut identified_vec = serde_json::from_value::(json_from_vec).unwrap(); From 6b2510b84f8cfb136aab9dfdf9829f3c06f02be7 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 16 Dec 2023 20:44:48 +0100 Subject: [PATCH 20/24] add iterators --- .../src/identified_vec_into_iterator.rs | 36 ++++++++++++++++ identified_vec/src/identified_vec_iterator.rs | 42 +++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 identified_vec/src/identified_vec_into_iterator.rs create mode 100644 identified_vec/src/identified_vec_iterator.rs diff --git a/identified_vec/src/identified_vec_into_iterator.rs b/identified_vec/src/identified_vec_into_iterator.rs new file mode 100644 index 0000000..4eb7f1a --- /dev/null +++ b/identified_vec/src/identified_vec_into_iterator.rs @@ -0,0 +1,36 @@ +use crate::is_identifiable_vec::IsIdentifiableVec; +use crate::vec::IdentifiedVec; +use std::fmt::Debug; +use std::hash::Hash; + +/// An owning iterator over the items of an `IdentifiedVec`. +pub struct IdentifiedVecIntoIterator +where + I: Eq + Hash + Clone + Debug, +{ + identified_vec: IdentifiedVec, +} + +impl IdentifiedVecIntoIterator +where + I: Eq + Hash + Clone + Debug, +{ + pub fn new(identified_vec: IdentifiedVec) -> Self { + Self { identified_vec } + } +} + +impl Iterator for IdentifiedVecIntoIterator +where + I: Eq + Hash + Clone + Debug, +{ + type Item = E; + + fn next(&mut self) -> Option { + if self.identified_vec.len() == 0 { + return None; + } + let result = self.identified_vec.remove_at(0); + Some(result) + } +} diff --git a/identified_vec/src/identified_vec_iterator.rs b/identified_vec/src/identified_vec_iterator.rs new file mode 100644 index 0000000..e63a5f1 --- /dev/null +++ b/identified_vec/src/identified_vec_iterator.rs @@ -0,0 +1,42 @@ +use crate::is_identifiable_vec::IsIdentifiableVec; +use crate::vec::IdentifiedVec; +use std::fmt::Debug; +use std::hash::Hash; + +/// An iterator over the items of an `IdentifiedVec`. +pub struct IdentifiedVecIterator<'a, I, E> +where + I: Eq + Hash + Clone + Debug, +{ + identified_vec: &'a IdentifiedVec, + index: usize, +} + +impl<'a, I, E> IdentifiedVecIterator<'a, I, E> +where + I: Eq + Hash + Clone + Debug, +{ + pub fn new(identified_vec: &'a IdentifiedVec) -> Self { + Self { + identified_vec, + index: 0, + } + } +} + +impl<'a, I, E> Iterator for IdentifiedVecIterator<'a, I, E> +where + I: Eq + Hash + Clone + Debug, +{ + type Item = &'a E; + + fn next(&mut self) -> Option { + if self.index < self.identified_vec.len() { + let id = Some(&self.identified_vec.order[self.index]).unwrap(); + self.index += 1; + return self.identified_vec.get(id); + } else { + None + } + } +} From 338fa1524e2ac9d186f82b8e389a380bb5a43e98 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 16 Dec 2023 20:51:03 +0100 Subject: [PATCH 21/24] WIP --- identified_vec/src/vec.rs | 20 ------------- identified_vec_macros/src/lib.rs | 11 +++++-- tests/tests.rs | 49 ++------------------------------ 3 files changed, 11 insertions(+), 69 deletions(-) diff --git a/identified_vec/src/vec.rs b/identified_vec/src/vec.rs index 7d80c9b..9957d02 100644 --- a/identified_vec/src/vec.rs +++ b/identified_vec/src/vec.rs @@ -827,26 +827,6 @@ where } } -/////////////////////// -//// DEBUG /// -/////////////////////// -impl IdentifiedVec -where - E: Debug, - I: Eq + Hash + Clone + Debug, -{ - #[cfg(not(tarpaulin_include))] - #[cfg(debug_assertions)] - pub fn debug(&self) { - println!("{}", self.debug_str()); - } - - #[cfg(debug_assertions)] - pub fn debug_str(&self) -> String { - format!("order: {:?}\nelements: {:?}", self.order, self.elements) - } -} - impl IdentifiedVec where ID: Eq + Hash + Clone + Debug, diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index 6d59bc5..aadd296 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -1,8 +1,6 @@ #[macro_export] macro_rules! newtype_identified_vec { (of: $item_ty: ty, named: $struct_name: ident) => { - use identified_vec::IdentifiedVecIntoIterator; - #[derive(Debug, Clone, Eq, PartialEq)] pub struct $struct_name(IdentifiedVecOf<$item_ty>); @@ -21,9 +19,16 @@ macro_rules! newtype_identified_vec { } } + impl std::fmt::Display for $struct_name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.elements().fmt(f) + } + } + impl IntoIterator for $struct_name { type Item = $item_ty; - type IntoIter = IdentifiedVecIntoIterator<<$item_ty as Identifiable>::ID, $item_ty>; + type IntoIter = + identified_vec::IdentifiedVecIntoIterator<<$item_ty as Identifiable>::ID, $item_ty>; fn into_iter(self) -> Self::IntoIter { Self::IntoIter::new(self.0) diff --git a/tests/tests.rs b/tests/tests.rs index a4f8c4f..0e02ec1 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -55,7 +55,7 @@ impl Identifiable for User { } } -type SUT = IdentifiedVecOf; +newtype_identified_vec!(of: u32, named: SUT); newtype_identified_vec!(of: User, named: Users); #[test] @@ -69,14 +69,6 @@ fn ids() { assert_eq!(identified_vec.ids(), &[1, 2, 3]) } -#[test] -fn debug_str() { - let identified_vec = SUT::from_iter([1, 2, 3]); - assert!(identified_vec - .debug_str() - .starts_with("order: [1, 2, 3]\nelements: {"),) -} - #[test] fn elements() { let vec = vec![User::blob(), User::blob_jr(), User::blob_sr()]; @@ -306,6 +298,7 @@ fn append() { #[test] fn try_append_unique_element() { + type SUT = IdentifiedVecOf; let mut identified_vec = SUT::from_iter([1, 2, 3]); let result = identified_vec.try_append_unique_element(4); assert!(result.is_ok()); @@ -644,6 +637,7 @@ fn display() { #[test] fn hash() { + type SUT = IdentifiedVecOf; let identified_vec = SUT::from_iter([1, 2, 3]); assert_eq!( HashSet::>::from_iter([identified_vec.clone()]), @@ -651,43 +645,6 @@ fn hash() { ) } -#[test] -fn isid() { - struct CollectionOfUsersVia(IdentifiedVecOf); - impl ViaMarker for CollectionOfUsersVia {} - impl IsIdentifiableVecOfVia for CollectionOfUsersVia { - fn via_mut(&mut self) -> &mut IdentifiedVecOf { - &mut self.0 - } - fn via(&self) -> &IdentifiedVecOf { - &self.0 - } - fn from_identified_vec_of(identified_vec_of: IdentifiedVecOf) -> Self { - Self(identified_vec_of) - } - } - impl IntoIterator for CollectionOfUsersVia { - type Item = User; - - type IntoIter = IdentifiedVecIntoIterator<::ID, User>; - - fn into_iter(self) -> Self::IntoIter { - todo!() - } - } - - let mut sut = CollectionOfUsersVia::new(); - sut.append(User::blob_jr()); - assert_eq!(sut.items(), [User::blob_jr()]); - sut.remove_at(0); - assert_eq!(sut.len(), 0); - sut.update_or_append(User::blob_sr()); - sut.update_or_append(User::blob_sr()); - assert_eq!(sut.items(), [User::blob_sr()]); - sut.update_or_append(User::blob_jr()); - assert_eq!(sut.items(), [User::blob_sr(), User::blob_jr()]); -} - #[test] fn test_macro() { newtype_identified_vec!(of: User, named: CollectionOfUsers); From 0064f3e8e83f788796167daed54a322a1bcf1c3d Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 16 Dec 2023 21:03:23 +0100 Subject: [PATCH 22/24] 100% code coverage again, now with 'newtype_identified_vec' macro --- identified_vec_macros/src/lib.rs | 13 ++++--------- tests/tests.rs | 32 ++++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index aadd296..e9b8425 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -21,7 +21,7 @@ macro_rules! newtype_identified_vec { impl std::fmt::Display for $struct_name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.elements().fmt(f) + std::fmt::Display::fmt(&self.0, f) } } @@ -47,7 +47,7 @@ macro_rules! newtype_identified_vec { where S: Serializer, { - Vec::serialize(&self.elements(), serializer) + IdentifiedVecOf::serialize(&self.0, serializer) } } @@ -60,13 +60,8 @@ macro_rules! newtype_identified_vec { fn deserialize>( deserializer: D, ) -> Result<$struct_name, D::Error> { - let elements = Vec::<$item_ty>::deserialize(deserializer)?; - IdentifiedVecOf::<$item_ty>::try_from_iter_select_unique_with( - elements, - |(idx, _, _)| Err(IdentifiedVecOfSerdeFailure::DuplicateElementsAtIndex(idx)), - ) - .map(|id_vec_of| Self::from_identified_vec_of(id_vec_of)) - .map_err(de::Error::custom) + let id_vec_of = IdentifiedVecOf::<$item_ty>::deserialize(deserializer)?; + return Ok(Self::from_identified_vec_of(id_vec_of)); } } }; diff --git a/tests/tests.rs b/tests/tests.rs index 0e02ec1..85b2249 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -88,6 +88,16 @@ fn into_iter() { } } +#[test] +fn into_iter_identified_vec() { + type Users = IdentifiedVecOf; + let vec = vec![User::blob(), User::blob_jr(), User::blob_sr()]; + let identified_vec = Users::from_iter(vec.clone()); + for (idx, element) in identified_vec.into_iter().enumerate() { + assert_eq!(vec[idx], element) + } +} + #[test] fn iter() { let vec = vec![User::blob(), User::blob_jr(), User::blob_sr()]; @@ -395,6 +405,7 @@ fn update_with() { sut.append(User::new(2, "Blob, Jr.")); sut.update_with(&2, |u| u.name.borrow_mut().make_ascii_uppercase()); assert_eq!(sut.items(), [User::new(2, "BLOB, JR.")]); + assert_eq!(sut.update_with(&999, |_| panic!("not called")), false); } #[test] @@ -544,8 +555,8 @@ fn serde_using_vec() { #[test] fn eq() { - #[derive(Eq, PartialEq, Clone, Hash, Debug)] - struct Foo { + #[derive(Eq, PartialEq, Clone, Hash, Debug, Serialize, Deserialize)] + pub struct Foo { id: &'static str, value: String, } @@ -566,16 +577,17 @@ fn eq() { } // Create `IdentifiedVec` using all of the initializers - let mut vecs: Vec> = vec![ - IdentifiedVecOf::new(), - IdentifiedVecOf::new_identifying_element(|e| e.id()), - IdentifiedVecOf::from_iter_select_unique_with([], |_| ConflictResolutionChoice::ChooseLast), - IdentifiedVecOf::from_iter_select_unique_ids_with( + newtype_identified_vec!(of: Foo, named: SUT); + let mut vecs: Vec = vec![ + SUT::new(), + SUT::new_identifying_element(|e| e.id()), + SUT::from_iter_select_unique_with([], |_| ConflictResolutionChoice::ChooseLast), + SUT::from_iter_select_unique_ids_with( [], |e| e.id(), |_| ConflictResolutionChoice::ChooseLast, ), - IdentifiedVecOf::try_from_iter_select_unique_ids_with( + SUT::try_from_iter_select_unique_ids_with( [], |e: &Foo| e.id(), |_| Result::<_, ()>::Ok(ConflictResolutionChoice::ChooseLast), @@ -584,7 +596,7 @@ fn eq() { ]; assert_eq!( - IdentifiedVecOf::try_from_iter_select_unique_ids_with( + SUT::try_from_iter_select_unique_ids_with( [Foo::new(), Foo::new()], |e: &Foo| e.id(), |_| Err(IdentifiedVecOfSerdeFailure::DuplicateElementsAtIndex(1)), @@ -593,7 +605,7 @@ fn eq() { ); assert_eq!( - IdentifiedVecOf::try_from_iter_select_unique_with([Foo::new(), Foo::new()], |_| Err( + SUT::try_from_iter_select_unique_with([Foo::new(), Foo::new()], |_| Err( IdentifiedVecOfSerdeFailure::DuplicateElementsAtIndex(1) ),), Err(IdentifiedVecOfSerdeFailure::DuplicateElementsAtIndex(1)) From 43b61c647800988a5c29ed3ad1bf88cbcdfb98b4 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 16 Dec 2023 21:04:31 +0100 Subject: [PATCH 23/24] prepare 0.1.5 --- Cargo.lock | 4 ++-- identified_vec/Cargo.toml | 2 +- identified_vec_macros/Cargo.toml | 5 +---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba5a6d4..4b495fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,7 +21,7 @@ dependencies = [ [[package]] name = "identified_vec" -version = "0.1.4" +version = "0.1.5" dependencies = [ "serde", "thiserror", @@ -29,7 +29,7 @@ dependencies = [ [[package]] name = "identified_vec_macros" -version = "0.1.0" +version = "0.1.5" dependencies = [ "identified_vec", "proc-macro2", diff --git a/identified_vec/Cargo.toml b/identified_vec/Cargo.toml index 58e38de..a692f19 100644 --- a/identified_vec/Cargo.toml +++ b/identified_vec/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identified_vec" -version = "0.1.4" +version = "0.1.5" edition = "2021" authors = ["Alexander Cyon "] description = "Like HashSet but retaining INSERTION order and without `Hash` requirement on the Element type." diff --git a/identified_vec_macros/Cargo.toml b/identified_vec_macros/Cargo.toml index 1589419..62c03bb 100644 --- a/identified_vec_macros/Cargo.toml +++ b/identified_vec_macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "identified_vec_macros" -version = "0.1.0" +version = "0.1.5" edition = "2021" authors = ["Alexander Cyon "] @@ -9,6 +9,3 @@ identified_vec = { path = "../identified_vec" } proc-macro2 = "1.0.70" quote = "1.0" syn = { version = "2.0", features = ["extra-traits", "full", "parsing"] } - -# [lib] -# proc-macro = true From ce86fe2bc4cb44ead16373d9848ecea39819a8e0 Mon Sep 17 00:00:00 2001 From: Alexander Cyon Date: Sat, 16 Dec 2023 21:12:58 +0100 Subject: [PATCH 24/24] doc --- identified_vec_macros/src/lib.rs | 22 ++++++++++++++++++++++ tests/tests.rs | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/identified_vec_macros/src/lib.rs b/identified_vec_macros/src/lib.rs index e9b8425..c7ee743 100644 --- a/identified_vec_macros/src/lib.rs +++ b/identified_vec_macros/src/lib.rs @@ -1,3 +1,25 @@ +//! The `newtype_identified_vec` macro allows you to create +//! a newtype wrapping an `IdentifiedVecOf` of the item type +//! you pass in, but it gets super powers! It implements the +//! traits `IsIdentifiableVecOfVia`, which implements the trait +//! `IsIdentifiableVecOf`, meaning that the declared newtype, +//! gets all the methods and functions of a `IdentifiedVecOf`, +//! and if you use the `"serde"` feature, it is also +//! (de)serializable. +//! +//! You use it like so: +//! ``` +//! extern crate identified_vec; +//! extern crate identified_vec_macros; +//! use identified_vec_macros::newtype_identified_vec; +//! use identified_vec::{IsIdentifiableVecOfVia, ViaMarker, IsIdentifiableVec, IsIdentifiableVecOf, IdentifiedVec, IdentifiedVecOf, Identifiable}; +//! +//! newtype_identified_vec!(of: u32, named: Ints);; +//! +//! let mut ints = Ints::new(); +//! ints.append(5); +//! ``` +//! #[macro_export] macro_rules! newtype_identified_vec { (of: $item_ty: ty, named: $struct_name: ident) => { diff --git a/tests/tests.rs b/tests/tests.rs index 85b2249..e273681 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -7,7 +7,7 @@ use identified_vec::{ }; #[cfg(any(test, feature = "serde"))] -use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; use identified_vec_macros::newtype_identified_vec;