backbone-bucket 0.4.3

Bucket Bounded Context: File Storage Module for Backbone Framework
Documentation
//! Bucket Module
//!
//! Generated by metaphor-schema. Enhanced with runtime implementations.
//!
//! This module provides:
//! - Domain entities and repositories
//! - Application services
//! - HTTP and gRPC handlers
//! - Route configuration
//! - State machine enforcement
//! - Validation rules runtime
//! - RBAC middleware
//! - Trigger execution system
//! - Computed fields
//! - Workflow orchestrator

#![recursion_limit = "1024"]
#![allow(unused_imports)]

// Generated modules
pub mod domain;
pub mod infrastructure;
pub mod application;
pub mod presentation;
pub mod seeders;
pub mod exports;

// The module's extension (hand-written; ADR-0031).
#[path = "lib.ext.rs"]
mod lib_ext;
pub use lib_ext::*;
/// This crate's module and builder under fixed names, for the extension's `impl` blocks.
#[allow(dead_code)]
pub(crate) type ThisModule = BucketModule;
#[allow(dead_code)]
pub(crate) type ThisModuleBuilder = BucketModuleBuilder;

// Re-exports for convenience - Domain entities
pub use domain::entity::*;

// Re-exports - State Machine
pub use domain::state_machine::*;

// Re-exports - Computed Fields
pub use domain::computed::{HasComputedFields, ComputedFieldValues};

// Re-exports - Permissions
pub use domain::permission::{Action, FieldRestriction, PermissionChecker, PermissionError, PermissionResult};

// Re-exports - Infrastructure
pub use infrastructure::persistence::*;

// Re-exports - Application services
pub use application::service::AccessLogService;
pub use application::service::BucketService;
pub use application::service::ContentHashService;
pub use application::service::ConversionJobService;
pub use application::service::FileCommentService;
pub use application::service::FileLockService;
pub use application::service::FileShareService;
pub use application::service::FileVersionService;
pub use application::service::ProcessingJobService;
pub use application::service::StoredFileService;
pub use application::service::ThumbnailService;
pub use application::service::UploadSessionService;
pub use application::service::UserQuotaService;

// Re-exports - Validation
pub use application::validator::{ValidationError, ValidationResult};

// Re-exports - Triggers
pub use application::triggers::*;

// Re-exports - Workflows
pub use application::workflows::*;

use std::sync::Arc;
use axum::Router;
use sqlx::PgPool;

/// Bucket module configuration
///
/// Use the builder pattern to configure and register this module:
///
/// ```text
/// let bucket = BucketModule::builder()
///     .with_database(pool.clone())
///     .build()?;
///
/// // Unguarded full CRUD (trusted/admin); compose a guarded router for production.
/// let router = bucket.all_crud_routes();
/// ```
pub struct BucketModule {
    #[allow(dead_code)]
    pub(crate) access_log_service: Arc<AccessLogService>,
    pub(crate) bucket_service: Arc<BucketService>,
    pub(crate) content_hash_service: Arc<ContentHashService>,
    pub(crate) conversion_job_service: Arc<ConversionJobService>,
    pub(crate) file_comment_service: Arc<FileCommentService>,
    pub(crate) file_lock_service: Arc<FileLockService>,
    pub(crate) file_share_service: Arc<FileShareService>,
    #[allow(dead_code)]
    pub(crate) file_version_service: Arc<FileVersionService>,
    pub(crate) processing_job_service: Arc<ProcessingJobService>,
    pub(crate) stored_file_service: Arc<StoredFileService>,
    #[allow(dead_code)]
    pub(crate) thumbnail_service: Arc<ThumbnailService>,
    pub(crate) upload_session_service: Arc<UploadSessionService>,
    pub(crate) user_quota_service: Arc<UserQuotaService>,
    /// The module's extension state (`lib.ext.rs`); its fields read through `Deref`.
    pub(crate) ext: ModuleExt,
}

impl std::ops::Deref for BucketModule {
    type Target = ModuleExt;
    fn deref(&self) -> &Self::Target {
        &self.ext
    }
}

impl std::ops::DerefMut for BucketModule {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.ext
    }
}

/// What the extension's `build` hook receives from the generated build (ADR-0031).
#[allow(dead_code)]
pub(crate) struct ModuleParts<'a> {
    pub(crate) db_pool: &'a PgPool,
    pub(crate) access_log_service: &'a Arc<AccessLogService>,
    pub(crate) access_log_repository: &'a Arc<AccessLogRepository>,
    pub(crate) bucket_service: &'a Arc<BucketService>,
    pub(crate) bucket_repository: &'a Arc<BucketRepository>,
    pub(crate) content_hash_service: &'a Arc<ContentHashService>,
    pub(crate) content_hash_repository: &'a Arc<ContentHashRepository>,
    pub(crate) conversion_job_service: &'a Arc<ConversionJobService>,
    pub(crate) conversion_job_repository: &'a Arc<ConversionJobRepository>,
    pub(crate) file_comment_service: &'a Arc<FileCommentService>,
    pub(crate) file_comment_repository: &'a Arc<FileCommentRepository>,
    pub(crate) file_lock_service: &'a Arc<FileLockService>,
    pub(crate) file_lock_repository: &'a Arc<FileLockRepository>,
    pub(crate) file_share_service: &'a Arc<FileShareService>,
    pub(crate) file_share_repository: &'a Arc<FileShareRepository>,
    pub(crate) file_version_service: &'a Arc<FileVersionService>,
    pub(crate) file_version_repository: &'a Arc<FileVersionRepository>,
    pub(crate) processing_job_service: &'a Arc<ProcessingJobService>,
    pub(crate) processing_job_repository: &'a Arc<ProcessingJobRepository>,
    pub(crate) stored_file_service: &'a Arc<StoredFileService>,
    pub(crate) stored_file_repository: &'a Arc<StoredFileRepository>,
    pub(crate) thumbnail_service: &'a Arc<ThumbnailService>,
    pub(crate) thumbnail_repository: &'a Arc<ThumbnailRepository>,
    pub(crate) upload_session_service: &'a Arc<UploadSessionService>,
    pub(crate) upload_session_repository: &'a Arc<UploadSessionRepository>,
    pub(crate) user_quota_service: &'a Arc<UserQuotaService>,
    pub(crate) user_quota_repository: &'a Arc<UserQuotaRepository>,
}

impl BucketModule {
    /// Create a new module builder
    pub fn builder() -> BucketModuleBuilder {
        BucketModuleBuilder::new()
    }

    /// Mount ALL generated CRUD endpoints (12 per entity) with NO domain
    /// validation — the fully **unguarded** surface. A well-formed request can
    /// create invalid rows or soft-delete a referenced master out from under its
    /// dependents. Prefer a guarded composition (read + validated writes) for any
    /// real deployment; use this only in trusted/admin/seeding contexts.
    pub fn all_crud_routes(&self) -> Router {
        use presentation::http::{
            create_bucket_routes,
            create_content_hash_routes,
            create_conversion_job_routes,
            create_file_comment_routes,
            create_file_lock_routes,
            create_file_share_routes,
            create_processing_job_routes,
            create_stored_file_routes,
            create_upload_session_routes,
            create_user_quota_routes,
        };

        Router::new()
            .merge(create_bucket_routes(self.bucket_service.clone()))
            .merge(create_content_hash_routes(self.content_hash_service.clone()))
            .merge(create_conversion_job_routes(self.conversion_job_service.clone()))
            .merge(create_file_comment_routes(self.file_comment_service.clone()))
            .merge(create_file_lock_routes(self.file_lock_service.clone()))
            .merge(create_file_share_routes(self.file_share_service.clone()))
            .merge(create_processing_job_routes(self.processing_job_service.clone()))
            .merge(create_stored_file_routes(self.stored_file_service.clone()))
            .merge(create_upload_session_routes(self.upload_session_service.clone()))
            .merge(create_user_quota_routes(self.user_quota_service.clone()))
    }

    /// Deprecated alias for [`Self::all_crud_routes`]. `routes()` reads like
    /// "the routes" but mounts UNVALIDATED generic CRUD on every entity — a naive
    /// mount exposes unguarded writes. Compose a guarded router (read + validated
    /// writes) for production, or call `all_crud_routes()` to opt into the full
    /// unguarded surface explicitly.
    #[deprecated(note = "mounts unvalidated generic CRUD; prefer readonly_routes() + validated writes, or all_crud_routes() for the full/unguarded surface")]
    pub fn routes(&self) -> Router {
        self.all_crud_routes()
    }

    /// Read-only routes for every entity (GET endpoints only) — the safe base.
    ///
    /// Generic mutation can't reach here, so this surface cannot bypass a
    /// validated write service's invariants. Use this as the production base and
    /// merge validated write routes (or a write service's HTTP layer) onto it.
    pub fn readonly_routes(&self) -> Router {
        use presentation::http::{
            create_bucket_read_routes,
            create_content_hash_read_routes,
            create_conversion_job_read_routes,
            create_file_comment_read_routes,
            create_file_lock_read_routes,
            create_file_share_read_routes,
            create_processing_job_read_routes,
            create_stored_file_read_routes,
            create_upload_session_read_routes,
            create_user_quota_read_routes,
        };

        Router::new()
            .merge(create_bucket_read_routes(self.bucket_service.clone()))
            .merge(create_content_hash_read_routes(self.content_hash_service.clone()))
            .merge(create_conversion_job_read_routes(self.conversion_job_service.clone()))
            .merge(create_file_comment_read_routes(self.file_comment_service.clone()))
            .merge(create_file_lock_read_routes(self.file_lock_service.clone()))
            .merge(create_file_share_read_routes(self.file_share_service.clone()))
            .merge(create_processing_job_read_routes(self.processing_job_service.clone()))
            .merge(create_stored_file_read_routes(self.stored_file_service.clone()))
            .merge(create_upload_session_read_routes(self.upload_session_service.clone()))
            .merge(create_user_quota_read_routes(self.user_quota_service.clone()))
    }

}

/// Builder for BucketModule
pub struct BucketModuleBuilder {
    db_pool: Option<PgPool>,
    /// The builder's extension state (`lib.ext.rs`); its fields read through `Deref`.
    ext: ModuleBuilderExt,
}

impl std::ops::Deref for BucketModuleBuilder {
    type Target = ModuleBuilderExt;
    fn deref(&self) -> &Self::Target {
        &self.ext
    }
}

impl std::ops::DerefMut for BucketModuleBuilder {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.ext
    }
}

impl BucketModuleBuilder {
    /// Create a new builder
    pub fn new() -> Self {
        Self {
            db_pool: None,
            ext: Default::default(),
        }
    }

    /// Set the database connection pool
    pub fn with_database(mut self, pool: PgPool) -> Self {
        self.db_pool = Some(pool);
        self
    }


    /// Build the module with configured dependencies
    pub fn build(self) -> anyhow::Result<BucketModule> {
        let db_pool = self.db_pool
            .ok_or_else(|| anyhow::anyhow!("Database pool not configured"))?;

        // AccessLog service
        let access_log_repository = Arc::new(AccessLogRepository::new(db_pool.clone()));
        let access_log_service = Arc::new(AccessLogService::with_repository(access_log_repository.clone()));

        // Bucket service
        let bucket_repository = Arc::new(BucketRepository::new(db_pool.clone()));
        let bucket_service = Arc::new(BucketService::with_repository(bucket_repository.clone()).with_guard(Arc::new(crate::application::service::BucketRowRules)));

        // ContentHash service
        let content_hash_repository = Arc::new(ContentHashRepository::new(db_pool.clone()));
        let content_hash_service = Arc::new(ContentHashService::with_repository(content_hash_repository.clone()));

        // ConversionJob service
        let conversion_job_repository = Arc::new(ConversionJobRepository::new(db_pool.clone()));
        let conversion_job_service = Arc::new(ConversionJobService::with_repository(conversion_job_repository.clone()).with_guard(Arc::new(crate::application::service::ConversionJobRowRules)));

        // FileComment service
        let file_comment_repository = Arc::new(FileCommentRepository::new(db_pool.clone()));
        let file_comment_service = Arc::new(FileCommentService::with_repository(file_comment_repository.clone()).with_guard(Arc::new(crate::application::service::FileCommentRowRules)));

        // FileLock service
        let file_lock_repository = Arc::new(FileLockRepository::new(db_pool.clone()));
        let file_lock_service = Arc::new(FileLockService::with_repository(file_lock_repository.clone()).with_guard(Arc::new(crate::application::service::FileLockRowRules)));

        // FileShare service
        let file_share_repository = Arc::new(FileShareRepository::new(db_pool.clone()));
        let file_share_service = Arc::new(FileShareService::with_repository(file_share_repository.clone()).with_guard(Arc::new(crate::application::service::FileShareRowRules)));

        // FileVersion service
        let file_version_repository = Arc::new(FileVersionRepository::new(db_pool.clone()));
        let file_version_service = Arc::new(FileVersionService::with_repository(file_version_repository.clone()));

        // ProcessingJob service
        let processing_job_repository = Arc::new(ProcessingJobRepository::new(db_pool.clone()));
        let processing_job_service = Arc::new(ProcessingJobService::with_repository(processing_job_repository.clone()).with_guard(Arc::new(crate::application::service::ProcessingJobRowRules)));

        // StoredFile service
        let stored_file_repository = Arc::new(StoredFileRepository::new(db_pool.clone()));
        let stored_file_service = Arc::new(StoredFileService::with_repository(stored_file_repository.clone()).with_guard(Arc::new(crate::application::service::StoredFileRowRules)));

        // Thumbnail service
        let thumbnail_repository = Arc::new(ThumbnailRepository::new(db_pool.clone()));
        let thumbnail_service = Arc::new(ThumbnailService::with_repository(thumbnail_repository.clone()));

        // UploadSession service
        let upload_session_repository = Arc::new(UploadSessionRepository::new(db_pool.clone()));
        let upload_session_service = Arc::new(UploadSessionService::with_repository(upload_session_repository.clone()).with_guard(Arc::new(crate::application::service::UploadSessionRowRules)));

        // UserQuota service
        let user_quota_repository = Arc::new(UserQuotaRepository::new(db_pool.clone()));
        let user_quota_service = Arc::new(UserQuotaService::with_repository(user_quota_repository.clone()).with_guard(Arc::new(crate::application::service::UserQuotaRowRules)));


        // The extension builds its own state from what the generated build made.
        let ext = self.ext.build(&ModuleParts {
            db_pool: &db_pool,
            access_log_service: &access_log_service,
            access_log_repository: &access_log_repository,
            bucket_service: &bucket_service,
            bucket_repository: &bucket_repository,
            content_hash_service: &content_hash_service,
            content_hash_repository: &content_hash_repository,
            conversion_job_service: &conversion_job_service,
            conversion_job_repository: &conversion_job_repository,
            file_comment_service: &file_comment_service,
            file_comment_repository: &file_comment_repository,
            file_lock_service: &file_lock_service,
            file_lock_repository: &file_lock_repository,
            file_share_service: &file_share_service,
            file_share_repository: &file_share_repository,
            file_version_service: &file_version_service,
            file_version_repository: &file_version_repository,
            processing_job_service: &processing_job_service,
            processing_job_repository: &processing_job_repository,
            stored_file_service: &stored_file_service,
            stored_file_repository: &stored_file_repository,
            thumbnail_service: &thumbnail_service,
            thumbnail_repository: &thumbnail_repository,
            upload_session_service: &upload_session_service,
            upload_session_repository: &upload_session_repository,
            user_quota_service: &user_quota_service,
            user_quota_repository: &user_quota_repository,
        })?;

        Ok(BucketModule {
            access_log_service,
            bucket_service,
            content_hash_service,
            conversion_job_service,
            file_comment_service,
            file_lock_service,
            file_share_service,
            file_version_service,
            processing_job_service,
            stored_file_service,
            thumbnail_service,
            upload_session_service,
            user_quota_service,
            ext,
        })
    }
}

impl Default for BucketModuleBuilder {
    fn default() -> Self {
        Self::new()
    }
}

// This directory's extension (hand-written; ADR-0031).
include!("mod.ext.rs");