puerto 0.13.0

Puerto — Rust full-stack DDD framework CLI. Scaffolds Clean Architecture projects with auto-wired DI, SQLx support, and zero manual wiring. Formerly puerto-framework.
{% import "application/macros.tera" as macros %}{% set required = fields | filter(attribute="required_string", value=true) %}{% set string_vos = fields | filter(attribute="string_vo", value=true) %}use std::sync::Arc;

use async_trait::async_trait;
{% for import in vo_imports %}use {{ import }};
{% endfor %}
use crate::domain::{{ snake }}::{
    errors::{{ pascal }}Error,
    model::{{ "{" }}{{ pascal }}, {{ pascal }}Props},
    repository::{{ pascal }}RepositoryTrait,
    use_cases::create_{{ snake }}::{Create{{ pascal }}Params, Create{{ pascal }}UseCaseTrait},
};

pub struct Create{{ pascal }}UseCaseImpl {
    pub repository: Arc<dyn {{ pascal }}RepositoryTrait>,
}

#[async_trait]
impl Create{{ pascal }}UseCaseTrait for Create{{ pascal }}UseCaseImpl {
    async fn execute(&self, params: Create{{ pascal }}Params) -> Result<{{ pascal }}, {{ pascal }}Error> {
        tracing::info!("create_{{ snake }}: start");
{% for f in fields | filter(attribute="is_vo", value=true) %}let {{ f.name }}{% if f.is_vec_vo %}: Vec<{{ f.vo }}>{% endif %} = {{ f.from_params }};
{% endfor %}        let entity = {{ pascal }}::new({{ pascal }}Props {
{% for f in fields %}            {{ f.name }}{% if not f.is_vo %}: params.{{ f.name }}{% endif %},
{% endfor %}        }).inspect_err(|e| tracing::warn!(error = %e, "create_{{ snake }}: rejected"))?;
        self.repository.save(&entity).await.inspect_err(|e| tracing::error!(error = %e, "create_{{ snake }}: repository failed"))?;
        tracing::info!(id = %entity.id, "create_{{ snake }}: done");
        Ok(entity)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::domain::{{ snake }}::repository::mocks::Mock{{ pascal }}Repository;
    use crate::domain::shared::repository_error::RepositoryError;
{% for import in test_imports %}    use {{ import }};
{% endfor %}

    #[tokio::test]
    async fn should_create_{{ snake }}_when_{% if fields | length == 1 and required | length == 1 and fields.0.name == "name" %}name_is_valid{% else %}fields_are_valid{% endif %}() {
        // Arrange
        let mut mock_repo = Mock{{ pascal }}Repository::new();
        mock_repo.expect_save().returning(|_| Ok(()));
        let use_case = Create{{ pascal }}UseCaseImpl {
            repository: Arc::new(mock_repo),
        };

        // Act
        let result = use_case
            .execute(Create{{ pascal }}Params {
{{ macros::params(fields=fields, word="example") }}            })
            .await;

        // Assert
        assert!(result.is_ok());{% set enum_vos = fields | filter(attribute="is_enum_vo", value=true) %}{% if required %}
        assert_eq!(result.unwrap().{{ required.0.name }}, "example");{% elif string_vos %}
        assert_eq!(result.unwrap().{{ string_vos.0.name }}.value(), "example");{% elif enum_vos %}
        assert_eq!(result.unwrap().{{ enum_vos.0.name }}.as_str(), "{{ enum_vos.0.first_variant }}");{% endif %}
    }

    #[tokio::test]
    async fn should_return_duplicate_when_the_store_rejects_a_duplicate() {
        // Arrange
        let mut mock_repo = Mock{{ pascal }}Repository::new();
        mock_repo
            .expect_save()
            .returning(|_| Err(RepositoryError::Duplicate));
        let use_case = Create{{ pascal }}UseCaseImpl {
            repository: Arc::new(mock_repo),
        };

        // Act
        let result = use_case
            .execute(Create{{ pascal }}Params {
{{ macros::params(fields=fields, word="example") }}            })
            .await;

        // Assert
        assert!(result.is_err());
        assert_eq!(result.unwrap_err().to_string(), "{{ snake }}.duplicate");
    }
{% for f in required | concat(with=fields | filter(attribute="is_vo", value=true) | filter(attribute="field_type", value="String")) %}
    #[tokio::test]
    async fn should_return_error_when_{{ f.name }}_is_empty() {
        // Arrange
        let mock_repo = Mock{{ pascal }}Repository::new();
        let use_case = Create{{ pascal }}UseCaseImpl {
            repository: Arc::new(mock_repo),
        };

        // Act
        let result = use_case
            .execute(Create{{ pascal }}Params {
{{ macros::params(fields=fields, word="example", empty=f.name) }}            })
            .await;

        // Assert
        assert!(result.is_err());
        assert_eq!(
            result.unwrap_err().to_string(),
            "{% if f.is_vo %}{{ snake }}.invalid_{{ f.vo_snake }}{% else %}{{ snake }}.validation_error.{{ f.name }}_empty{% endif %}"
        );
    }
{% endfor %}}