diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 9cc3a95..eb689e6 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -8,24 +8,39 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: taiki-e/install-action@v2 + with: + tool: just + - run: echo "RUST_TOOLCHAIN=$(just rust-version)" >> $GITHUB_ENV + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} - uses: Swatinem/rust-cache@v2 with: shared-key: debug-build - name: Test - run: cargo test --workspace + run: just test lint: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - uses: taiki-e/install-action@v2 + with: + tool: just + - run: | + echo "RUST_TOOLCHAIN=$(just rust-version)" >> $GITHUB_ENV + echo "RUST_TOOLCHAIN_NIGHTLY=$(just rust-nightly-version)" >> $GITHUB_ENV + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_TOOLCHAIN_NIGHTLY }} + components: rustfmt + - uses: dtolnay/rust-toolchain@master with: + toolchain: ${{ env.RUST_TOOLCHAIN }} components: rustfmt, clippy - uses: Swatinem/rust-cache@v2 with: shared-key: debug-build - name: Lint - run: cargo clippy --workspace --all-targets --all-features -- -D warnings - - name: Check formatting - run: cargo fmt --all --check + run: just lint "strict" diff --git a/Cargo.toml b/Cargo.toml index 523abbe..08f0d95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,48 +13,44 @@ keywords = ["bitset", "stack", "small", "data-structure"] num-traits = "0.2.17" [lints.rust] +macro_use_extern_crate = "warn" +meta_variable_misuse = "warn" +missing_copy_implementations = "warn" missing_debug_implementations = "warn" -missing_docs = "warn" -unused_crate_dependencies = "warn" +redundant_lifetimes = "deny" +single_use_lifetimes = "deny" +unreachable_pub = "deny" +unnameable_types = "deny" +unsafe_code = "deny" +unstable_features = "deny" +unused_crate_dependencies = "deny" +unused_extern_crates = "warn" unused_import_braces = "warn" unused_lifetimes = "warn" unused_macro_rules = "warn" -unused_results = "warn" -unused_tuple_struct_fields = "warn" +unused_qualifications = "warn" +unused_results = "deny" +dead_code = "warn" +unused = { level = "warn", priority = -1 } +missing_docs = "deny" +refining_impl_trait = { level = "warn", priority = -1 } + +[lints.rustdoc] +broken_intra_doc_links = "deny" +private_intra_doc_links = "allow" [lints.clippy] -correctness = "deny" -suspicious = "deny" -complexity = "warn" -perf = "warn" -style = "warn" -cargo = "warn" -# selected lints from the `pedantic` group -cast_lossless = "warn" -cast_possible_truncation = "warn" -cast_possible_wrap = "warn" -checked_conversions = "warn" -cloned_instead_of_copied = "warn" -copy_iterator = "warn" -doc_link_with_quotes = "warn" -doc_markdown = "warn" -explicit_into_iter_loop = "warn" -explicit_iter_loop = "warn" -filter_map_next = "warn" -flat_map_option = "warn" -iter_not_returning_iterator = "warn" -manual_assert = "warn" -manual_instant_elapsed = "warn" -manual_let_else = "warn" -manual_ok_or = "warn" -map_unwrap_or = "warn" -match_bool = "warn" -match_same_arms = "warn" -missing_errors_doc = "warn" -missing_fields_in_debug = "warn" -missing_panics_doc = "warn" -module_name_repetitions = "warn" -range_minus_one = "warn" -range_plus_one = "warn" -redundant_else = "warn" -semicolon_if_nothing_returned = "warn" +complexity = { level = "warn", priority = -1 } +correctness = { level = "deny", priority = -1 } +perf = { level = "warn", priority = -1 } +style = { level = "warn", priority = -1 } +suspicious = { level = "deny", priority = -1 } +todo = "warn" + +disallowed-types = "deny" +missing_const_for_fn = "warn" + +allow_attributes = "deny" +allow_attributes_without_reason = "deny" + +pedantic = { level = "warn", priority = -1 } # setting a lower priority for the group to allow individual overrides diff --git a/justfile b/justfile new file mode 100644 index 0000000..512d6a0 --- /dev/null +++ b/justfile @@ -0,0 +1,41 @@ +# Fail on early and on unset variables in non-shebang recipes +set shell := ["bash", "-euo", "pipefail", "-c"] +# Allow usage of bash methods to handle multiple arguments and work around quoting issues +set positional-arguments +set quiet + +@default: fmt lint test + +rust_version := `sed -nr 's/channel = "(.*)"/\1/p' rust-toolchain.toml` +rust_nightly_version := `sed -nr 's/channel = "(.*)"/\1/p' rust-toolchain-nightly.toml` + +rust-version: + echo '{{rust_version}}' + +rust-nightly-version: + echo '{{rust_nightly_version}}' + +test: + cargo test --workspace --all-targets --all-features + cargo test --workspace --doc + +lint strict="": + cargo '+{{rust_nightly_version}}' fmt -- --check + cargo clippy \ + --workspace \ + --tests \ + --benches \ + --all-targets \ + --all-features \ + --quiet \ + -- {{ if strict != "" { "-D warnings" } else { "" } }} + cargo doc --all --no-deps --document-private-items --all-features --quiet + +fmt: + cargo '+{{rust_nightly_version}}' fmt + +udeps: + cargo '+{{rust_nightly_version}}' udeps + +install-nightly: + rustup toolchain install '{{rust_nightly_version}}' \ No newline at end of file diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml new file mode 100644 index 0000000..74f46e1 --- /dev/null +++ b/rust-toolchain-nightly.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly-2024-09-06" diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..1de01fa --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.81.0" diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..ac01b76 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,7 @@ +imports_granularity = "Item" +wrap_comments = true +format_code_in_doc_comments = true +normalize_comments = true +normalize_doc_attributes = true +group_imports = "StdExternalCrate" +use_field_init_shorthand = true \ No newline at end of file diff --git a/src/iterators.rs b/src/iterators.rs index b86717d..ac13005 100644 --- a/src/iterators.rs +++ b/src/iterators.rs @@ -13,7 +13,7 @@ pub struct IntoIter { } impl IntoIter { - pub(crate) fn new(blocks: [T; N]) -> Self { + pub(crate) const fn new(blocks: [T; N]) -> Self { Self { blocks, index_front: 0, diff --git a/src/lib.rs b/src/lib.rs index e703840..b27b088 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,8 @@ //! known beforehand. The [`TinyBitSet`] is copyable and the implementation //! assumes in many places that the data is small enough to cheaply be copied. //! Thus it is mostly suitable for sizes of up to 256 bits. For larger sizes, a -//! heap-allocated crate like [`fixedbitset`][fixedbitset] is likely a better fit. +//! heap-allocated crate like [`fixedbitset`][fixedbitset] is likely a better +//! fit. //! //! One unique feature of this crate is that it uses const generics to have a //! single generic bitset type whose size and underlying storage type can be @@ -41,9 +42,8 @@ use std::ops::BitXorAssign; use std::ops::Index; use std::ops::Not; -use num_traits::PrimInt; - pub use iterators::IntoIter; +use num_traits::PrimInt; /// Integer that can be used as a block of bits in a bitset. pub trait BitBlock: @@ -122,6 +122,7 @@ impl TinyBitSet { /// Creates an empty bitset. /// /// Equivalent to [`Self::EMPTY`]. + #[must_use] pub const fn new() -> Self { Self::EMPTY } @@ -131,6 +132,7 @@ impl TinyBitSet { /// # Panics /// /// Panics if `bit >= Self::CAPACITY`. + #[must_use] pub fn singleton(bit: usize) -> Self { Self::new().inserted(bit) } @@ -156,7 +158,7 @@ impl TinyBitSet { } /// Iterates over the indices of set bits from lowest to highest. - pub fn iter(self) -> IntoIter { + pub const fn iter(self) -> IntoIter { IntoIter::new(self.blocks) } @@ -371,7 +373,8 @@ impl BitOrAssign for TinyBitSet { impl BitXor for TinyBitSet { type Output = Self; - /// Returns a bitset with all bits that are set in exactly one of `self` and `rhs`. + /// Returns a bitset with all bits that are set in exactly one of `self` and + /// `rhs`. fn bitxor(self, rhs: Self) -> Self::Output { array::from_fn(|i| self.blocks[i] ^ rhs.blocks[i]).into() } @@ -504,7 +507,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")] fn singleton_out_of_range() { let _ = TestBitSet::singleton(16); } @@ -548,7 +551,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")] fn insert_out_of_range() { TestBitSet::new().insert(16); } @@ -562,7 +565,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")] fn inserted_out_of_range() { let _ = TestBitSet::new().inserted(16); } @@ -579,7 +582,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")] fn remove_out_of_range() { TestBitSet::new().remove(16); } @@ -594,7 +597,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")] fn removed_out_of_range() { let _ = TestBitSet::new().removed(16); } @@ -611,7 +614,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")] fn toggle_out_of_range() { TestBitSet::new().toggle(16); } @@ -625,7 +628,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")] fn toggled_out_of_range() { let _ = TestBitSet::new().toggled(16); } @@ -644,7 +647,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")] fn assign_out_of_range() { TestBitSet::new().assign(16, true); } @@ -659,7 +662,7 @@ mod tests { } #[test] - #[should_panic] + #[should_panic(expected = "index out of bounds: the len is 2 but the index is 2")] fn assigned_out_of_range() { let _ = TestBitSet::new().assigned(16, true); } @@ -733,8 +736,8 @@ mod tests { assert_eq!(TestBitSet::ALL, !TestBitSet::EMPTY); assert_eq!(TestBitSet::EMPTY, !TestBitSet::ALL); assert_eq!( - TestBitSet::from([0b00111100, 0b10101010]), - !TestBitSet::from([0b11000011, 0b01010101]) + TestBitSet::from([0b0011_1100, 0b1010_1010]), + !TestBitSet::from([0b1100_0011, 0b0101_0101]) ); } @@ -752,9 +755,9 @@ mod tests { test(TestBitSet::ALL, TestBitSet::ALL, TestBitSet::ALL); test( - TestBitSet::from([0b11100111, 0b01010101]), - TestBitSet::from([0b00111100, 0b10101010]), - TestBitSet::from([0b00100100, 0b00000000]), + TestBitSet::from([0b1110_0111, 0b0101_0101]), + TestBitSet::from([0b0011_1100, 0b1010_1010]), + TestBitSet::from([0b0010_0100, 0b0000_0000]), ); } @@ -772,9 +775,9 @@ mod tests { test(TestBitSet::ALL, TestBitSet::ALL, TestBitSet::ALL); test( - TestBitSet::from([0b01100110, 0b01010101]), - TestBitSet::from([0b00111100, 0b10101010]), - TestBitSet::from([0b01111110, 0b11111111]), + TestBitSet::from([0b0110_0110, 0b0101_0101]), + TestBitSet::from([0b0011_1100, 0b1010_1010]), + TestBitSet::from([0b0111_1110, 0b1111_1111]), ); } @@ -792,9 +795,9 @@ mod tests { test(TestBitSet::ALL, TestBitSet::ALL, TestBitSet::EMPTY); test( - TestBitSet::from([0b01100110, 0b01010101]), - TestBitSet::from([0b00111100, 0b10101010]), - TestBitSet::from([0b01011010, 0b11111111]), + TestBitSet::from([0b0110_0110, 0b0101_0101]), + TestBitSet::from([0b0011_1100, 0b1010_1010]), + TestBitSet::from([0b0101_1010, 0b1111_1111]), ); }