144 lines
3.4 KiB
Go
144 lines
3.4 KiB
Go
package entry
|
|
|
|
import (
|
|
"RenewCMS/internal/domain/contenttype"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func SameValue(left any, right any) bool {
|
|
return reflect.DeepEqual(left, right)
|
|
}
|
|
|
|
type UniqueValue struct {
|
|
Key string
|
|
Value any
|
|
}
|
|
|
|
func ApplyDefaults(fields []contenttype.Field, data map[string]any) map[string]any {
|
|
filled := make(map[string]any, len(data))
|
|
for key, value := range data {
|
|
filled[key] = value
|
|
}
|
|
for _, field := range fields {
|
|
if _, present := filled[field.Key]; !present && field.Default != nil {
|
|
filled[field.Key] = field.Default
|
|
}
|
|
}
|
|
return filled
|
|
}
|
|
|
|
func Project(fields []contenttype.Field, data map[string]any) map[string]any {
|
|
projected := make(map[string]any, len(data))
|
|
for _, field := range fields {
|
|
if value, present := data[field.Key]; present {
|
|
projected[field.Key] = value
|
|
}
|
|
}
|
|
return projected
|
|
}
|
|
|
|
func Validate(fields []contenttype.Field, data map[string]any) error {
|
|
for _, field := range fields {
|
|
if err := validateField(field, data); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return firstUnknownField(fields, data)
|
|
}
|
|
|
|
func validateField(field contenttype.Field, data map[string]any) error {
|
|
value := data[field.Key]
|
|
if value == nil || contenttype.EmptyRelation(field, value) {
|
|
if field.Required {
|
|
return InvalidField(field.Key, RuleRequired)
|
|
}
|
|
return nil
|
|
}
|
|
if rule := field.Violation(value); rule != "" {
|
|
return InvalidField(field.Key, rule)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func firstUnknownField(fields []contenttype.Field, data map[string]any) error {
|
|
known := make(map[string]bool, len(fields))
|
|
for _, field := range fields {
|
|
known[field.Key] = true
|
|
}
|
|
var unknown []string
|
|
for key := range data {
|
|
if !known[key] {
|
|
unknown = append(unknown, key)
|
|
}
|
|
}
|
|
if len(unknown) == 0 {
|
|
return nil
|
|
}
|
|
sort.Strings(unknown)
|
|
return InvalidField(unknown[0], RuleUnknownField)
|
|
}
|
|
|
|
func UniqueValues(fields []contenttype.Field, data map[string]any) []UniqueValue {
|
|
var values []UniqueValue
|
|
for _, field := range fields {
|
|
if value := data[field.Key]; (field.Unique || field.IsExclusiveRelation()) && value != nil {
|
|
values = append(values, UniqueValue{Key: field.Key, Value: value})
|
|
}
|
|
}
|
|
return values
|
|
}
|
|
|
|
func MediaIDs(fields []contenttype.Field, data map[string]any) map[string]uint32 {
|
|
ids := map[string]uint32{}
|
|
for _, ref := range contenttype.MediaRefs(fields, data) {
|
|
ids[ref.Field.Key] = ref.ID
|
|
}
|
|
return ids
|
|
}
|
|
|
|
func MissingMedia(field contenttype.Field, unchanged bool) error {
|
|
if unchanged {
|
|
return InvalidField(field.Key, RuleRequired)
|
|
}
|
|
return InvalidField(field.Key, contenttype.RuleType)
|
|
}
|
|
|
|
func RelationIDs(fields []contenttype.Field, data map[string]any) map[string]map[uint32]bool {
|
|
ids := map[string]map[uint32]bool{}
|
|
for _, ref := range contenttype.RelationRefs(fields, data) {
|
|
held := make(map[uint32]bool, len(ref.IDs))
|
|
for _, id := range ref.IDs {
|
|
held[id] = true
|
|
}
|
|
ids[ref.Field.Key] = held
|
|
}
|
|
return ids
|
|
}
|
|
|
|
func MissingRelation(field contenttype.Field, unchanged bool) error {
|
|
return MissingMedia(field, unchanged)
|
|
}
|
|
|
|
func MatchesQuery(fields []contenttype.Field, data map[string]any, query string) bool {
|
|
if len(fields) == 0 {
|
|
return false
|
|
}
|
|
return strings.Contains(strings.ToLower(displayText(data[fields[0].Key])), strings.ToLower(query))
|
|
}
|
|
|
|
func displayText(value any) string {
|
|
switch typed := value.(type) {
|
|
case nil:
|
|
return ""
|
|
case string:
|
|
return typed
|
|
case float64:
|
|
return strconv.FormatFloat(typed, 'f', -1, 64)
|
|
}
|
|
return fmt.Sprint(value)
|
|
}
|