diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b406638 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: Rust + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + env: + RUST_BACKTRACE: 1 + steps: + - run: rustup update stable + - uses: actions/checkout@v4 + - run: cargo test --all-features + + rustfmt: + runs-on: ubuntu-latest + steps: + - run: rustup update stable + - uses: actions/checkout@v4 + - run: cargo fmt -- --check diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3ec5b10..0000000 --- a/.travis.yml +++ /dev/null @@ -1,17 +0,0 @@ -sudo: false -language: rust -cache: - cargo: true -rust: -- stable -- nightly -env: -- RUST_BACKTRACE=full -matrix: - allow_failures: - - rust: nightly - fast_finish: true -os: -- linux -script: -- cargo test --features "serde" diff --git a/Cargo.toml b/Cargo.toml index cba6fc7..95d821b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "linked_hash_set" version = "0.1.4" authors = ["Alex Butler "] -edition = "2018" +edition = "2021" description = "HashSet with insertion ordering" repository = "https://github.com/alexheretic/linked-hash-set" keywords = ["data-structures"] @@ -10,7 +10,7 @@ license = "Apache-2.0" readme="README.md" [dependencies] -linked-hash-map = "0.5.3" +linked-hash-map = "0.5.6" serde = { version = "1", optional = true } [dev-dependencies] diff --git a/LICENSE b/LICENSE index 8dada3e..d9a10c0 100644 --- a/LICENSE +++ b/LICENSE @@ -174,28 +174,3 @@ of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/release b/release deleted file mode 100755 index ae9ad9f..0000000 --- a/release +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -set -eu -dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cd "$dir" - -tagname=$(grep -m1 version Cargo.toml | cut -d '"' -f2) - -if git rev-parse "$tagname" >/dev/null 2>&1 -then - echo "tag $tagname already exists" >&2 - exit 1 -fi - -echo "Release $tagname" -read -p "continue? [y/N] " -n 1 -r -echo -if ! [[ $REPLY =~ ^[^Nn]$ ]]; then - exit 0 -fi - -git tag -s "$tagname" -m "Release $tagname" -git push --tags - -gio open "https://github.com/alexheretic/linked-hash-set/releases/new?tag=$tagname" diff --git a/src/lib.rs b/src/lib.rs index dcdf9cb..f1e2b94 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,12 +17,14 @@ pub mod serde; use linked_hash_map as map; use linked_hash_map::{Keys, LinkedHashMap}; -use std::borrow::Borrow; -use std::collections::hash_map::RandomState; -use std::fmt; -use std::hash::{BuildHasher, Hash, Hasher}; -use std::iter::{Chain, FromIterator}; -use std::ops::{BitAnd, BitOr, BitXor, Sub}; +use std::{ + borrow::Borrow, + collections::hash_map::RandomState, + fmt, + hash::{BuildHasher, Hash, Hasher}, + iter::{Chain, FromIterator}, + ops::{BitAnd, BitOr, BitXor, Sub}, +}; // Note: This implementation is adapted from std `HashSet` implementation ~2017-10 // parts relying on std `HashMap` functionality that is not present in `LinkedHashMap` or @@ -116,11 +118,8 @@ use std::ops::{BitAnd, BitOr, BitXor, Sub}; /// ``` /// use linked_hash_set::LinkedHashSet; /// -/// fn main() { -/// let viking_names: LinkedHashSet<&str> = -/// ["Einar", "Olaf", "Harald"].iter().cloned().collect(); -/// // use the values stored in the set -/// } +/// let viking_names: LinkedHashSet<&str> = ["Einar", "Olaf", "Harald"].iter().cloned().collect(); +/// // use the values stored in the set /// ``` /// /// [`front()`]: struct.LinkedHashSet.html#method.front @@ -514,10 +513,11 @@ where /// assert_eq!(set.contains(&1), true); /// assert_eq!(set.contains(&4), false); /// ``` - pub fn contains(&self, value: &Q) -> bool + pub fn contains(&self, value: &Q) -> bool where T: Borrow, Q: Hash + Eq, + Q: ?Sized, { self.map.contains_key(value) } @@ -541,10 +541,11 @@ where /// assert_eq!(was_refreshed, true); /// assert_eq!(set.into_iter().collect::>(), vec![1, 3, 2]); /// ``` - pub fn refresh(&mut self, value: &Q) -> bool + pub fn refresh(&mut self, value: &Q) -> bool where T: Borrow, Q: Hash + Eq, + Q: ?Sized, { self.map.get_refresh(value).is_some() } @@ -707,10 +708,11 @@ where /// assert_eq!(set.remove(&2), true); /// assert_eq!(set.remove(&2), false); /// ``` - pub fn remove(&mut self, value: &Q) -> bool + pub fn remove(&mut self, value: &Q) -> bool where T: Borrow, Q: Hash + Eq, + Q: ?Sized, { self.map.remove(value).is_some() } @@ -865,7 +867,7 @@ where } } -impl<'a, 'b, T, S> BitOr<&'b LinkedHashSet> for &'a LinkedHashSet +impl BitOr<&LinkedHashSet> for &LinkedHashSet where T: Eq + Hash + Clone, S: BuildHasher + Default, @@ -897,7 +899,7 @@ where } } -impl<'a, 'b, T, S> BitAnd<&'b LinkedHashSet> for &'a LinkedHashSet +impl BitAnd<&LinkedHashSet> for &LinkedHashSet where T: Eq + Hash + Clone, S: BuildHasher + Default, @@ -929,7 +931,7 @@ where } } -impl<'a, 'b, T, S> BitXor<&'b LinkedHashSet> for &'a LinkedHashSet +impl BitXor<&LinkedHashSet> for &LinkedHashSet where T: Eq + Hash + Clone, S: BuildHasher + Default, @@ -961,7 +963,7 @@ where } } -impl<'a, 'b, T, S> Sub<&'b LinkedHashSet> for &'a LinkedHashSet +impl Sub<&LinkedHashSet> for &LinkedHashSet where T: Eq + Hash + Clone, S: BuildHasher + Default, @@ -1141,7 +1143,7 @@ impl<'a, K> Iterator for Iter<'a, K> { self.iter.size_hint() } } -impl<'a, K> ExactSizeIterator for Iter<'a, K> { +impl ExactSizeIterator for Iter<'_, K> { fn len(&self) -> usize { self.iter.len() } @@ -1152,7 +1154,7 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { } } -impl<'a, K: fmt::Debug> fmt::Debug for Iter<'a, K> { +impl fmt::Debug for Iter<'_, K> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_list().entries(self.clone()).finish() } @@ -1252,7 +1254,7 @@ where } } -impl<'a, T, S> fmt::Debug for Intersection<'a, T, S> +impl fmt::Debug for Intersection<'_, T, S> where T: fmt::Debug + Eq + Hash, S: BuildHasher, @@ -1297,7 +1299,7 @@ where } } -impl<'a, T, S> fmt::Debug for Difference<'a, T, S> +impl fmt::Debug for Difference<'_, T, S> where T: fmt::Debug + Eq + Hash, S: BuildHasher, @@ -1330,7 +1332,7 @@ where } } -impl<'a, T, S> fmt::Debug for SymmetricDifference<'a, T, S> +impl fmt::Debug for SymmetricDifference<'_, T, S> where T: fmt::Debug + Eq + Hash, S: BuildHasher, @@ -1348,7 +1350,7 @@ impl<'a, T, S> Clone for Union<'a, T, S> { } } -impl<'a, T, S> fmt::Debug for Union<'a, T, S> +impl fmt::Debug for Union<'_, T, S> where T: fmt::Debug + Eq + Hash, S: BuildHasher, @@ -1813,10 +1815,7 @@ mod test_linked { #[test] fn clone_order_is_maintained() { let set = set![123, 234, 56, 677]; - assert_eq!( - set.clone().into_iter().collect::>(), - vec![123, 234, 56, 677] - ); + assert_eq!(set.into_iter().collect::>(), vec![123, 234, 56, 677]); } #[test] @@ -1871,4 +1870,33 @@ mod test_linked { assert_eq!(iter.next(), None); assert_eq!(iter.next_back(), None); } + + #[test] + fn linked_set_equality() { + let mut set1 = LinkedHashSet::new(); + assert!(set1.insert(234)); + assert!(set1.insert(123)); + assert!(set1.insert(345)); + + let mut set2 = LinkedHashSet::new(); + assert!(set2.insert(123)); + assert!(set2.insert(345)); + assert!(set2.insert(234)); + + assert_eq!(set1, set2); + + /// Returns true if the given sets are equal and have identical iteration order. + fn equal_and_same_order(a: &LinkedHashSet, b: &LinkedHashSet) -> bool { + a.len() == b.len() && a.iter().eq(b) + } + + assert!(!equal_and_same_order(&set1, &set2)); + + let mut set3 = LinkedHashSet::new(); + assert!(set3.insert(234)); + assert!(set3.insert(123)); + assert!(set3.insert(345)); + + assert!(equal_and_same_order(&set1, &set3)); + } } diff --git a/src/serde.rs b/src/serde.rs index 653b0c0..165d426 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,11 +1,15 @@ //! An optional implementation of serialization/deserialization. -use super::LinkedHashSet; -use serde::de::{Error, SeqAccess, Visitor}; -use serde::ser::SerializeSeq; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use std::fmt::{Formatter, Result as FmtResult}; -use std::hash::{BuildHasher, Hash}; -use std::marker::PhantomData; +use crate::LinkedHashSet; +use serde::{ + de::{Error, SeqAccess, Visitor}, + ser::SerializeSeq, + Deserialize, Deserializer, Serialize, Serializer, +}; +use std::{ + fmt::{Formatter, Result as FmtResult}, + hash::{BuildHasher, Hash}, + marker::PhantomData, +}; impl Serialize for LinkedHashSet where