Skip to content

feat: worldstate changes#31

Merged
Mettwasser merged 9 commits intomasterfrom
worldstate-changes
Dec 31, 2025
Merged

feat: worldstate changes#31
Mettwasser merged 9 commits intomasterfrom
worldstate-changes

Conversation

@Mettwasser
Copy link
Member

@Mettwasser Mettwasser commented Dec 29, 2025

What did you fix?

Some changes regarding the changes to worldstate.

  • A cache for the worldstate requests
  • New default values
    • which are now configurable
  • A few Fixes regarding items
  • Dependency upgrades

Reproduction steps


Evidence/screenshot/link to line

Considerations

  • Does this contain a new dependency? [Yes (Upgrades)]
  • Does this introduce opinionated data formatting or manual data entry? [No]
  • Does this pr include updated data files in a separate commit that can be reverted for a clean code-only pr? [-]
  • Have I run the linter? [Yes]
  • Is is a bug fix, feature request, or enhancement? [Bug Fix/Feature/Enhancement]

Summary by CodeRabbit

  • New Features

    • Background listeners for periodic updates (stateless and stateful), including nested-update callbacks.
    • In-memory caching for type-based queries and item lookups to reduce repeated network requests.
  • Improvements

    • Minimum Rust version raised to 1.91 and docs build improved.
    • Multiple dependency upgrades and expanded README with a runnable example.
  • Breaking Changes

    • Several item fields are now optional.
    • Category field removed from weapon and warframe types.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 29, 2025

Walkthrough

Adds per-type and per-item in-memory caching to the worldstate Client, splits listener logic into two modules (single-item and nested-item listeners), tightens Queryable trait bounds, adjusts several item model fields, and bumps MSRV and multiple dependencies.

Changes

Cohort / File(s) Summary
Dependency & Toolchain
Cargo.toml, warframe-macros/Cargo.toml
MSRV bumped to 1.91; package.metadata.docs.rs added; many dependencies upgraded (tokio, reqwest, chrono, serde, serde_json, serde_repr, futures, thiserror, moka, derive_more, serde_with, tracing, governor, tracing-subscriber, proc-macro2, quote, syn, etc.).
Crate attribute
src/lib.rs
Adds #![cfg_attr(docsrs, feature(doc_cfg))] for docs.rs builds.
Client: caching & API surface
src/worldstate/client.rs
Adds ClientConfig, type/item caches (TypeCache, ItemCache), exposes http and base_url as pub(crate), initializes caches in Default/new, adds type_cached() and cached_item() helpers, routes fetch/fetch_using_lang/query_item through caches, and removes embedded listener methods.
Listeners (moved/split)
src/worldstate/listener.rs, src/worldstate/listener_nested.rs, src/worldstate/mod.rs
New modules implement expiry-based single-item listeners (call_on_update*) and collection diff nested listeners (call_on_nested_update*); mod.rs wires both modules into the crate.
Trait bound tightening
src/worldstate/models/base.rs
Queryable now requires Clone + 'static; associated Return now requires DeserializeOwned + Send + Sync + Clone + 'static.
Model changes
src/worldstate/models/items/warframe.rs, src/worldstate/models/items/weapon.rs
Removed category from Warframe, RangedWeapon, MeleeWeapon. In Warframe, aura, wikia_thumbnail, and wikia_url changed to Option<String>.
Docs / README
README.md
MSRV updated, install line and runnable example added, contributing/commitlint notes added.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant Cache
  participant HTTP as "HTTP Client"
  participant Callback

  rect rgb(245,249,255)
  Note over Client,Cache: Type/item fetch with caching
  Client->>Cache: lookup (TypeId, Language) / (Language, query)
  alt cache hit
    Cache-->>Client: cached result
    Client-->>Caller: return cached result
  else cache miss
    Client->>HTTP: perform network fetch (query / query_with_lang)
    HTTP-->>Client: response (OK / 404 / error)
    Client->>Cache: store result (respect 404 config)
    Client-->>Caller: return result
  end
  end

  rect rgb(250,250,250)
  Note over Client,Callback: Listener (call_on_update*) flow
  Client->>HTTP: initial fetch T
  Client->>Client: compute sleep until expiry
  loop running
    Client->>Client: sleep (expiry / nested_listener_sleep)
    Client->>HTTP: fetch new T / Vec<T>
    alt change detected
      Client->>Callback: invoke with (old, new) or (state, old, new) / (item, Change)
      Callback-->>Client: completed
      Client->>Client: update stored state
    end
  end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • TobiTenno
  • CraigEge

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'feat: worldstate changes' is vague and generic; it does not clearly describe the main changes. While technically related to the changeset, it lacks specificity about what 'worldstate changes' entail. Consider using a more descriptive title that highlights the primary changes, such as 'feat: add client caching layer and refactor listener APIs' or 'feat: introduce request caching and restructure listener implementation'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch worldstate-changes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/worldstate/models/base.rs (1)

3-5: Unnecessary self import.

The std::self import doesn't appear to serve any purpose. Consider removing it.

🔎 Suggested fix
 use std::{
-    self,
     fmt::Write,
     ops::{
src/worldstate/client.rs (1)

220-242: LGTM with minor note.

The cached_item() method correctly implements conditional caching based on config.cache_404_item_requests (lines 237-239), which is a good design to balance memory usage and network efficiency.

Minor observation: Line 229 creates a new Box<str> allocation for every cache lookup. While this is necessary given the ItemCache type definition, it could be optimized in the future by using &str as the cache key type if memory allocations become a concern.

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 733a100 and 1d1d154.

📒 Files selected for processing (10)
  • Cargo.toml
  • src/lib.rs
  • src/worldstate/client.rs
  • src/worldstate/listener.rs
  • src/worldstate/listener_nested.rs
  • src/worldstate/mod.rs
  • src/worldstate/models/base.rs
  • src/worldstate/models/items/warframe.rs
  • src/worldstate/models/items/weapon.rs
  • warframe-macros/Cargo.toml
💤 Files with no reviewable changes (1)
  • src/worldstate/models/items/weapon.rs
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: Mettwasser
Repo: WFCD/warframe.rs PR: 24
File: src/market/client.rs:127-143
Timestamp: 2025-04-17T14:00:48.579Z
Learning: In the Warframe.rs codebase, the caching implementation in the market client is designed to return the direct value type (T) rather than an Arc<T> to maintain consistent API interfaces between cached and non-cached paths, even if it means some cloning overhead.
📚 Learning: 2025-04-05T10:21:42.020Z
Learnt from: Mettwasser
Repo: WFCD/warframe.rs PR: 24
File: src/worldstate/models/global_upgrades.rs:3-4
Timestamp: 2025-04-05T10:21:42.020Z
Learning: In the warframe.rs codebase, the `#[model(..., timed)]` attribute macro automatically adds `activation` and `expiry` fields to structs and implements the `TimedEvent` trait. These fields are not visible in the source code but are added during compilation.

Applied to files:

  • src/worldstate/models/items/warframe.rs
  • Cargo.toml
📚 Learning: 2025-04-17T14:00:48.579Z
Learnt from: Mettwasser
Repo: WFCD/warframe.rs PR: 24
File: src/market/client.rs:127-143
Timestamp: 2025-04-17T14:00:48.579Z
Learning: In the Warframe.rs codebase, the caching implementation in the market client is designed to return the direct value type (T) rather than an Arc<T> to maintain consistent API interfaces between cached and non-cached paths, even if it means some cloning overhead.

Applied to files:

  • Cargo.toml
🧬 Code graph analysis (3)
src/worldstate/listener_nested.rs (1)
src/worldstate/listener.rs (1)
  • ignore_state (8-13)
src/worldstate/listener.rs (3)
src/worldstate/listener_nested.rs (1)
  • ignore_state (10-15)
src/market/client.rs (1)
  • item (141-141)
src/worldstate/client.rs (1)
  • any (139-139)
src/worldstate/client.rs (3)
src/market/client.rs (4)
  • items (456-482)
  • items (492-497)
  • new (121-125)
  • item (141-141)
src/worldstate/models/base.rs (2)
  • query (80-92)
  • query_with_language (97-110)
src/worldstate/models/mod.rs (1)
  • query (94-96)
🔇 Additional comments (20)
src/lib.rs (1)

1-1: LGTM!

The doc_cfg feature gate is correctly configured for docs.rs builds, enabling proper documentation of feature-gated items. This aligns well with the corresponding [package.metadata.docs.rs] configuration in Cargo.toml.

Cargo.toml (1)

13-15: Docs.rs configuration looks correct.

The all-features = true ensures all feature-gated APIs are documented, and --cfg docsrs works in conjunction with the cfg_attr(docsrs, ...) in src/lib.rs.

src/worldstate/models/items/warframe.rs (2)

20-20: Breaking change: aura is now optional.

This prevents deserialization failures when the aura field is absent in the API response. Note that this is a breaking change for consumers who previously relied on aura being a non-optional String.


83-85: Breaking change: Wikia fields are now optional.

Making wikia_thumbnail and wikia_url optional improves resilience against missing API data. This is a breaking change for existing consumers.

src/worldstate/mod.rs (1)

33-34: Good modularization of listener logic.

Extracting listener functionality into dedicated private modules improves code organization and separation of concerns. The modules extend Client through impl blocks without altering the public API surface.

src/worldstate/models/base.rs (1)

74-76: Breaking change: Stricter trait bounds on Queryable.

The added bounds (Clone + 'static on the trait, Send + Sync + Clone + 'static on Return) are necessary to support the new caching layer and async listener functionality. This is a breaking change for downstream implementations that don't satisfy these bounds.

The bounds are appropriate:

  • Clone enables cache value returns without Arc overhead (consistent with the codebase design per learnings).
  • Send + Sync enables async task usage.
  • 'static enables type-erased storage in the cache.
src/worldstate/listener.rs (3)

8-13: LGTM!

The ignore_state helper cleanly adapts a stateless callback to the stateful signature, avoiding code duplication in the public API methods.


43-49: LGTM!

The public API is well-documented with a clear example. The delegation to call_on_update_inner with the ignore_state wrapper is a clean pattern.


129-131: Verify comparison operator intent.

The condition item.expiry() >= new_item.expiry() continues without invoking the callback when expiries are equal. Confirm this is intentional—if items can have the same expiry but different content, changes might be missed.

src/worldstate/listener_nested.rs (3)

10-15: ignore_state helper is appropriately duplicated.

While similar to the helper in listener.rs, the different callback signature (&T, Change vs &T, &T) justifies having separate implementations. The naming consistency is good.


54-61: LGTM!

The public API is well-documented with a clear example demonstrating the Change::Added and Change::Removed variants.


117-165: Solid nested listener implementation.

The diff-based approach using CrossDiff is appropriate for tracking additions and removals in Vec<T> types. Unlike the regular listener, this implementation avoids potential panics by not converting TimeDelta to std::Duration.

One consideration: if self.fetch::<T>().await fails, the error propagates and terminates the listener. You may want to add retry logic or logging for transient failures to improve resilience.

warframe-macros/Cargo.toml (1)

19-21: Dependency version bumps look reasonable.

The updates to proc-macro2, quote, and syn are all patch versions to the latest releases, which should be backward compatible.

src/worldstate/client.rs (7)

53-54: LGTM!

The type aliases are well-defined. Using TypeId as part of the cache key for TypeCache provides type safety, and Box<str> for ItemCache is memory-efficient for owned strings.


83-88: LGTM!

Field visibility is appropriately scoped: pub(crate) for fields needed by listener modules (http, base_url, config), and private for implementation details (type_cache, items_cache).


99-104: Reasonable cache TTL choices.

The cache TTLs are well-chosen: 5 minutes for frequently-changing worldstate data, and 12 hours for more stable item data.


112-126: LGTM!

The updated constructor signature provides flexibility for advanced users to supply custom caches and configuration. This is appropriately documented as a breaking change in the PR description.


181-183: LGTM!

The refactored fetch() and fetch_using_lang() methods correctly delegate to type_cached() with appropriate language parameters and fallback functions.

Also applies to: 214-218


273-280: LGTM!

Both query_item() and query_item_using_lang() correctly delegate to cached_item() and properly use urlencoding::encode() for URL-safe query parameter encoding.

Also applies to: 313-320


128-152: Verify AsyncFn trait requirement and edition configuration.

The AsyncFn trait bound (line 131) and edition "2024" setting indicate experimental or future Rust features. Confirm these are supported in your target Rust version and that the project builds successfully.

The cloning pattern (lines 139, 148) intentionally returns T::Return directly rather than Arc<T::Return> to maintain consistent API interfaces between cached and non-cached code paths. This is the intended design.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
README.md (2)

40-40: Fix grammar: "are linting" → "are linted".

Line 40 uses present progressive tense incorrectly. The statement should use passive voice: "Commit messages are linted in the PR."

🔎 Proposed fix
-Commit messages are linting in the PR
+Commit messages are linted in the PR

15-32: Fix grammar on line 40: change "are linting" to "are linted".

The example code imports are all correctly exported from the public API. However, line 40 contains a grammar error in the commitlint section: "Commit messages are linting in the PR" should use the passive voice "are linted" instead of the present progressive "are linting".

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d1d154 and 071b96e.

📒 Files selected for processing (1)
  • README.md
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-04-05T10:21:42.020Z
Learnt from: Mettwasser
Repo: WFCD/warframe.rs PR: 24
File: src/worldstate/models/global_upgrades.rs:3-4
Timestamp: 2025-04-05T10:21:42.020Z
Learning: In the warframe.rs codebase, the `#[model(..., timed)]` attribute macro automatically adds `activation` and `expiry` fields to structs and implements the `TimedEvent` trait. These fields are not visible in the source code but are added during compilation.

Applied to files:

  • README.md
📚 Learning: 2025-04-17T14:00:48.579Z
Learnt from: Mettwasser
Repo: WFCD/warframe.rs PR: 24
File: src/market/client.rs:127-143
Timestamp: 2025-04-17T14:00:48.579Z
Learning: In the Warframe.rs codebase, the caching implementation in the market client is designed to return the direct value type (T) rather than an Arc<T> to maintain consistent API interfaces between cached and non-cached paths, even if it means some cloning overhead.

Applied to files:

  • README.md
🔇 Additional comments (1)
README.md (1)

8-11: Documentation improvements look good.

The installation instruction, MSRV note update to 1.91, and readability enhancements align well with the PR's dependency upgrades and are clearly presented.

@Mettwasser Mettwasser enabled auto-merge (rebase) December 29, 2025 17:12
@Mettwasser Mettwasser self-assigned this Dec 30, 2025
@Mettwasser Mettwasser requested a review from CraigEge December 30, 2025 11:41
BREAKING CHANGE: call_on_update now returns a new error type
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/worldstate/listener.rs (2)

58-64: Consider removing unnecessary clippy allow attributes.

The #[allow(clippy::missing_errors_doc)] may be unnecessary since the method signature clearly returns Result<(), ListenerError> with proper error handling. The missing_panics_doc suppression also seems unneeded given the error handling at lines 156-158 prevents panics.

🔎 Suggested change
-    #[allow(clippy::missing_panics_doc, clippy::missing_errors_doc)]
     pub async fn call_on_update<T, Callback>(&self, callback: Callback) -> Result<(), ListenerError>

104-115: Consider removing unnecessary clippy allow attributes.

Same as with call_on_update, these allow attributes may be unnecessary given the proper error handling in place.

🔎 Suggested change
-    #[allow(clippy::missing_panics_doc, clippy::missing_errors_doc)]
     pub async fn call_on_update_with_state<S, T, Callback>(
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 071b96e and cd4e587.

📒 Files selected for processing (1)
  • src/worldstate/listener.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-29T16:49:08.409Z
Learnt from: Mettwasser
Repo: WFCD/warframe.rs PR: 31
File: src/worldstate/client.rs:46-47
Timestamp: 2025-12-29T16:49:08.409Z
Learning: When reviewing Rust code, note that std::time::Duration provides from_mins and from_hours since Rust 1.53.0. Do not flag these as non-existent or as custom extensions; treat them as standard library constructors and verify usage accordingly. This applies to all Rust source files, including src/worldstate/client.rs.

Applied to files:

  • src/worldstate/listener.rs
🧬 Code graph analysis (1)
src/worldstate/listener.rs (1)
src/worldstate/listener_nested.rs (1)
  • ignore_state (10-15)
🔇 Additional comments (4)
src/worldstate/listener.rs (4)

10-15: LGTM!

The ignore_state helper correctly adapts a non-stateful callback to the stateful signature required by the inner implementation.


17-29: LGTM!

The error type is well-designed with clear documentation. Good defensive programming to handle the FailedToConvertTime case even though it's theoretically impossible.


154-158: LGTM: Proper error handling for time conversion.

The time conversion is now properly handled with error propagation instead of unwrapping, addressing potential issues when chrono_time_to_sleep could be negative. This is good defensive programming.


134-152: LGTM: Buffer sleep provides rate limiting.

The logic correctly handles the case where items have expired or updated. The buffer sleep at line 143 (self.config.listener_sleep_timeout) provides rate limiting to prevent tight loops if the API returns expired items repeatedly.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
src/worldstate/listener.rs (2)

54-54: Fix documentation typo (previously flagged).

The comment has an extra 's': "don't forget to start it as a bg task (or .await it)s" should be "don't forget to start it as a bg task (or .await it)".

🔎 Suggested fix
-    ///     client.call_on_update(on_cetus_update); // don't forget to start it as a bg task (or .await it)s
+    ///     client.call_on_update(on_cetus_update); // don't forget to start it as a bg task (or .await it)

96-96: Replace unprofessional content in documentation example (previously flagged).

The example code contains inappropriate content that should be replaced with professional placeholder text.

🔎 Suggested fix
-    ///         _s: "My ginormous ass".into(),
+    ///         _s: "Example string".into(),
🧹 Nitpick comments (2)
src/worldstate/listener.rs (2)

17-29: Error handling is sound, minor documentation note.

The error variants are well-defined. Note that while the comment states FailedToConvertTime "should in theory never happen," it can occur in edge cases like clock skew, very short-lived items, or if the initial fetch returns an already-expired item. The error handling is correct and defensive.


31-65: Consider showing complete example usage.

The examples at lines 54 and 100 show method calls without demonstrating how to actually spawn them as background tasks or await them. While the comments do mention this requirement, showing a complete example would be clearer:

// Either spawn as background task:
tokio::spawn(client.call_on_update(on_cetus_update));

// Or await in an async context:
client.call_on_update(on_cetus_update).await?;

Also applies to: 67-116

📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd4e587 and f50f46a.

📒 Files selected for processing (1)
  • src/worldstate/listener.rs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-29T16:49:08.409Z
Learnt from: Mettwasser
Repo: WFCD/warframe.rs PR: 31
File: src/worldstate/client.rs:46-47
Timestamp: 2025-12-29T16:49:08.409Z
Learning: When reviewing Rust code, note that std::time::Duration provides from_mins and from_hours since Rust 1.53.0. Do not flag these as non-existent or as custom extensions; treat them as standard library constructors and verify usage accordingly. This applies to all Rust source files, including src/worldstate/client.rs.

Applied to files:

  • src/worldstate/listener.rs
🧬 Code graph analysis (1)
src/worldstate/listener.rs (1)
src/worldstate/listener_nested.rs (1)
  • ignore_state (10-15)
🔇 Additional comments (2)
src/worldstate/listener.rs (2)

10-15: LGTM: Clean adapter pattern.

The ignore_state helper correctly adapts stateless callbacks to the stateful inner implementation by ignoring the unit parameter.


118-168: LGTM: Core listener logic is well-structured and handles edge cases appropriately.

The implementation correctly handles the listener lifecycle:

  • Buffer sleep (line 143) prevents API hammering after expiry
  • The retry loop (line 148) handles cases where the API hasn't updated yet
  • Proper error handling for time conversion (lines 156-158) catches edge cases like clock skew or very short-lived items
  • Debug logging provides good observability

The previous concern about panic on line 144 has been addressed—the code now uses proper error propagation at lines 156-158 rather than unwrap().

Copy link
Collaborator

@CraigEge CraigEge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after recent fixes

@Mettwasser Mettwasser disabled auto-merge December 31, 2025 00:22
@Mettwasser Mettwasser merged commit 80e4583 into master Dec 31, 2025
4 checks passed
@Mettwasser Mettwasser deleted the worldstate-changes branch December 31, 2025 00:22
@wfcd-bot-boi
Copy link
Collaborator

🎉 This PR is included in version 9.0.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants