752 lines
22 KiB
Go
752 lines
22 KiB
Go
// file: model/value.go
|
|
package model
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/mitchellh/mapstructure"
|
|
"go.mongodb.org/mongo-driver/bson"
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
|
|
"github.com/tech/sendico/pkg/merrors"
|
|
)
|
|
|
|
// ----------------------------
|
|
// Assignment model (domain)
|
|
// ----------------------------
|
|
type Value struct {
|
|
PermissionBound `bson:",inline" json:",inline"`
|
|
|
|
Target ObjectRef `bson:"target" json:"target"`
|
|
Type PropertyType `bson:"type" json:"type"`
|
|
Cardinality Cardinality `bson:"cardinality" json:"cardinality"`
|
|
|
|
PropertySchemaRef primitive.ObjectID `bson:"propertySchemaRef" json:"propertySchemaRef"`
|
|
|
|
// Small typed shape via keys like: "string"/"strings", "integer"/"integers", etc.
|
|
Values SettingsT `bson:"data" json:"data" yaml:"data"`
|
|
}
|
|
|
|
type Money struct {
|
|
Amount primitive.Decimal128 `bson:"amount" json:"amount"`
|
|
Currency Currency `bson:"currency" json:"currency"`
|
|
}
|
|
|
|
type Object = map[string]Value
|
|
|
|
// ----------------------------
|
|
// SINGLE getters
|
|
// ----------------------------
|
|
|
|
func (v *Value) AsString() (string, error) {
|
|
if v.Type != PTString {
|
|
return "", invalidType(PTString, v.Type)
|
|
}
|
|
if v.Cardinality != One {
|
|
return "", merrors.InvalidArgument("invalid cardinality: expected one")
|
|
}
|
|
type payload struct {
|
|
Value string `mapstructure:"string" bson:"string" json:"string" yaml:"string"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return "", err
|
|
}
|
|
return p.Value, nil
|
|
}
|
|
|
|
func (v *Value) AsColor() (string, error) {
|
|
if v.Type != PTColor {
|
|
return "", invalidType(PTColor, v.Type)
|
|
}
|
|
if v.Cardinality != One {
|
|
return "", merrors.InvalidArgument("invalid cardinality: expected one")
|
|
}
|
|
type payload struct {
|
|
Value string `mapstructure:"color" bson:"color" json:"color" yaml:"color"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return "", err
|
|
}
|
|
return p.Value, nil
|
|
}
|
|
|
|
func (v *Value) AsInteger() (int64, error) {
|
|
if v.Type != PTInteger {
|
|
return 0, invalidType(PTInteger, v.Type)
|
|
}
|
|
if v.Cardinality != One {
|
|
return 0, merrors.InvalidArgument("invalid cardinality: expected one")
|
|
}
|
|
type payload struct {
|
|
Value int64 `mapstructure:"integer" bson:"integer" json:"integer" yaml:"integer"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return 0, err
|
|
}
|
|
return p.Value, nil
|
|
}
|
|
|
|
func (v *Value) AsFloat() (float64, error) {
|
|
if v.Type != PTFloat {
|
|
return 0, invalidType(PTFloat, v.Type)
|
|
}
|
|
if v.Cardinality != One {
|
|
return 0, merrors.InvalidArgument("invalid cardinality: expected one")
|
|
}
|
|
type payload struct {
|
|
Value float64 `mapstructure:"float" bson:"float" json:"float" yaml:"float"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return 0, err
|
|
}
|
|
return p.Value, nil
|
|
}
|
|
|
|
func (v *Value) AsDateTime() (time.Time, error) {
|
|
if v.Type != PTDateTime {
|
|
return time.Time{}, invalidType(PTDateTime, v.Type)
|
|
}
|
|
if v.Cardinality != One {
|
|
return time.Time{}, merrors.InvalidArgument("invalid cardinality: expected one")
|
|
}
|
|
type payload struct {
|
|
Value time.Time `mapstructure:"date_time" bson:"date_time" json:"date_time" yaml:"date_time"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
return p.Value, nil
|
|
}
|
|
|
|
func (v *Value) AsMonetary() (Money, error) {
|
|
if v.Type != PTMonetary {
|
|
return Money{}, invalidType(PTMonetary, v.Type)
|
|
}
|
|
if v.Cardinality != One {
|
|
return Money{}, merrors.InvalidArgument("invalid cardinality: expected one")
|
|
}
|
|
type payload struct {
|
|
Value Money `mapstructure:"monetary" bson:"monetary" json:"monetary" yaml:"monetary"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return Money{}, err
|
|
}
|
|
return p.Value, nil
|
|
}
|
|
|
|
func (v *Value) AsReference() (primitive.ObjectID, error) {
|
|
if v.Type != PTReference {
|
|
return primitive.NilObjectID, invalidType(PTReference, v.Type)
|
|
}
|
|
if v.Cardinality != One {
|
|
return primitive.NilObjectID, merrors.InvalidArgument("invalid cardinality: expected one")
|
|
}
|
|
type payload struct {
|
|
Value primitive.ObjectID `mapstructure:"reference" bson:"reference" json:"reference" yaml:"reference"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return primitive.NilObjectID, err
|
|
}
|
|
return p.Value, nil
|
|
}
|
|
|
|
func (v *Value) AsObject() (Object, error) {
|
|
if v.Type != PTObject {
|
|
return nil, invalidType(PTObject, v.Type)
|
|
}
|
|
if v.Cardinality != One {
|
|
return nil, merrors.InvalidArgument("invalid cardinality: expected one")
|
|
}
|
|
type payload struct {
|
|
Value Object `mapstructure:"object" bson:"object" json:"object" yaml:"object"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.Value, nil
|
|
}
|
|
|
|
// ----------------------------
|
|
// ARRAY getters
|
|
// ----------------------------
|
|
|
|
func (v *Value) AsStrings() ([]string, error) {
|
|
if v.Type != PTString {
|
|
return nil, invalidType(PTString, v.Type)
|
|
}
|
|
if v.Cardinality != Many {
|
|
return nil, merrors.InvalidArgument("invalid cardinality: expected many")
|
|
}
|
|
type payload struct {
|
|
Values []string `mapstructure:"strings" bson:"strings" json:"strings" yaml:"strings"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.Values, nil
|
|
}
|
|
|
|
func (v *Value) AsColors() ([]string, error) {
|
|
if v.Type != PTColor {
|
|
return nil, invalidType(PTColor, v.Type)
|
|
}
|
|
if v.Cardinality != Many {
|
|
return nil, merrors.InvalidArgument("invalid cardinality: expected many")
|
|
}
|
|
type payload struct {
|
|
Values []string `mapstructure:"colors" bson:"colors" json:"colors" yaml:"colors"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.Values, nil
|
|
}
|
|
|
|
func (v *Value) AsIntegers() ([]int64, error) {
|
|
if v.Type != PTInteger {
|
|
return nil, invalidType(PTInteger, v.Type)
|
|
}
|
|
if v.Cardinality != Many {
|
|
return nil, merrors.InvalidArgument("invalid cardinality: expected many")
|
|
}
|
|
type payload struct {
|
|
Values []int64 `mapstructure:"integers" bson:"integers" json:"integers" yaml:"integers"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.Values, nil
|
|
}
|
|
|
|
func (v *Value) AsFloats() ([]float64, error) {
|
|
if v.Type != PTFloat {
|
|
return nil, invalidType(PTFloat, v.Type)
|
|
}
|
|
if v.Cardinality != Many {
|
|
return nil, merrors.InvalidArgument("invalid cardinality: expected many")
|
|
}
|
|
type payload struct {
|
|
Values []float64 `mapstructure:"floats" bson:"floats" json:"floats" yaml:"floats"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.Values, nil
|
|
}
|
|
|
|
func (v *Value) AsDateTimes() ([]time.Time, error) {
|
|
if v.Type != PTDateTime {
|
|
return nil, invalidType(PTDateTime, v.Type)
|
|
}
|
|
if v.Cardinality != Many {
|
|
return nil, merrors.InvalidArgument("invalid cardinality: expected many")
|
|
}
|
|
type payload struct {
|
|
Values []time.Time `mapstructure:"date_times" bson:"date_times" json:"date_times" yaml:"date_times"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.Values, nil
|
|
}
|
|
|
|
func (v *Value) AsMonetaries() ([]Money, error) {
|
|
if v.Type != PTMonetary {
|
|
return nil, invalidType(PTMonetary, v.Type)
|
|
}
|
|
if v.Cardinality != Many {
|
|
return nil, merrors.InvalidArgument("invalid cardinality: expected many")
|
|
}
|
|
type payload struct {
|
|
Values []Money `mapstructure:"monetaries" bson:"monetaries" json:"monetaries" yaml:"monetaries"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.Values, nil
|
|
}
|
|
|
|
func (v *Value) AsReferences() ([]primitive.ObjectID, error) {
|
|
if v.Type != PTReference {
|
|
return nil, invalidType(PTReference, v.Type)
|
|
}
|
|
if v.Cardinality != Many {
|
|
return nil, merrors.InvalidArgument("invalid cardinality: expected many")
|
|
}
|
|
type payload struct {
|
|
Values []primitive.ObjectID `mapstructure:"references" bson:"references" json:"references" yaml:"references"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.Values, nil
|
|
}
|
|
|
|
func (v *Value) AsObjects() ([]Object, error) {
|
|
if v.Type != PTObject {
|
|
return nil, invalidType(PTObject, v.Type)
|
|
}
|
|
if v.Cardinality != Many {
|
|
return nil, merrors.InvalidArgument("invalid cardinality: expected many")
|
|
}
|
|
type payload struct {
|
|
Values []Object `mapstructure:"objects" bson:"objects" json:"objects" yaml:"objects"`
|
|
}
|
|
var p payload
|
|
if err := mapstructure.Decode(v.Values, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.Values, nil
|
|
}
|
|
|
|
// ----------------------------
|
|
// FACTORIES (scheme + value)
|
|
// ----------------------------
|
|
|
|
// Strings
|
|
func NewStringValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, v string) (Value, error) {
|
|
if scheme.Type != PTString {
|
|
return Value{}, invalidType(PTString, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateStrings([]string{v}); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{
|
|
PermissionBound: scope,
|
|
Target: target,
|
|
Type: PTString,
|
|
Cardinality: One,
|
|
PropertySchemaRef: scheme.ID,
|
|
Values: SettingsT{VKString: v},
|
|
}, nil
|
|
}
|
|
|
|
func NewStringsValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, vv []string) (Value, error) {
|
|
if scheme.Type != PTString {
|
|
return Value{}, invalidType(PTString, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateStrings(vv); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{
|
|
PermissionBound: scope,
|
|
Target: target,
|
|
Type: PTString,
|
|
Cardinality: Many,
|
|
PropertySchemaRef: scheme.ID,
|
|
Values: SettingsT{VKStrings: vv},
|
|
}, nil
|
|
}
|
|
|
|
// Colors
|
|
func NewColorValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, v string) (Value, error) {
|
|
if scheme.Type != PTColor {
|
|
return Value{}, invalidType(PTColor, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateColors([]string{v}); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTColor, One, scheme.ID, SettingsT{VKColor: v}}, nil
|
|
}
|
|
func NewColorsValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, vv []string) (Value, error) {
|
|
if scheme.Type != PTColor {
|
|
return Value{}, invalidType(PTColor, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateColors(vv); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTColor, Many, scheme.ID, SettingsT{VKColors: vv}}, nil
|
|
}
|
|
|
|
// Integers
|
|
func NewIntegerValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, v int64) (Value, error) {
|
|
if scheme.Type != PTInteger {
|
|
return Value{}, invalidType(PTInteger, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateIntegers([]int64{v}); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTInteger, One, scheme.ID, SettingsT{VKInteger: v}}, nil
|
|
}
|
|
func NewIntegersValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, vv []int64) (Value, error) {
|
|
if scheme.Type != PTInteger {
|
|
return Value{}, invalidType(PTInteger, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateIntegers(vv); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTInteger, Many, scheme.ID, SettingsT{VKIntegers: vv}}, nil
|
|
}
|
|
|
|
// Floats
|
|
func NewFloatValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, v float64) (Value, error) {
|
|
if scheme.Type != PTFloat {
|
|
return Value{}, invalidType(PTFloat, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateFloats([]float64{v}); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTFloat, One, scheme.ID, SettingsT{VKFloat: v}}, nil
|
|
}
|
|
func NewFloatsValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, vv []float64) (Value, error) {
|
|
if scheme.Type != PTFloat {
|
|
return Value{}, invalidType(PTFloat, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateFloats(vv); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTFloat, Many, scheme.ID, SettingsT{VKFloats: vv}}, nil
|
|
}
|
|
|
|
// DateTimes
|
|
func NewDateTimeValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, v time.Time) (Value, error) {
|
|
if scheme.Type != PTDateTime {
|
|
return Value{}, invalidType(PTDateTime, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateDateTimes([]time.Time{v}); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTDateTime, One, scheme.ID, SettingsT{VKDateTime: v}}, nil
|
|
}
|
|
func NewDateTimesValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, vv []time.Time) (Value, error) {
|
|
if scheme.Type != PTDateTime {
|
|
return Value{}, invalidType(PTDateTime, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateDateTimes(vv); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTDateTime, Many, scheme.ID, SettingsT{VKDateTimes: vv}}, nil
|
|
}
|
|
|
|
// Monetary (needs org currency for validation if required by scheme)
|
|
func NewMonetaryValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, v Money, orgCurrency Currency) (Value, error) {
|
|
if scheme.Type != PTMonetary {
|
|
return Value{}, invalidType(PTMonetary, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateMonetaries([]Money{v}, orgCurrency); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTMonetary, One, scheme.ID, SettingsT{VKMonetary: v}}, nil
|
|
}
|
|
func NewMonetariesValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, vv []Money, orgCurrency Currency) (Value, error) {
|
|
if scheme.Type != PTMonetary {
|
|
return Value{}, invalidType(PTMonetary, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateMonetaries(vv, orgCurrency); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTMonetary, Many, scheme.ID, SettingsT{VKMonetaries: vv}}, nil
|
|
}
|
|
|
|
// References (needs exist-fn)
|
|
func NewReferenceValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, v primitive.ObjectID, exist ExistFn) (Value, error) {
|
|
if scheme.Type != PTReference {
|
|
return Value{}, invalidType(PTReference, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateReferences([]primitive.ObjectID{v}, exist); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTReference, One, scheme.ID, SettingsT{VKReference: v}}, nil
|
|
}
|
|
func NewReferencesValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, vv []primitive.ObjectID, exist ExistFn) (Value, error) {
|
|
if scheme.Type != PTReference {
|
|
return Value{}, invalidType(PTReference, scheme.Type)
|
|
}
|
|
if err := scheme.ValidateReferences(vv, exist); err != nil {
|
|
return Value{}, err
|
|
}
|
|
return Value{scope, target, PTReference, Many, scheme.ID, SettingsT{VKReferences: vv}}, nil
|
|
}
|
|
|
|
// Objects (opaque maps)
|
|
func NewObjectValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, v Object) (Value, error) {
|
|
if scheme.Type != PTObject {
|
|
return Value{}, invalidType(PTObject, scheme.Type)
|
|
}
|
|
// Add your own ValidateObject if needed
|
|
return Value{scope, target, PTObject, One, scheme.ID, SettingsT{VKObject: v}}, nil
|
|
}
|
|
func NewObjectsValue(scope PermissionBound, target ObjectRef, scheme PropertySchema, vv []Object) (Value, error) {
|
|
if scheme.Type != PTObject {
|
|
return Value{}, invalidType(PTObject, scheme.Type)
|
|
}
|
|
return Value{scope, target, PTObject, Many, scheme.ID, SettingsT{VKObjects: vv}}, nil
|
|
}
|
|
|
|
// ----------------------------
|
|
// Custom BSON Marshalers/Unmarshalers
|
|
// ----------------------------
|
|
|
|
// MarshalBSON implements bson.Marshaler to ensure proper serialization
|
|
func (v Value) MarshalBSON() ([]byte, error) {
|
|
// Create a temporary struct that preserves the exact structure
|
|
temp := struct {
|
|
PermissionBound `bson:",inline"`
|
|
Target ObjectRef `bson:"target"`
|
|
Type PropertyType `bson:"type"`
|
|
Cardinality Cardinality `bson:"cardinality"`
|
|
PropertySchemaRef primitive.ObjectID `bson:"propertySchemaRef"`
|
|
Values SettingsTWrapper `bson:"data"`
|
|
}{
|
|
PermissionBound: v.PermissionBound,
|
|
Target: v.Target,
|
|
Type: v.Type,
|
|
Cardinality: v.Cardinality,
|
|
PropertySchemaRef: v.PropertySchemaRef,
|
|
Values: SettingsTWrapper(v.Values),
|
|
}
|
|
|
|
return bson.Marshal(temp)
|
|
}
|
|
|
|
// UnmarshalBSON implements bson.Unmarshaler to ensure proper deserialization
|
|
func (v *Value) UnmarshalBSON(data []byte) error {
|
|
// Create a temporary struct that matches the BSON structure
|
|
temp := struct {
|
|
PermissionBound `bson:",inline"`
|
|
Target ObjectRef `bson:"target"`
|
|
Type PropertyType `bson:"type"`
|
|
Cardinality Cardinality `bson:"cardinality"`
|
|
PropertySchemaRef primitive.ObjectID `bson:"propertySchemaRef"`
|
|
Values SettingsTWrapper `bson:"data"`
|
|
}{}
|
|
|
|
if err := bson.Unmarshal(data, &temp); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Copy the values back to the original struct
|
|
v.PermissionBound = temp.PermissionBound
|
|
v.Target = temp.Target
|
|
v.Type = temp.Type
|
|
v.Cardinality = temp.Cardinality
|
|
v.PropertySchemaRef = temp.PropertySchemaRef
|
|
v.Values = SettingsT(temp.Values)
|
|
|
|
return nil
|
|
}
|
|
|
|
// ----------------------------
|
|
// Custom BSON Marshalers for SettingsT
|
|
// ----------------------------
|
|
|
|
// SettingsT is a type alias, so we need to define a wrapper type for methods
|
|
type SettingsTWrapper SettingsT
|
|
|
|
// MarshalBSON implements bson.Marshaler for SettingsT to preserve exact types
|
|
func (s SettingsTWrapper) MarshalBSON() ([]byte, error) {
|
|
// Convert SettingsT to bson.M to preserve exact types
|
|
doc := bson.M{}
|
|
for key, value := range s {
|
|
doc[key] = value
|
|
}
|
|
return bson.Marshal(doc)
|
|
}
|
|
|
|
// UnmarshalBSON implements bson.Unmarshaler for SettingsT to preserve exact types
|
|
func (s *SettingsTWrapper) UnmarshalBSON(data []byte) error {
|
|
// Unmarshal into a generic map first
|
|
var doc bson.M
|
|
if err := bson.Unmarshal(data, &doc); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Convert back to SettingsT, preserving types
|
|
*s = make(SettingsT)
|
|
for key, value := range doc {
|
|
// Handle special cases where BSON converts types
|
|
switch v := value.(type) {
|
|
case primitive.A:
|
|
// Convert primitive.A back to appropriate slice type
|
|
if len(v) > 0 {
|
|
switch v[0].(type) {
|
|
case string:
|
|
strings := make([]string, len(v))
|
|
for i, item := range v {
|
|
strings[i] = item.(string)
|
|
}
|
|
(*s)[key] = strings
|
|
case int32, int64:
|
|
ints := make([]int64, len(v))
|
|
for i, item := range v {
|
|
switch val := item.(type) {
|
|
case int32:
|
|
ints[i] = int64(val)
|
|
case int64:
|
|
ints[i] = val
|
|
}
|
|
}
|
|
(*s)[key] = ints
|
|
case float32, float64:
|
|
floats := make([]float64, len(v))
|
|
for i, item := range v {
|
|
switch val := item.(type) {
|
|
case float32:
|
|
floats[i] = float64(val)
|
|
case float64:
|
|
floats[i] = val
|
|
}
|
|
}
|
|
(*s)[key] = floats
|
|
case primitive.DateTime:
|
|
times := make([]time.Time, len(v))
|
|
for i, item := range v {
|
|
times[i] = item.(primitive.DateTime).Time().Truncate(time.Millisecond)
|
|
}
|
|
(*s)[key] = times
|
|
case primitive.ObjectID:
|
|
refs := make([]primitive.ObjectID, len(v))
|
|
for i, item := range v {
|
|
refs[i] = item.(primitive.ObjectID)
|
|
}
|
|
(*s)[key] = refs
|
|
case bson.M:
|
|
// Handle nested objects (Money, Object, etc.)
|
|
if key == VKMonetaries {
|
|
// Handle Money slice
|
|
moneys := make([]Money, len(v))
|
|
for i, item := range v {
|
|
if itemMap, ok := item.(bson.M); ok {
|
|
var money Money
|
|
if amount, ok := itemMap[MKAmount].(primitive.Decimal128); ok {
|
|
money.Amount = amount
|
|
}
|
|
if currency, ok := itemMap[MKCurrency].(string); ok {
|
|
money.Currency = Currency(currency)
|
|
}
|
|
moneys[i] = money
|
|
}
|
|
}
|
|
(*s)[key] = moneys
|
|
} else {
|
|
// Handle Object slice
|
|
objects := make([]Object, len(v))
|
|
for i, item := range v {
|
|
obj := make(Object)
|
|
for k, val := range item.(bson.M) {
|
|
// Recursively handle nested Values
|
|
if valMap, ok := val.(bson.M); ok {
|
|
var nestedValue Value
|
|
if data, err := bson.Marshal(valMap); err == nil {
|
|
if err := bson.Unmarshal(data, &nestedValue); err == nil {
|
|
obj[k] = nestedValue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
objects[i] = obj
|
|
}
|
|
(*s)[key] = objects
|
|
}
|
|
default:
|
|
// Fallback: keep as primitive.A
|
|
(*s)[key] = v
|
|
}
|
|
} else {
|
|
// Empty array - determine type from key name
|
|
switch key {
|
|
case VKStrings, VKColors:
|
|
(*s)[key] = []string{}
|
|
case VKIntegers:
|
|
(*s)[key] = []int64{}
|
|
case VKFloats:
|
|
(*s)[key] = []float64{}
|
|
case VKDateTimes:
|
|
(*s)[key] = []time.Time{}
|
|
case VKReferences:
|
|
(*s)[key] = []primitive.ObjectID{}
|
|
case VKMonetaries:
|
|
(*s)[key] = []Money{}
|
|
case VKObjects:
|
|
(*s)[key] = []Object{}
|
|
default:
|
|
(*s)[key] = []interface{}{}
|
|
}
|
|
}
|
|
case primitive.DateTime:
|
|
// Convert primitive.DateTime back to time.Time and truncate to millisecond precision
|
|
(*s)[key] = v.Time().Truncate(time.Millisecond)
|
|
case int64:
|
|
// Handle time.Time that gets converted to int64 (Unix timestamp)
|
|
if key == VKDateTime {
|
|
(*s)[key] = time.Unix(v, 0).UTC().Truncate(time.Millisecond)
|
|
} else {
|
|
(*s)[key] = v
|
|
}
|
|
case bson.M:
|
|
// Handle nested objects
|
|
if key == VKMonetary {
|
|
// Handle Money struct
|
|
var money Money
|
|
if amount, ok := v[MKAmount].(primitive.Decimal128); ok {
|
|
money.Amount = amount
|
|
}
|
|
if currency, ok := v[MKCurrency].(string); ok {
|
|
money.Currency = Currency(currency)
|
|
}
|
|
(*s)[key] = money
|
|
} else if key == VKMonetaries {
|
|
// Handle Money slice - this shouldn't happen in single values
|
|
(*s)[key] = v
|
|
} else if key == VKObject {
|
|
// Handle Object type
|
|
obj := make(Object)
|
|
for k, val := range v {
|
|
if valMap, ok := val.(bson.M); ok {
|
|
var nestedValue Value
|
|
if data, err := bson.Marshal(valMap); err == nil {
|
|
if err := bson.Unmarshal(data, &nestedValue); err == nil {
|
|
obj[k] = nestedValue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
(*s)[key] = obj
|
|
} else {
|
|
// Generic map
|
|
(*s)[key] = v
|
|
}
|
|
case nil:
|
|
// Handle nil values - determine type from key name
|
|
switch key {
|
|
case VKStrings, VKColors:
|
|
(*s)[key] = []string(nil)
|
|
case VKIntegers:
|
|
(*s)[key] = []int64(nil)
|
|
case VKFloats:
|
|
(*s)[key] = []float64(nil)
|
|
case VKDateTimes:
|
|
(*s)[key] = []time.Time(nil)
|
|
case VKReferences:
|
|
(*s)[key] = []primitive.ObjectID(nil)
|
|
case VKMonetaries:
|
|
(*s)[key] = []Money(nil)
|
|
case VKObjects:
|
|
(*s)[key] = []Object(nil)
|
|
default:
|
|
(*s)[key] = nil
|
|
}
|
|
default:
|
|
// Keep as-is for primitive types
|
|
(*s)[key] = value
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|