Skip to content

decrating: SPLIT shipper-storage — types to shipper-types, backend to shipper::ops::storage#68

Merged
EffortlessSteven merged 2 commits into
mainfrom
feature/decrating-split-storage
Apr 15, 2026
Merged

decrating: SPLIT shipper-storage — types to shipper-types, backend to shipper::ops::storage#68
EffortlessSteven merged 2 commits into
mainfrom
feature/decrating-split-storage

Conversation

@EffortlessSteven

Copy link
Copy Markdown
Member

Summary

Per docs/decrating-plan.md §3.2, shipper-storage is split by concern rather than absorbed wholesale. The crate currently only implements filesystem storage (S3/GCS/Azure bail with "not yet implemented") — not a finished public product yet. Splitting keeps the stable contract surface in shipper-types and the half-finished backend behind shipper's internals.

Stacks on #53 (env+storage+git dual-impl cleanup intermediate).

What moved where

Config/data types → shipper-types::storage (stable public contract)

  • StorageType (enum File | S3 | Gcs | Azure)
  • CloudStorageConfig and all its builders
  • ParseStorageTypeError, ValidateStorageConfigError (plain error structs — no anyhow dep in shipper-types)

Runtime backend code → shipper/src/ops/storage/ (crate-private pub(crate))

  • StorageBackend trait
  • FileStorage filesystem impl (atomic temp-file + rename)
  • S3/GCS/Azure stubs that bail with "not yet implemented"
  • build_storage_backend(cfg) factory
  • config_from_env() env-var parser

Introduces shipper::ops as the crate-private operational layer for internals not yet ready for public API.

Callers updated

  • shipper-config::StorageConfigInner now uses shipper_types::storage::{CloudStorageConfig, StorageType}.
  • shipper-config's [dependencies] drops shipper-storage.
  • shipper's [dependencies] drops shipper-storage.
  • Workspace members list drops crates/shipper-storage.
  • docs/architecture.md and RELEASE_CHECKLIST_v0.3.0.md stop listing shipper-storage.

Why StorageBackend is not public

  • Filesystem impl is mature, cloud impls are stubs.
  • Promising a public trait via crates.io would freeze a half-finished design.
  • Keeping it pub(crate) lets us evolve the trait as cloud backends are implemented. When they are real, the module can be promoted to public or extracted into a new crate.

LOC delta

  • Added shipper-types/src/storage/mod.rs: 554 LOC (types, tests, proptests, snapshot tests).
  • Added shipper/src/ops/mod.rs + shipper/src/ops/storage/mod.rs: 600 LOC (trait, filesystem impl, stubs, tests).
  • Deleted shipper-storage/ crate: ~2040 LOC total (src/lib.rs 1664, tests/storage_integration.rs 375, Cargo.toml/README).

Snapshots were renamed (insta picks up the new module path) — git diff -M shows most as renames.

Test plan

  • cargo check --workspace passes.
  • cargo clippy --workspace --all-targets --all-features -- -D warnings passes.
  • cargo fmt --all -- --check passes.
  • cargo test --workspace passes (all suites green, including the new 187-test shipper-types run and the 28-test shipper::ops::storage suite).
  • Snapshot files renamed/regenerated via cargo insta accept and committed.

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Apr 15, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@EffortlessSteven has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 25 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 0 minutes and 25 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: efd35593-6f23-4848-87a6-d6c58cd0aa35

📥 Commits

Reviewing files that changed from the base of the PR and between f3bfa03 and 784ff28.

⛔ Files ignored due to path filters (41)
  • Cargo.lock is excluded by !**/*.lock
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__atomic_write_roundtrip_files.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__atomic_write_roundtrip_state.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__build_backend_file_type.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__build_backend_unimplemented_errors.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__cloud_config_validate_file_always_ok.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__copied_content.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__file_state.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__file_storage_copy_and_mv@copied_content.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__file_storage_copy_and_mv@file_state.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__file_storage_copy_and_mv@moved_content.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__file_storage_exists_snapshot.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__file_storage_list_empty_dir.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__file_storage_list_sorted.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__file_storage_metadata.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__file_storage_read_missing_error.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__file_storage_write_read_snapshot.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__json_output.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__moved_content.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__parsed_back.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__snapshot_debug_file_storage.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__snapshot_delete_lifecycle.snap is excluded by !**/*.snap
  • crates/shipper-storage/src/snapshots/shipper_storage__snapshot_tests__storage_type_from_str_error.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__cloud_config_azure.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__cloud_config_default.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__cloud_config_full_path_variants.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__cloud_config_gcs.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__cloud_config_minimal_file.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__cloud_config_s3_full.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__cloud_config_validate_errors.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__cloud_config_validate_file_always_ok.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__json_output.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__parsed_back.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__snapshot_debug_cloud_config_all_options.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__snapshot_debug_cloud_config_defaults.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__snapshot_debug_storage_type_all.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__storage_type_default_snap.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__storage_type_display_all.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__storage_type_from_str_aliases.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__storage_type_from_str_error.snap is excluded by !**/*.snap
  • crates/shipper-types/src/storage/snapshots/shipper_types__storage__snapshot_tests__storage_type_serde_roundtrip.snap is excluded by !**/*.snap
📒 Files selected for processing (20)
  • Cargo.toml
  • RELEASE_CHECKLIST_v0.3.0.md
  • crates/shipper-config/Cargo.toml
  • crates/shipper-config/src/lib.rs
  • crates/shipper-storage/CLAUDE.md
  • crates/shipper-storage/Cargo.toml
  • crates/shipper-storage/README.md
  • crates/shipper-storage/proptest-regressions/lib.txt
  • crates/shipper-storage/src/lib.rs
  • crates/shipper-storage/tests/storage_integration.rs
  • crates/shipper-types/src/lib.rs
  • crates/shipper-types/src/storage/CLAUDE.md
  • crates/shipper-types/src/storage/mod.rs
  • crates/shipper/Cargo.toml
  • crates/shipper/src/lib.rs
  • crates/shipper/src/ops/mod.rs
  • crates/shipper/src/ops/storage/CLAUDE.md
  • crates/shipper/src/ops/storage/mod.rs
  • crates/shipper/src/storage.rs
  • docs/architecture.md
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/decrating-split-storage

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.

…age, backend → shipper::ops::storage

Per docs/decrating-plan.md §3.2, shipper-storage is split by concern
rather than absorbed wholesale. The crate currently only implements
filesystem storage (S3/GCS/Azure bail out as "not yet implemented") —
not a finished public product yet.

This PR:
- Moves StorageType, CloudStorageConfig, and their error types into
  shipper-types::storage (the stable contract surface that embedders
  use to declare their storage choice). anyhow is avoided here — the
  new ParseStorageTypeError and ValidateStorageConfigError keep the
  types crate free of anyhow.
- Moves StorageBackend trait, FileStorage impl, cloud-backend stubs,
  build_storage_backend factory, and config_from_env env-parsing into
  shipper/src/ops/storage/ as crate-private (pub(crate)). We do not
  promise the StorageBackend trait publicly until cloud backends are
  real.
- Introduces shipper::ops as the crate-private operational layer for
  future internals that should not yet be on the public API surface.
- Updates shipper-config to import CloudStorageConfig/StorageType from
  shipper_types::storage and drops its shipper-storage dependency.
- Removes the shipper-storage crate from the workspace and deletes it.
- Updates docs/architecture.md and RELEASE_CHECKLIST_v0.3.0.md to drop
  shipper-storage references.

Stacks on PR #53 (env+storage+git dedup intermediate).

Per docs/decrating-plan.md §6 Phase 2 (storage SPLIT special case).
After cherry-picking PR #68's storage SPLIT commit onto main, `lib.rs`
ended up with two `pub(crate) mod ops;` declarations: the original from
main's ops/ layer introduction, and a new one added by PR #68 to house
ops::storage. Removed the duplicate.

Also preserved PR #66's per-call unique tmp-filename race fix in the new
FileStorage::write location at `crates/shipper/src/ops/storage/mod.rs`.
The pattern uses pid + thread-id + nanos suffix to prevent concurrent
writes to the same destination from racing on rename.
@EffortlessSteven EffortlessSteven force-pushed the feature/decrating-split-storage branch from ad5b1d6 to 784ff28 Compare April 15, 2026 07:37
@EffortlessSteven EffortlessSteven merged commit 6e39a8c into main Apr 15, 2026
8 of 15 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