pub struct MediaManager { /* private fields */ }Expand description
Glue between the Media model and a StorageRegistry. Cheap
to clone — internal state is Arc-shared.
v0.38 — tri-dialect. Every query method dispatches per backend
(PG / MySQL 8+ / SQLite) via the unified crate::sql::Pool enum.
PG-specific idioms (ANY($1), NOW() - INTERVAL,
DELETE … USING, ON CONFLICT DO UPDATE, INSERT … RETURNING)
are rewritten as portable equivalents (pre-computed timestamps
bound from Rust, IN (?, ?, …) expansions, subquery rewrites,
ON DUPLICATE KEY UPDATE on MySQL, SELECT … LAST_INSERT_ID()
in a transaction on MySQL).
Implementations§
Source§impl MediaManager
impl MediaManager
Sourcepub fn new(pool: PgPool, registry: StorageRegistry) -> Self
pub fn new(pool: PgPool, registry: StorageRegistry) -> Self
PG back-compat constructor.
Sourcepub fn new_pool(pool: impl Into<Pool>, registry: StorageRegistry) -> Self
pub fn new_pool(pool: impl Into<Pool>, registry: StorageRegistry) -> Self
Tri-dialect constructor (v0.38).
pub fn registry(&self) -> &StorageRegistry
Sourcepub fn pool(&self) -> &PgPool
pub fn pool(&self) -> &PgPool
PG back-compat accessor. Panics if the manager wraps a non-PG
pool — prefer Self::pool_dyn for tri-dialect code.
Sourcepub fn pool_dyn(&self) -> &Pool
pub fn pool_dyn(&self) -> &Pool
Tri-dialect accessor — the unified crate::sql::Pool enum.
Sourcepub async fn save_bytes(&self, opts: SaveOpts) -> Result<Media, MediaError>
pub async fn save_bytes(&self, opts: SaveOpts) -> Result<Media, MediaError>
Write opts.bytes to the storage backend, then insert a
Media row in the Ready state. Returns the inserted row.
§Errors
UnknownDisk if the disk isn’t registered, Storage for any
upload failure, Db for the row insert.
Sourcepub async fn begin_upload(
&self,
intent: UploadIntent,
) -> Result<UploadTicket, MediaError>
pub async fn begin_upload( &self, intent: UploadIntent, ) -> Result<UploadTicket, MediaError>
Issue a presigned PUT URL for direct browser upload, and
pre-create a Media row in Pending state. The browser PUTs
directly to S3 (or compatible); the server later calls
Self::finalize_upload to verify the object landed and
flip the row to Ready.
§Errors
UnknownDisk / Db / a Storage error if the backend doesn’t
support presigned URLs.
Sourcepub async fn finalize_upload(&self, media_id: i64) -> Result<Media, MediaError>
pub async fn finalize_upload(&self, media_id: i64) -> Result<Media, MediaError>
Confirm the storage object exists for media_id and flip
the row from Pending to Ready. If the object isn’t there,
flip to Failed instead so a purge sweep can clean it up.
Returns the (possibly-updated) row regardless.
§Errors
Db if the row doesn’t exist or the update fails. Storage
for transport failures during the exists check.
Sourcepub async fn get(&self, id: i64) -> Result<Option<Media>, MediaError>
pub async fn get(&self, id: i64) -> Result<Option<Media>, MediaError>
Fetch by id. Soft-deleted rows are excluded; pass through to
Self::get_including_deleted when you want them.
Sourcepub async fn get_including_deleted(
&self,
id: i64,
) -> Result<Option<Media>, MediaError>
pub async fn get_including_deleted( &self, id: i64, ) -> Result<Option<Media>, MediaError>
Like Self::get but returns soft-deleted rows too. Use
from admin / restore flows.
Sourcepub fn url(&self, m: &Media) -> Option<String>
pub fn url(&self, m: &Media) -> Option<String>
CDN-aware URL for m. Returns None when neither the disk’s
CDN prefix nor the backend’s public URL is available.
Sourcepub fn origin_url(&self, m: &Media) -> Option<String>
pub fn origin_url(&self, m: &Media) -> Option<String>
Bare backend URL (no CDN). For internal admin / debug.
Sourcepub async fn presigned_get(&self, m: &Media, ttl: Duration) -> Option<String>
pub async fn presigned_get(&self, m: &Media, ttl: Duration) -> Option<String>
Time-limited download link suitable for <a href=...>.
Returns None when the disk’s backend can’t sign.
Sourcepub async fn load_bytes(&self, m: &Media) -> Result<Vec<u8>, MediaError>
pub async fn load_bytes(&self, m: &Media) -> Result<Vec<u8>, MediaError>
Read the file bytes server-side.
Sourcepub async fn delete(&self, m: &Media) -> Result<(), MediaError>
pub async fn delete(&self, m: &Media) -> Result<(), MediaError>
Soft-delete: mark deleted_at = NOW(). The storage object
stays put — purge it later via Self::purge or wait for
the purge_orphans sweep.
Sourcepub async fn purge(&self, m: &Media) -> Result<(), MediaError>
pub async fn purge(&self, m: &Media) -> Result<(), MediaError>
Hard-delete: remove the storage object AND the row. Use for
“I’m sure I want this gone right now” — typically called by
the post_delete signal once delete() has soft-deleted.
Sourcepub async fn purge_orphans(
&self,
older_than: Duration,
) -> Result<u64, MediaError>
pub async fn purge_orphans( &self, older_than: Duration, ) -> Result<u64, MediaError>
Hard-delete every soft-deleted Media row older than
older_than, removing the storage object as we go. Returns
the count of rows purged.
Run from the crate::scheduler (e.g. nightly) to keep
orphan storage objects from accumulating.
Sourcepub async fn purge_pending(
&self,
older_than: Duration,
) -> Result<u64, MediaError>
pub async fn purge_pending( &self, older_than: Duration, ) -> Result<u64, MediaError>
Hard-delete every Media row stuck in Pending for longer
than older_than. Direct-browser-upload flows leave Pending
rows behind when the browser abandons before calling
finalize_upload; this sweep cleans them up.
Sourcepub async fn create_collection(
&self,
name: impl Into<String>,
slug: impl Into<String>,
parent: Option<i64>,
description: impl Into<String>,
) -> Result<MediaCollection, MediaError>
pub async fn create_collection( &self, name: impl Into<String>, slug: impl Into<String>, parent: Option<i64>, description: impl Into<String>, ) -> Result<MediaCollection, MediaError>
Create a new collection. slug must be unique. parent may be
None (root) or another collection’s id (sub-folder).
§Errors
Db for unique-constraint violations on slug or any other
underlying sqlx error.
Sourcepub async fn get_collection(
&self,
id: i64,
) -> Result<Option<MediaCollection>, MediaError>
pub async fn get_collection( &self, id: i64, ) -> Result<Option<MediaCollection>, MediaError>
Look up by id (excludes soft-deleted).
Sourcepub async fn get_collection_by_slug(
&self,
slug: &str,
) -> Result<Option<MediaCollection>, MediaError>
pub async fn get_collection_by_slug( &self, slug: &str, ) -> Result<Option<MediaCollection>, MediaError>
Look up by slug (excludes soft-deleted).
Sourcepub async fn list_collections(&self) -> Result<Vec<MediaCollection>, MediaError>
pub async fn list_collections(&self) -> Result<Vec<MediaCollection>, MediaError>
List every non-deleted collection, ordered by (parent_id, name)
so siblings group together — handy for tree-renderers.
Sourcepub async fn collection_path(&self, id: i64) -> Result<String, MediaError>
pub async fn collection_path(&self, id: i64) -> Result<String, MediaError>
Build the slug-joined path for a collection: "products/2026/launch".
Walks up the parent chain. Cycles raise Other.
Sourcepub async fn delete_collection(&self, id: i64) -> Result<(), MediaError>
pub async fn delete_collection(&self, id: i64) -> Result<(), MediaError>
Soft-delete a collection. Media inside it is NOT deleted —
rows are orphaned (collection_id set to NULL) so they remain
queryable and the storage objects survive.
Sourcepub async fn move_to_collection(
&self,
media_id: i64,
collection_id: Option<i64>,
) -> Result<(), MediaError>
pub async fn move_to_collection( &self, media_id: i64, collection_id: Option<i64>, ) -> Result<(), MediaError>
Move a Media into a collection (or None to set “loose”).
Sourcepub async fn list_in_collection(
&self,
collection_id: i64,
recursive: bool,
) -> Result<Vec<Media>, MediaError>
pub async fn list_in_collection( &self, collection_id: i64, recursive: bool, ) -> Result<Vec<Media>, MediaError>
List media in collection_id. When recursive, descends into
every nested collection.
Sourcepub async fn ensure_tag(&self, slug: &str) -> Result<MediaTag, MediaError>
pub async fn ensure_tag(&self, slug: &str) -> Result<MediaTag, MediaError>
Find or create a tag by slug (auto-derives name from
slug if creating).
Sourcepub async fn get_tag_by_slug(
&self,
slug: &str,
) -> Result<Option<MediaTag>, MediaError>
pub async fn get_tag_by_slug( &self, slug: &str, ) -> Result<Option<MediaTag>, MediaError>
Look up a tag by slug.
Sourcepub async fn tag(&self, media_id: i64, slugs: &[&str]) -> Result<(), MediaError>
pub async fn tag(&self, media_id: i64, slugs: &[&str]) -> Result<(), MediaError>
Apply tags to a media row. Auto-creates missing tags. Idempotent — duplicates ignored.
Sourcepub async fn untag(&self, media_id: i64, slug: &str) -> Result<(), MediaError>
pub async fn untag(&self, media_id: i64, slug: &str) -> Result<(), MediaError>
Remove a single tag from a media row.
Replace the entire tag set for a media row. Tags not in
slugs are removed; tags in slugs are added (auto-created
if needed).
List tags applied to a media row, alphabetically by slug.
Sourcepub async fn list_with_tag(
&self,
slug: &str,
limit: i64,
offset: i64,
) -> Result<Vec<Media>, MediaError>
pub async fn list_with_tag( &self, slug: &str, limit: i64, offset: i64, ) -> Result<Vec<Media>, MediaError>
List media that carry slug. Soft-deleted media excluded.
Top tags by usage count, descending. Limit clamped at 1000.
Trait Implementations§
Source§impl Clone for MediaManager
impl Clone for MediaManager
Source§fn clone(&self) -> MediaManager
fn clone(&self) -> MediaManager
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl Freeze for MediaManager
impl !RefUnwindSafe for MediaManager
impl Send for MediaManager
impl Sync for MediaManager
impl Unpin for MediaManager
impl UnsafeUnpin for MediaManager
impl !UnwindSafe for MediaManager
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more