use super::{write_file, Scaffold, ScaffoldArgs, ScaffoldResult};
use anyhow::Result;
pub struct RatingsScaffold;
impl Scaffold for RatingsScaffold {
fn name(&self) -> &'static str {
"ratings"
}
fn description(&self) -> &'static str {
"Ratings: polymorphic, star/boolean/like types, average aggregation, distribution"
}
fn generate(&self, args: &ScaffoldArgs) -> Result<ScaffoldResult> {
let mut r = ScaffoldResult::default();
let d = args.dry_run;
write_file(
&mut r,
"src/app/controllers/rating_controller.rs",
CONTROLLER,
d,
)?;
write_file(&mut r, "migrations/create_ratings_table.sql", MIGRATION, d)?;
r.warnings.push("Register /ratings routes".into());
Ok(r)
}
}
const CONTROLLER: &str = r#"use axum::{extract::{Path, Query, State}, response::IntoResponse, Json};
use rok_auth::axum::{Ctx, Response};
use serde::Deserialize;
#[derive(Deserialize)]
pub struct RatingsQuery { pub rateable_type: String, pub rateable_id: i64 }
pub async fn summary(Query(q): Query<RatingsQuery>, State(pool): State<sqlx::PgPool>) -> impl IntoResponse {
// TODO: avg, count, distribution
Response::json(serde_json::json!({
"average": null,
"count": 0,
"distribution": { "1": 0, "2": 0, "3": 0, "4": 0, "5": 0 }
}))
}
pub async fn store(ctx: Ctx, Json(body): Json<serde_json::Value>, State(pool): State<sqlx::PgPool>) -> impl IntoResponse {
// TODO: upsert rating (one per user per rateable)
Response::created(serde_json::json!({ "message": "Rating saved" }))
}
pub async fn destroy(ctx: Ctx, Path(id): Path<i64>, State(pool): State<sqlx::PgPool>) -> impl IntoResponse {
Response::no_content()
}
"#;
const MIGRATION: &str = r#"CREATE TABLE ratings (
id BIGSERIAL PRIMARY KEY,
rateable_type TEXT NOT NULL,
rateable_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
score SMALLINT NOT NULL CHECK (score BETWEEN 1 AND 5),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (rateable_type, rateable_id, user_id)
);
CREATE INDEX ON ratings (rateable_type, rateable_id);
"#;