dog_blob/lib.rs
1//! # dog-blob: Production-ready blob storage infrastructure
2//!
3//! `dog-blob` provides streaming-first, resumable, range-friendly blob storage for DogRS applications
4//! with zero boilerplate. It's designed to eliminate all the routine media handling code that
5//! services shouldn't have to write.
6//!
7//! ## Key Features
8//!
9//! - **Streaming-first**: Handle huge video files without buffering entire content in memory
10//! - **Multipart/resumable uploads**: Built-in coordination for large files with automatic fallback
11//! - **Range requests**: First-class support for video/audio scrubbing and partial content delivery
12//! - **Storage agnostic**: Works with any backend (S3, filesystem, memory, custom implementations)
13//! - **Server agnostic**: No HTTP coupling - works with any protocol (HTTP, gRPC, CLI, background jobs)
14//! - **Zero boilerplate**: Services focus on business logic, not media mechanics
15//!
16//! ## Quick Start
17//!
18//! ```rust
19//! use dog_blob::prelude::*;
20//! use tokio_util::io::ReaderStream;
21//! use std::io::Cursor;
22//!
23//! # #[tokio::main]
24//! # async fn main() -> BlobResult<()> {
25//! // 1. Create adapter with S3-compatible storage
26//! let store = dog_blob::S3CompatibleStore::from_env()?;
27//! let adapter = BlobAdapter::new(store, BlobConfig::default());
28//!
29//! // 2. Create context for your tenant/user
30//! let ctx = BlobCtx::new("my-app".to_string())
31//! .with_actor("user-123".to_string());
32//!
33//! // 3. Upload a file
34//! let data = b"Hello, world!";
35//! let stream = ReaderStream::new(Cursor::new(data));
36//! let put_request = BlobPut::new()
37//! .with_content_type("text/plain")
38//! .with_filename("hello.txt");
39//!
40//! let receipt = adapter.put(ctx.clone(), put_request, Box::pin(stream)).await?;
41//!
42//! // 4. Download with range support
43//! let opened = adapter.open(ctx, receipt.id, None).await?;
44//! # Ok(())
45//! # }
46//! ```
47//!
48//! ## Architecture
49//!
50//! dog-blob follows a clean separation of concerns:
51//!
52//! ```text
53//! ┌─────────────────┐
54//! │ Your Service │ ← Business logic only
55//! ├─────────────────┤
56//! │ BlobAdapter │ ← Media coordination
57//! ├─────────────────┤
58//! │ BlobStore │ ← Storage primitives
59//! └─────────────────┘
60//! ```
61//!
62//! The key insight: **BlobAdapter is infrastructure, not a service**. You embed it in your services:
63//!
64//! ```rust
65//! use dog_blob::prelude::*;
66//!
67//! pub struct MediaService {
68//! blobs: BlobAdapter, // This is all you need!
69//! }
70//!
71//! impl MediaService {
72//! pub async fn upload_photo(&self, tenant_id: String, data: Vec<u8>) -> BlobResult<String> {
73//! let ctx = BlobCtx::new(tenant_id);
74//! let stream = futures::stream::once(async { Ok(bytes::Bytes::from(data)) });
75//!
76//! // One line handles all the blob complexity
77//! let receipt = self.blobs.put(ctx, BlobPut::new(), Box::pin(stream)).await?;
78//!
79//! Ok(receipt.id.to_string())
80//! }
81//! }
82//! ```
83
84pub mod adapter;
85mod config;
86mod coordinator;
87mod error;
88mod receipt;
89mod s3_store;
90mod session_store;
91pub mod store;
92mod types;
93mod upload;
94
95
96
97// Re-export main types for clean API
98pub use adapter::BlobAdapter;
99pub use config::{BlobConfig, UploadRules};
100pub use coordinator::DefaultUploadCoordinator;
101pub use error::{BlobError, BlobResult};
102pub use receipt::{BlobReceipt, OpenedBlob, ResolvedRange};
103pub use s3_store::{S3CompatibleStore, S3Config};
104pub use store::{
105 BlobInfo, BlobMetadata, BlobStore, MultipartBlobStore, SignedUrlBlobStore, BlobKeyStrategy, DefaultKeyStrategy,
106 PutResult, GetResult, ObjectHead, StoreCapabilities
107};
108pub use types::{
109 BlobCtx, BlobId, BlobPut, ByteRange, ByteStream,
110 UploadId, UploadSession, UploadStatus, PartReceipt, UploadProgress,
111 ChunkSessionId, ChunkResult, ChunkSession
112};
113pub use upload::{UploadCoordinator, UploadIntent, UploadSessionStore};
114pub use session_store::MemoryUploadSessionStore;
115
116/// Prelude for convenient imports
117pub mod prelude {
118 pub use crate::{
119 BlobAdapter, BlobConfig, BlobError, BlobResult, BlobReceipt,
120 BlobStore, BlobCtx, BlobId, BlobPut, ByteStream
121 };
122}