Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 13 additions & 33 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,42 +1,22 @@
# tuplities Copilot Instructions

## Architecture Overview
This is a Rust workspace providing tuple utilities. The project is split into three crates for improved compile times:
## Architecture

- `tuplities/`: Main crate that re-exports traits from subcrates
- `tuplities-{snake_trait_name}/`: One of several crates of the suite which provides the `Tuple{camel_trait_name}` trait with derive macro implementation
- `tuplities-derive/`: Procedural macro crate that generates all of the trait implementations for tuples of various sizes
The workspace contains one crate, `tuplities/`. Recursive utilities operate on `()`, singleton tuples `(T,)`, and nested pairs `(Head, Tail)`.

The derive macro generates implementations for tuples from size 0 (unit `()`) up to `MAX_TUPLE_SIZE`, controlled by Cargo features to balance functionality against compile time.
`tuplities/src/flat_bridge.rs` generates flat/nested boundary conversions through internal `macro_rules!` macros. The default flat limit is 8. Features `size-16`, `size-32`, `size-48`, `size-64`, `size-96`, and `size-128` select larger limits, with the largest enabled size taking precedence.

## Key Patterns

- **Feature-gated sizes**: Use features like `size-16`, `size-32` to control maximum supported tuple size (default: 8). See `tuplities-derive/src/tuple_size.rs` for size configuration logic.
- **Procedural macro generation**: Implementations are generated using `generate_all_sizes()` function that iterates over tuple sizes. See `tuplities-derive/src/tuplities_clone.rs` for the pattern.
- **Type parameter generation**: Use `type_params()` and `indices()` helpers in `tuple_size.rs` for generating generic type lists and field access indices.

## Developer Workflows
- Build all crates: `cargo build --workspace`
- Run tests: `cargo test --workspace`
- Enable larger tuple support: `cargo build --workspace --features size-32` (propagates to all crates)
- Debug macro expansion: `cargo expand --package tuplities-derive`

## Code Examples
```rust
// Basic usage
use tuplities::prelude::TupleClone;

let original = (1, "hello".to_string(), vec![1, 2, 3]);
let cloned = original.tuple_clone();
assert_eq!(original, cloned);
```
The crate is `no_std`. The `alloc` feature enables homogeneous vector conversion. The default `flatten-nest` feature enables flat tuple and matrix conversions and includes `alloc`.

## Conventions

- Strict linting: Workspace enforces `missing_docs = "forbid"` and extensive Clippy rules. Always document public APIs.
- Edition 2024: Use modern Rust features available in the 2024 edition.
- Workspace dependencies: Define shared deps in root `Cargo.toml` under `[workspace.dependencies]`.
- Use recursive trait implementations for nested tuple operations.
- Preserve public associated-type equalities used by downstream generic code.
- Keep shared dependencies under `[workspace.dependencies]`.
- Document public APIs and preserve the workspace's Rust and Clippy lints.
- The crate README provides crate-level documentation and executable doctests.
- Use Rust edition `2024` and support the declared minimum compiler.

## Verification

## Integration Points
- External deps: `syn`, `quote`, `proc-macro2` for macro generation
- Cross-crate communication: Derive crate generates code used by clone crate, re-exported by main crate
Scope checks to `-p tuplities`. Verify default features, `--no-default-features`, `alloc`, and supported flat width configurations. Exercise flat/nested round trips, mutable borrowing, and downstream behavior when changing the bridges.
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ updates:
# The reference selects the Rust toolchain rather than the action version,
# so Dependabot reads 1.85.0 as a release and offers a nonexistent 1.120.0.
- dependency-name: dtolnay/rust-toolchain
- package-ecosystem: cargo
directory: /
schedule:
interval: weekly
157 changes: 87 additions & 70 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,101 +2,118 @@ name: Rust CI

on:
push:
branches: [ main ]
branches: [main]
pull_request:
branches: [ main ]
branches: [main]
workflow_dispatch:

env:
CARGO_TERM_COLOR: always
CARGO_BUILD_JOBS: "4"

permissions:
contents: read

jobs:
test:
name: Test
name: Tests (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
rust: [stable, beta, nightly]
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Cache cargo registry
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- name: Run tests
run: cargo test --verbose
- name: Run tests (release)
run: cargo test --release --verbose
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@7e38f4b43b4db5c8dd498af069a4f6196df1d067
with:
toolchain: stable
- run: cargo test -p tuplities --locked
- run: cargo test -p tuplities --all-features --locked

fmt:
name: Rustfmt
quality:
name: Formatting, linting, and documentation
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Check formatting
run: cargo fmt --all -- --check
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@7e38f4b43b4db5c8dd498af069a4f6196df1d067
with:
toolchain: stable
components: rustfmt, clippy
- run: cargo fmt -p tuplities -- --check
- run: cargo clippy -p tuplities --all-targets --all-features --locked -- -D warnings
- run: cargo doc -p tuplities --no-deps --all-features --locked
env:
RUSTDOCFLAGS: -D warnings

clippy:
name: Clippy
msrv:
name: MSRV and no_std
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Run clippy
run: cargo clippy --all-targets -- -D warnings
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@7e38f4b43b4db5c8dd498af069a4f6196df1d067
with:
toolchain: 1.85.0
targets: thumbv7em-none-eabi
- run: cargo check -p tuplities --lib --all-features --locked
- run: cargo check -p tuplities --lib --no-default-features --locked
- run: cargo check -p tuplities --lib --no-default-features --target thumbv7em-none-eabi --locked

doc:
name: Documentation
features:
name: Features (${{ matrix.features || 'core' }})
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
matrix:
features:
- ""
- alloc
- flatten-nest
- flatten-nest,size-16
- flatten-nest,size-32
- flatten-nest,size-48
- flatten-nest,size-64
- flatten-nest,size-96
- flatten-nest,size-128
- flatten-nest,size-16,size-32
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Check documentation
run: cargo doc --no-deps --document-private-items
env:
RUSTDOCFLAGS: -D warnings
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@7e38f4b43b4db5c8dd498af069a4f6196df1d067
with:
toolchain: stable
- run: cargo test -p tuplities --no-default-features --features "$FEATURES" --locked
env:
FEATURES: ${{ matrix.features }}

coverage:
name: Code Coverage
name: Coverage
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@nightly
with:
components: llvm-tools-preview
- name: Install tarpaulin
uses: taiki-e/install-action@cargo-tarpaulin
- name: Generate coverage
run: cargo tarpaulin --engine=llvm --out xml --workspace --exclude tuplities-derive --run-types AllTargets --run-types Doctests
- name: Upload coverage to Codecov
uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@7e38f4b43b4db5c8dd498af069a4f6196df1d067
with:
toolchain: nightly
components: llvm-tools-preview
- uses: taiki-e/install-action@e407f7bafb71fd004bc5c2da3032e5470cbb6ef0
with:
tool: cargo-llvm-cov
fallback: none
- run: cargo llvm-cov -p tuplities --all-targets --all-features --locked --no-report
- run: cargo llvm-cov -p tuplities --doc --all-features --locked --no-clean --lcov --output-path target/coverage.lcov
- uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: target/coverage.lcov
fail_ci_if_error: false
13 changes: 8 additions & 5 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ jobs:
audit:
name: Security Audit
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
persist-credentials: false
- name: Install cargo-audit
run: cargo install cargo-audit
- name: Run security audit
run: cargo audit
uses: taiki-e/install-action@e407f7bafb71fd004bc5c2da3032e5470cbb6ef0
with:
tool: cargo-audit
fallback: none
- run: cargo audit
36 changes: 36 additions & 0 deletions .github/workflows/workflow-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Workflow validation

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
validate:
name: Workflow validation
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install workflow validators
uses: taiki-e/install-action@e407f7bafb71fd004bc5c2da3032e5470cbb6ef0 # v2.87.24
with:
tool: shellcheck@0.11.0,zizmor@1.30.1
fallback: none
- name: Install actionlint
run: |
curl --fail --location --max-time 120 --output "$RUNNER_TEMP/actionlint.tar.gz" \
https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_linux_amd64.tar.gz
printf '%s %s\n' 8aca8db96f1b94770f1b0d72b6dddcb1ebb8123cb3712530b08cc387b349a3d8 \
"$RUNNER_TEMP/actionlint.tar.gz" | sha256sum --check
tar --extract --gzip --file "$RUNNER_TEMP/actionlint.tar.gz" --directory "$RUNNER_TEMP" actionlint
- name: Validate workflows
run: '"$RUNNER_TEMP/actionlint"'
- name: Audit workflow security
run: zizmor --offline --strict-collection .github/workflows
8 changes: 1 addition & 7 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,9 @@ target
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# Generated by cargo mutants
# Contains mutation testing data
**/mutants.out*/

# RustRover
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
build_rs_cov.profraw
cobertura.xml
#.idea/
17 changes: 13 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## Added in Unreleased
### `0.2.0` package contract

- `NestedTupleChain` trait for chaining two nested tuples together.
- `NestTupleRef` and `NestTupleMut` traits for converting flat tuples into nested tuples of references and mutable references, respectively.
- `NestedTupleFlattenOption` trait for flattening nested tuples of double options into nested tuples of options.
- One `tuplities` package provides recursive tuple utilities and flat tuple interoperability.
- The `flatten-nest` feature enables flat bridges and `alloc`.
- Flat widths are selected through the existing `size-N` features.
- `NestedTuple` provides structural membership and a compile-time element count.
- Recursive indexing and row access operate without flat tuple support or allocation.
- Declarative `neple!` and `neplety!` construct and name singleton-terminated lists.
- Recursive split, insert, remove, and linear-time reverse consume owned tuple nodes and preserve element types.
- The minimum supported Rust version is `1.85`.
- Public methods provide executable examples for result shapes, borrowed mutation, and conversion errors.
- `cargo llvm-cov` measures runtime targets and doctests.
- CI covers Rust `1.85.0`, allocator-free embedded builds, supported flat widths, and mixed features.
- `unsafe_code` is forbidden.

## [0.1.4] - 2025-12-15

Expand Down
Loading
Loading