nest-rs-cli 1.1.0

Scaffolding CLI for NestRS — new projects, feature generators, and project health checks.
//! **Resource** templates — a DB-backed CRUD slice (`g resource`).
//!
//! An `#[expose]` entity, a `CrudService`, and a `#[crud]` HTTP controller
//! behind the app's guards — the `demo/crates/features/src/orgs/` shape.
//!
//! **The guards are load-bearing, not hardening.** `Repo` filters every read by
//! the caller's ambient `Ability`, which only an `AbilityGuard` installs; a
//! DB-backed controller without one compiles and mounts, then answers 500 on
//! every route (no ability) with no row ever reaching Postgres. So there is no
//! unguarded variant to generate — `g resource` bootstraps `g auth` instead.

pub const MOD: &str = r#"mod entity;
mod module;
mod service;

pub mod http;

pub use entity::*;
pub use module::{{module}};
pub use service::{{service}};

pub use http::{{{controller}}, {{http_module}}};
"#;

pub const ENTITY: &str = r#"use nest_rs_resource::expose;
use sea_orm::entity::prelude::*;
use serde::{Deserialize, Serialize};

#[expose(name = "{{entity}}", service = super::service::{{service}})]
#[sea_orm::model]
#[derive(Clone, Debug, DeriveEntityModel)]
#[sea_orm(
    table_name = "{{table}}",
    model_attrs(derive(PartialEq, Serialize, Deserialize))
)]
pub struct Model {
    #[sea_orm(primary_key, auto_increment = false)]
    #[expose]
    pub id: Uuid,
    #[expose(input(create, update), validate(length(min = 1)))]
    pub name: String,
}

impl ActiveModelBehavior for ActiveModel {}
"#;

pub const SERVICE: &str = r#"use nest_rs_core::injectable;
use nest_rs_seaorm::{Creatable, CrudService, Deletable, Updatable};

use super::entity::{{{create_op}}, Entity as {{pascal}}, {{update_op}}};

#[injectable]
#[derive(Default)]
pub struct {{service}};

// Read API + the audited ORM choke point.
impl CrudService for {{service}} {
    type Entity = {{pascal}};
}

// Opt-in write capabilities — drop any your resource does not offer (and the
// matching #[crud] op), instead of declaring a placeholder input type.
impl Creatable for {{service}} {
    type Create = {{create_op}};
}

impl Updatable for {{service}} {
    type Update = {{update_op}};
}

impl Deletable for {{service}} {}
"#;

pub const MODULE: &str = r#"use nest_rs_core::module;

use super::service::{{service}};

#[module(providers = [{{service}}])]
pub struct {{module}};
"#;

pub const HTTP_MOD: &str = r#"mod controller;
mod module;

pub use controller::{{controller}};
pub use module::{{http_module}};
"#;

/// Imports `AuthzHttpModule` alongside the port so the ability guard is
/// reachable — the access graph fails boot otherwise.
pub const HTTP_MODULE: &str = r#"use nest_rs_core::module;

use super::controller::{{controller}};
use crate::authz::AuthzHttpModule;
use crate::{{snake}}::{{module}};

#[module(
    imports = [{{module}}, AuthzHttpModule],
    providers = [{{controller}}],
)]
pub struct {{http_module}};
"#;

/// The `#[crud]` + guards controller, mirroring
/// `demo/crates/features/src/orgs/http/controller.rs`. `AuthnGuard` /
/// `AuthzGuard` come from the workspace's own auth adapter (`nestrs g auth`).
pub const HTTP_CONTROLLER: &str = r#"use std::sync::Arc;

use nest_rs_http::{controller, crud};

use crate::authn::AuthnGuard;
use crate::authz::AuthzGuard;
use crate::{{snake}}::{{{create_op}}, Entity as {{entity}}Entity, {{entity}}, {{service}}, {{update_op}}};

#[controller(path = "/{{kebab}}")]
#[use_guards(AuthnGuard, AuthzGuard)]
pub struct {{controller}} {
    #[inject]
    svc: Arc<{{service}}>,
}

// Every operation is authenticated, ability-filtered, transactional, and
// field-masked — declared by the two guards, generated by #[crud]. The routes
// answer 403 until AppAbility grants a rule for {{entity}}.
#[crud(
    service = svc,
    entity = {{entity}}Entity,
    output = {{entity}},
    create = {{create_op}},
    update = {{update_op}},
)]
impl {{controller}} {}
"#;