Files
descrybe/apps/api/internal/processing/legacy_convert.go
T

112 lines
2.7 KiB
Go
Raw Normal View History

2026-08-16 16:57:36 +02:00
package processing
import (
"fmt"
"strings"
)
// isDescriptionFieldKey reports keys whose values should be plain text scalars
// (not JSON arrays / nested HTML wrappers) for process + V1 poll contracts.
func isDescriptionFieldKey(key string) bool {
switch strings.ToLower(strings.TrimSpace(key)) {
case "description", "desc", "body", "short_description", "shortdescription",
"processed_description", "long_description", "longdescription":
return true
default:
return false
}
}
// PlainDescriptionFromAny coerces legacy description shapes to a single plain
// string: string, {#text}, or arrays of those (joined with newlines). HTML is
// stripped via v1PlainDescription so poll/store never keep ["…"] or markup blobs.
func PlainDescriptionFromAny(v any) string {
if v == nil {
return ""
}
switch t := v.(type) {
case string:
return v1PlainDescription(t)
case []string:
parts := make([]string, 0, len(t))
for _, s := range t {
if p := v1PlainDescription(s); p != "" {
parts = append(parts, p)
}
}
return strings.Join(parts, "\n")
case []any:
parts := make([]string, 0, len(t))
for _, item := range t {
if p := PlainDescriptionFromAny(item); p != "" {
parts = append(parts, p)
}
}
return strings.Join(parts, "\n")
case map[string]any:
for _, k := range []string{"#text", "text", "value", "description", "body"} {
if p := PlainDescriptionFromAny(t[k]); p != "" {
return p
}
}
return ""
default:
s := strings.TrimSpace(fmt.Sprint(t))
if s == "" || s == "<nil>" {
return ""
}
return v1PlainDescription(s)
}
}
// coerceScalarText flattens arrays / #text wrappers into a trimmed string without
// HTML stripping (names, brands, stock labels, …).
func coerceScalarText(v any) string {
if v == nil {
return ""
}
switch t := v.(type) {
case string:
return strings.TrimSpace(t)
case []string:
parts := make([]string, 0, len(t))
for _, s := range t {
s = strings.TrimSpace(s)
if s != "" {
parts = append(parts, s)
}
}
return strings.Join(parts, " ")
case []any:
parts := make([]string, 0, len(t))
for _, item := range t {
if s := coerceScalarText(item); s != "" {
parts = append(parts, s)
}
}
return strings.Join(parts, " ")
case map[string]any:
for _, k := range []string{"#text", "text", "value"} {
if s := coerceScalarText(t[k]); s != "" {
return s
}
}
return ""
case float64, float32, int, int64, int32, bool:
s := strings.TrimSpace(fmt.Sprint(t))
if s == "<nil>" {
return ""
}
return s
default:
if n, ok := t.(jsonNumberStringer); ok {
return strings.TrimSpace(n.String())
}
s := strings.TrimSpace(fmt.Sprint(t))
if s == "" || s == "<nil>" {
return ""
}
return s
}
}