rok-cli 0.3.2

Developer CLI for rok-based Axum applications
use super::{write_file, Scaffold, ScaffoldArgs, ScaffoldResult};
use anyhow::Result;

pub struct ImportScaffold;

impl Scaffold for ImportScaffold {
    fn name(&self) -> &'static str {
        "import"
    }
    fn description(&self) -> &'static str {
        "CSV/Excel import: row validation, error report, chunked processing, duplicate handling"
    }

    fn generate(&self, args: &ScaffoldArgs) -> Result<ScaffoldResult> {
        let mut r = ScaffoldResult::default();
        let d = args.dry_run;
        write_file(
            &mut r,
            "src/app/controllers/import_controller.rs",
            CONTROLLER,
            d,
        )?;
        write_file(&mut r, "src/app/jobs/import_job.rs", JOB, d)?;
        write_file(
            &mut r,
            "migrations/create_import_logs_table.sql",
            MIGRATION,
            d,
        )?;
        r.warnings.push("Register POST /imports route".into());
        Ok(r)
    }
}

const CONTROLLER: &str = r#"use axum::{extract::{Multipart, State}, response::IntoResponse};
use rok_auth::axum::{Ctx, Response};

pub async fn preview(ctx: Ctx, mut multipart: Multipart) -> impl IntoResponse {
    // TODO: parse first N rows, validate, return preview with errors
    Response::json(serde_json::json!({ "preview": [], "errors": [], "total_rows": 0 }))
}

pub async fn confirm(ctx: Ctx, State(pool): State<sqlx::PgPool>) -> impl IntoResponse {
    // TODO: enqueue ImportJob with confirmed import_id
    Response::json(serde_json::json!({ "job_id": null, "message": "Import queued" }))
}
"#;

const JOB: &str = r#"use rok_queue::Job;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct ImportJob {
    pub import_log_id: i64,
    pub strategy: String,  // "skip" | "update" | "error"
    pub chunk_size: usize,
}

#[async_trait::async_trait]
impl Job for ImportJob {
    const QUEUE: &'static str = "imports";

    async fn handle(&self) -> anyhow::Result<()> {
        // TODO: read file from storage, process in chunks, handle duplicates per strategy
        Ok(())
    }
}
"#;

const MIGRATION: &str = r#"CREATE TABLE import_logs (
    id              BIGSERIAL PRIMARY KEY,
    user_id         BIGINT NOT NULL,
    filename        TEXT NOT NULL,
    status          TEXT NOT NULL DEFAULT 'pending',
    total_rows      INTEGER,
    imported_rows   INTEGER DEFAULT 0,
    error_rows      INTEGER DEFAULT 0,
    errors          JSONB,
    storage_key     TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now()
);
"#;