refactor(bot): split storage.rs into storage/{memory,redis,sqlite,mod}#47
Merged
BlindMaster24 merged 1 commit intomainfrom Apr 24, 2026
Merged
Conversation
bot/storage.rs had grown to 417 lines holding three unrelated concerns
behind a single file:
* the always-on StateStore trait with its default method
implementations (get/set/remove plus exists/ttl/keys/remove_prefix/
get_many/set_many).
* the always-on MemoryStateStore (HashMap + Entry with Instant-based
TTL).
* two feature-gated backends: RedisStateStore behind 'bot-redis'
(redis::Commands, SCAN/SETEX/pipeline) and SqliteStateStore behind
'bot-sqlite' (rusqlite with an expires_at column).
Split along those natural boundaries per the AGENTS.md guideline
("split files when a module grows beyond ~400-600 lines or mixes
multiple responsibilities"):
* storage/mod.rs - the StateStore trait with its default impls, the
module wiring, and the public re-exports.
* storage/memory.rs - Entry + MemoryStateStore + its trait impl.
* storage/redis_store.rs - RedisStateStore + its trait impl, gated
behind 'bot-redis'. Named *_store to avoid colliding with the
redis crate name inside the module namespace.
* storage/sqlite_store.rs - SqliteStateStore + its trait impl, gated
behind 'bot-sqlite'. Same *_store suffix for symmetry.
External call sites are unchanged. bot/mod.rs kept its re-exports
(pub use storage::{MemoryStateStore, StateStore}; feature-gated
pub use storage::RedisStateStore; pub use storage::SqliteStateStore)
exactly as they were, so bot::{MemoryStateStore, StateStore,
RedisStateStore, SqliteStateStore} continue to resolve and the
public surface is bit-for-bit identical.
Structural only, no semantic change:
* Trait method bodies for all three impls are byte-for-byte identical
to the pre-split file.
* TTL handling identical (Instant+Entry for memory, SETEX for Redis,
datetime('now') + expires_at column for SQLite).
* Feature gates identical - trait + memory always on, redis behind
bot-redis, sqlite behind bot-sqlite.
* MemoryStateStore and the StateStore trait gained brief doc comments
on individual methods (required for missing_docs = "deny"); no
behavioral effect.
Local verification:
* cargo fmt --all --check clean
* cargo clippy --workspace --all-targets --all-features -- -D warnings clean
* cargo test --workspace --all-features -> 287 passed / 0 failed
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
This was referenced Apr 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
bot/storage.rshad grown to 417 lines holding three unrelatedconcerns behind a single file:
StateStoretrait with its default methodimplementations (
get/set/removeplusexists/ttl/keys/remove_prefix/get_many/set_many);MemoryStateStore(HashMap+EntrywithInstant-based TTL);RedisStateStorebehindbot-redis(
redis::Commands,SCAN/SETEX/Pipeline) andSqliteStateStorebehind
bot-sqlite(rusqlitewith anexpires_atcolumn).Split along those natural boundaries per the AGENTS.md guideline
("split files when a module grows beyond ~400-600 lines or mixes
multiple responsibilities"):
storage/mod.rs- theStateStoretrait with its default impls,the module wiring, and the public re-exports (
MemoryStateStorealways,
RedisStateStoregated bybot-redis,SqliteStateStoregated by
bot-sqlite).storage/memory.rs-Entry+MemoryStateStore+ its trait impl.storage/redis_store.rs-RedisStateStore+ its trait impl,gated behind
bot-redis. Named*_storeto avoid colliding withthe
rediscrate name inside the module namespace.storage/sqlite_store.rs-SqliteStateStore+ its trait impl,gated behind
bot-sqlite. Same*_storesuffix for symmetry.External call sites are unchanged
bot/mod.rskept its re-exports exactly as they were:So
bot::{MemoryStateStore, StateStore, RedisStateStore, SqliteStateStore}continue to resolve and the public surface isbit-for-bit identical. The 9 call sites across the bot (
runtime.rs,runtime_async.rs,scheduler.rs,scheduler_async.rs,fsm.rs,context.rs,app.rs,router/dispatch.rs,lib.rs) compileunchanged.
Structural only, no semantic change
to the pre-split file.
Instant+Entryfor memory,SETEXforRedis,
datetime('now')+expires_atcolumn for SQLite.bot-redis, SQLite behindbot-sqlite.MemoryStateStore::newand theStateStoretrait gained brief doccomments on individual methods (required by
missing_docs = "deny"now that they are in their own file); no behavioral effect.
Local verification:
cargo fmt --all --checkcleancargo clippy --workspace --all-targets --all-features -- -D warningscleancargo test --workspace --all-features-> 287 passed / 0 failedReview & Testing Checklist for Human
storage/mod.rsre-exports:bot::MemoryStateStore,bot::StateStore, and the feature-gatedbot::RedisStateStore/
bot::SqliteStateStoremust all still resolve from downstreamcrates.
grep -rn "::MemoryStateStore\|::StateStore\|::RedisStateStore\|::SqliteStateStore" crates/teamtalkreproduces the full call surface.
*_storesuffix on the Redis and SQLite modulefiles is acceptable (the alternative is
mod redis;/mod sqlite;which shadows the external crate names insidethe bot namespace - a wart I'd rather avoid).
#[cfg(feature = "bot-redis")]and#[cfg(feature = "bot-sqlite")]still correctly gate thebackend modules (both at the
moddeclaration instorage/mod.rsand at thepub useline).Notes
This is the next PR in the P0 structural refactor queue after #44
(dedupe
can_issue_logged_in_command), #45 (extractpoll_command_completion), and #46 (splitclient/bus.rs). Next up:bot/fsm.rs(559 lines), thenclient/hooks/builders.rs(574 lines),types/entities/media_common.rs(458 lines), and theclient/backend.rs(1334 lines) +client/backend_mock.rs(1101lines) pair.
Branches from clean
main; I'm queueing all of these as independentPRs against
mainso you can merge them in whatever order suits. Ifany touch the same file I'll rebase the later one once its predecessor
lands.
The pre-existing
semverCI gate is expected to remain red;release-plzhandles the eventual version bump. Every other check isexpected to pass.
Link to Devin session: https://app.devin.ai/sessions/71fdd6196cb74723a2e277bb81993a9c
Requested by: @BlindMaster24