Skip to main content

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}