feat(crud): tipos y generador de DDL para recursos CRUD

Anade los tipos CRUDResource, CRUDField, CRUDListParams y CRUDListResult
que modelan un recurso CRUD sobre SQLite, junto con dos funciones puras:
- crud_define_resource valida nombre, tabla y campos (tipos SQLite validos,
  nombres reservados, duplicados) antes de retornar el CRUDResource.
- crud_generate_table_sql genera el DDL CREATE TABLE IF NOT EXISTS con
  id TEXT PRIMARY KEY, timestamps estandar y, si aplica, deleted_at para
  soft delete.

Primera capa de 0021 — el resto (handlers + registro de rutas) se apoya
sobre estas estructuras.
This commit is contained in:
2026-04-18 17:15:21 +02:00
parent 3bc2d2573d
commit 4c88adc183
10 changed files with 562 additions and 0 deletions
@@ -0,0 +1,39 @@
package infra
import (
"fmt"
"strings"
)
// CRUDGenerateTableSQL genera el DDL CREATE TABLE IF NOT EXISTS correspondiente a un CRUDResource.
// Incluye siempre: id TEXT PRIMARY KEY, created_at TEXT NOT NULL, updated_at TEXT NOT NULL.
// Si el recurso es SoftDelete, agrega una columna deleted_at TEXT (nullable).
// Cada CRUDField se mapea a su tipo SQLite y aplica NOT NULL, UNIQUE y DEFAULT segun corresponda.
func CRUDGenerateTableSQL(res CRUDResource) string {
var sb strings.Builder
sb.WriteString(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (\n", res.Table))
sb.WriteString(" id TEXT PRIMARY KEY")
for _, f := range res.Fields {
sb.WriteString(",\n ")
sb.WriteString(f.Name)
sb.WriteString(" ")
sb.WriteString(strings.ToUpper(f.Type))
if f.Required {
sb.WriteString(" NOT NULL")
}
if f.Unique {
sb.WriteString(" UNIQUE")
}
if f.Default != "" {
sb.WriteString(" DEFAULT ")
sb.WriteString(f.Default)
}
}
sb.WriteString(",\n created_at TEXT NOT NULL")
sb.WriteString(",\n updated_at TEXT NOT NULL")
if res.SoftDelete {
sb.WriteString(",\n deleted_at TEXT")
}
sb.WriteString("\n);\n")
return sb.String()
}