Skip to content

refactor(bot): split storage.rs into storage/{memory,redis,sqlite,mod}#47

Merged
BlindMaster24 merged 1 commit intomainfrom
devin/1776939277-split-bot-storage
Apr 24, 2026
Merged

refactor(bot): split storage.rs into storage/{memory,redis,sqlite,mod}#47
BlindMaster24 merged 1 commit intomainfrom
devin/1776939277-split-bot-storage

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented Apr 23, 2026

Summary

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 (MemoryStateStore
    always, RedisStateStore gated by bot-redis, SqliteStateStore
    gated by bot-sqlite).
  • 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 exactly as they were:

pub use storage::{MemoryStateStore, StateStore};
#[cfg(feature = "bot-redis")]
pub use storage::RedisStateStore;
#[cfg(feature = "bot-sqlite")]
pub use storage::SqliteStateStore;

So bot::{MemoryStateStore, StateStore, RedisStateStore, SqliteStateStore} continue to resolve and the public surface is
bit-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) compile
unchanged.

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::new and the StateStore trait gained brief doc
    comments on individual methods (required by missing_docs = "deny"
    now that they are in their own file); 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

Review & Testing Checklist for Human

  • Spot-check storage/mod.rs re-exports: bot::MemoryStateStore,
    bot::StateStore, and the feature-gated bot::RedisStateStore
    / bot::SqliteStateStore must all still resolve from downstream
    crates. grep -rn "::MemoryStateStore\|::StateStore\|::RedisStateStore\|::SqliteStateStore" crates/teamtalk
    reproduces the full call surface.
  • Confirm the *_store suffix on the Redis and SQLite module
    files is acceptable (the alternative is mod redis; /
    mod sqlite; which shadows the external crate names inside
    the bot namespace - a wart I'd rather avoid).
  • Confirm #[cfg(feature = "bot-redis")] and
    #[cfg(feature = "bot-sqlite")] still correctly gate the
    backend modules (both at the mod declaration in
    storage/mod.rs and at the pub use line).

Notes

This is the next PR in the P0 structural refactor queue after #44
(dedupe can_issue_logged_in_command), #45 (extract
poll_command_completion), and #46 (split client/bus.rs). Next up:
bot/fsm.rs (559 lines), then client/hooks/builders.rs (574 lines),
types/entities/media_common.rs (458 lines), and the
client/backend.rs (1334 lines) + client/backend_mock.rs (1101
lines) pair.

Branches from clean main; I'm queueing all of these as independent
PRs against main so you can merge them in whatever order suits. If
any touch the same file I'll rebase the later one once its predecessor
lands.

The pre-existing semver CI gate is expected to remain red;
release-plz handles the eventual version bump. Every other check is
expected to pass.

Link to Devin session: https://app.devin.ai/sessions/71fdd6196cb74723a2e277bb81993a9c
Requested by: @BlindMaster24


Open in Devin Review

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
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Contributor Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

@BlindMaster24 BlindMaster24 merged commit 1ca4bfc into main Apr 24, 2026
11 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant