| package management |
|
|
| import ( |
| "encoding/json" |
| "fmt" |
| "net/http" |
| "os" |
| "strings" |
|
|
| "github.com/gin-gonic/gin" |
| ) |
|
|
| |
| func genericUpdateField[T any](h *Handler, c *gin.Context, set func(T)) { |
| var body struct { |
| Value *T `json:"value"` |
| } |
| if err := c.ShouldBindJSON(&body); err != nil || body.Value == nil { |
| c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body"}) |
| return |
| } |
| set(*body.Value) |
| h.persist(c) |
| } |
|
|
| |
| func genericListFiles[T any]( |
| c *gin.Context, |
| dir string, |
| filterMap func(os.DirEntry) (T, bool), |
| sortFunc func([]T), |
| ) { |
| entries, err := os.ReadDir(dir) |
| if err != nil { |
| if os.IsNotExist(err) { |
| c.JSON(http.StatusOK, gin.H{"files": []T{}}) |
| return |
| } |
| c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("failed to read dir: %v", err)}) |
| return |
| } |
|
|
| files := make([]T, 0, len(entries)) |
| for _, e := range entries { |
| if item, ok := filterMap(e); ok { |
| files = append(files, item) |
| } |
| } |
|
|
| if sortFunc != nil { |
| sortFunc(files) |
| } |
|
|
| c.JSON(http.StatusOK, gin.H{"files": files}) |
| } |
|
|
| |
| func genericPutList[T any](h *Handler, c *gin.Context, setList func([]T), normalize func(*T), sanitize func()) { |
| data, err := c.GetRawData() |
| if err != nil { |
| c.JSON(400, gin.H{"error": "failed to read body"}) |
| return |
| } |
| var arr []T |
| if err = json.Unmarshal(data, &arr); err != nil { |
| var obj struct { |
| Items []T `json:"items"` |
| } |
| if err2 := json.Unmarshal(data, &obj); err2 != nil || len(obj.Items) == 0 { |
| c.JSON(400, gin.H{"error": "invalid body"}) |
| return |
| } |
| arr = obj.Items |
| } |
|
|
| if normalize != nil { |
| for i := range arr { |
| normalize(&arr[i]) |
| } |
| } |
|
|
| setList(arr) |
| if sanitize != nil { |
| sanitize() |
| } |
| h.persist(c) |
| } |
|
|
| |
| |
| |
| func genericPatchList[T any, P any]( |
| h *Handler, |
| c *gin.Context, |
| getList func() []T, |
| setList func([]T), |
| matchFunc func(T, string) bool, |
| updateFunc func(*T, P) bool, |
| normalize func(*T), |
| sanitize func(), |
| ) { |
| var body struct { |
| Index *int `json:"index"` |
| Match *string `json:"match"` |
| Value *P `json:"value"` |
| } |
| if err := c.ShouldBindJSON(&body); err != nil || body.Value == nil { |
| c.JSON(400, gin.H{"error": "invalid body"}) |
| return |
| } |
|
|
| list := getList() |
| targetIndex := -1 |
| if body.Index != nil && *body.Index >= 0 && *body.Index < len(list) { |
| targetIndex = *body.Index |
| } |
| if targetIndex == -1 && body.Match != nil { |
| match := strings.TrimSpace(*body.Match) |
| if match != "" && matchFunc != nil { |
| for i := range list { |
| if matchFunc(list[i], match) { |
| targetIndex = i |
| break |
| } |
| } |
| } |
| } |
|
|
| if targetIndex == -1 { |
| c.JSON(404, gin.H{"error": "item not found"}) |
| return |
| } |
|
|
| |
| |
| |
| |
|
|
| |
| |
|
|
| entry := list[targetIndex] |
| keep := updateFunc(&entry, *body.Value) |
|
|
| if !keep { |
| |
| list = append(list[:targetIndex], list[targetIndex+1:]...) |
| } else { |
| if normalize != nil { |
| normalize(&entry) |
| } |
| list[targetIndex] = entry |
| } |
|
|
| setList(list) |
| if sanitize != nil { |
| sanitize() |
| } |
| h.persist(c) |
| } |
|
|
| |
| func genericDeleteList[T any]( |
| h *Handler, |
| c *gin.Context, |
| getList func() []T, |
| setList func([]T), |
| shouldDelete func(T, string) bool, |
| queryParamName string, |
| sanitize func(), |
| ) { |
| if val := strings.TrimSpace(c.Query(queryParamName)); val != "" { |
| list := getList() |
| out := make([]T, 0, len(list)) |
| deleted := false |
| for _, v := range list { |
| if shouldDelete(v, val) { |
| deleted = true |
| continue |
| } |
| out = append(out, v) |
| } |
|
|
| if deleted { |
| setList(out) |
| if sanitize != nil { |
| sanitize() |
| } |
| h.persist(c) |
| } else { |
| c.JSON(404, gin.H{"error": "item not found"}) |
| } |
| return |
| } |
|
|
| if idxStr := c.Query("index"); idxStr != "" { |
| list := getList() |
| var idx int |
| if _, err := fmt.Sscanf(idxStr, "%d", &idx); err == nil && idx >= 0 && idx < len(list) { |
| list = append(list[:idx], list[idx+1:]...) |
| setList(list) |
| if sanitize != nil { |
| sanitize() |
| } |
| h.persist(c) |
| return |
| } |
| } |
| c.JSON(400, gin.H{"error": fmt.Sprintf("missing %s or index", queryParamName)}) |
| } |
|
|