package api import ( "encoding/json" "fmt" "net/http" "github.com/go-chi/chi/v5" "github.com/restxlsx/restxlsx/internal/excel" "github.com/restxlsx/restxlsx/internal/logger" ) type RestAPI struct { engine *excel.Engine log *logger.Logger } func NewRestAPI(engine *excel.Engine, log *logger.Logger) *RestAPI { return &RestAPI{engine: engine, log: log} } func (h *RestAPI) ListTables(w http.ResponseWriter, r *http.Request) { h.engine.Lock() tables := h.engine.ListTables() h.engine.Unlock() writeJSON(w, 200, map[string][]string{"tables": tables}) } func (h *RestAPI) ListRecords(w http.ResponseWriter, r *http.Request) { table := chi.URLParam(r, "table") h.engine.Lock() t := h.engine.GetTable(table) if t == nil { h.engine.Unlock() writeJSON(w, 404, map[string]string{"error": "table not found"}) return } rows := t.Rows h.engine.Unlock() writeJSON(w, 200, rows) } func (h *RestAPI) GetRecord(w http.ResponseWriter, r *http.Request) { table := chi.URLParam(r, "table") id := chi.URLParam(r, "id") h.engine.Lock() t := h.engine.GetTable(table) if t == nil { h.engine.Unlock() writeJSON(w, 404, map[string]string{"error": "table not found"}) return } idCol := h.engine.IDColumn(table) for _, row := range t.Rows { if fmt.Sprintf("%v", row[idCol]) == id { h.engine.Unlock() writeJSON(w, 200, row) return } } h.engine.Unlock() writeJSON(w, 404, map[string]string{"error": "record not found"}) } func (h *RestAPI) CreateRecord(w http.ResponseWriter, r *http.Request) { table := chi.URLParam(r, "table") var rec map[string]any if err := json.NewDecoder(r.Body).Decode(&rec); err != nil { writeJSON(w, 400, map[string]string{"error": "invalid JSON: " + err.Error()}) return } h.engine.Lock() defer h.engine.Unlock() t := h.engine.GetTable(table) if t == nil { writeJSON(w, 404, map[string]string{"error": "table not found"}) return } idCol := h.engine.IDColumn(table) if idCol != "" { if _, ok := rec[idCol]; !ok { rec[idCol] = len(t.Rows) + 1 } } t.Rows = append(t.Rows, rec) if err := h.engine.Flush(); err != nil { writeJSON(w, 503, map[string]string{"error": "flush failed: " + err.Error()}) return } writeJSON(w, 201, rec) } func (h *RestAPI) UpdateRecord(w http.ResponseWriter, r *http.Request) { table := chi.URLParam(r, "table") id := chi.URLParam(r, "id") var rec map[string]any if err := json.NewDecoder(r.Body).Decode(&rec); err != nil { writeJSON(w, 400, map[string]string{"error": "invalid JSON: " + err.Error()}) return } h.engine.Lock() defer h.engine.Unlock() t := h.engine.GetTable(table) if t == nil { writeJSON(w, 404, map[string]string{"error": "table not found"}) return } idCol := h.engine.IDColumn(table) for i, row := range t.Rows { if fmt.Sprintf("%v", row[idCol]) == id { for k, v := range rec { t.Rows[i][k] = v } if err := h.engine.Flush(); err != nil { writeJSON(w, 503, map[string]string{"error": "flush failed: " + err.Error()}) return } writeJSON(w, 200, t.Rows[i]) return } } writeJSON(w, 404, map[string]string{"error": "record not found"}) } func (h *RestAPI) DeleteRecord(w http.ResponseWriter, r *http.Request) { table := chi.URLParam(r, "table") id := chi.URLParam(r, "id") h.engine.Lock() defer h.engine.Unlock() t := h.engine.GetTable(table) if t == nil { writeJSON(w, 404, map[string]string{"error": "table not found"}) return } idCol := h.engine.IDColumn(table) for i, row := range t.Rows { if fmt.Sprintf("%v", row[idCol]) == id { t.Rows = append(t.Rows[:i], t.Rows[i+1:]...) if err := h.engine.Flush(); err != nil { writeJSON(w, 503, map[string]string{"error": "flush failed: " + err.Error()}) return } writeJSON(w, 204, nil) return } } writeJSON(w, 404, map[string]string{"error": "record not found"}) } type responseWriter struct { http.ResponseWriter status int } func (rw *responseWriter) WriteHeader(code int) { rw.status = code rw.ResponseWriter.WriteHeader(code) } func writeJSON(w http.ResponseWriter, status int, data any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) if data != nil { json.NewEncoder(w).Encode(data) } }