Yimaru-BackEnd/internal/domain/reorder.go

47 lines
1.6 KiB
Go

package domain
import (
"errors"
"fmt"
"sort"
)
// ErrReorderInvalidIDSet means ordered_ids is not an exact permutation of the current entities in scope.
var ErrReorderInvalidIDSet = errors.New("ordered_ids must list every id in this scope exactly once, with no duplicates")
// ReorderIDsRequest is the body for batch reorder endpoints (drag-and-drop UI).
// Send "ordered_ids": [] in display order. Must include every id in that scope (use GET list) when there is at least one entity.
type ReorderIDsRequest struct {
OrderedIDs []int64 `json:"ordered_ids"`
}
// ValidateReorderPermutation checks that ordered contains the same multiset of ids as expected (new order vs current scope).
func ValidateReorderPermutation(ordered, expected []int64) error {
if len(expected) == 0 {
if len(ordered) == 0 {
return nil
}
return fmt.Errorf("%w: no entities exist in this scope", ErrReorderInvalidIDSet)
}
if len(ordered) != len(expected) {
return fmt.Errorf("%w: want %d ids, got %d", ErrReorderInvalidIDSet, len(expected), len(ordered))
}
seen := make(map[int64]struct{}, len(ordered))
for _, id := range ordered {
if _, dup := seen[id]; dup {
return fmt.Errorf("%w: duplicate id %d", ErrReorderInvalidIDSet, id)
}
seen[id] = struct{}{}
}
a := append([]int64(nil), expected...)
b := append([]int64(nil), ordered...)
sort.Slice(a, func(i, j int) bool { return a[i] < a[j] })
sort.Slice(b, func(i, j int) bool { return b[i] < b[j] })
for i := range a {
if a[i] != b[i] {
return fmt.Errorf("%w: id set does not match current scope", ErrReorderInvalidIDSet)
}
}
return nil
}