handle dimension

This commit is contained in:
2024-02-11 15:50:43 +03:00
parent 0c7e94c834
commit e072fcbef5
13 changed files with 479 additions and 27 deletions

View File

@ -0,0 +1,49 @@
// Code generated by the FlatBuffers compiler. DO NOT EDIT.
package fbs
import (
flatbuffers "github.com/google/flatbuffers/go"
)
type Dimensions struct {
_tab flatbuffers.Struct
}
func (rcv *Dimensions) Init(buf []byte, i flatbuffers.UOffsetT) {
rcv._tab.Bytes = buf
rcv._tab.Pos = i
}
func (rcv *Dimensions) Table() flatbuffers.Table {
return rcv._tab.Table
}
func (rcv *Dimensions) Width() float32 {
return rcv._tab.GetFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(0))
}
func (rcv *Dimensions) MutateWidth(n float32) bool {
return rcv._tab.MutateFloat32(rcv._tab.Pos+flatbuffers.UOffsetT(0), n)
}
func (rcv *Dimensions) Height() float32 {
return rcv._tab.GetFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(4))
}
func (rcv *Dimensions) MutateHeight(n float32) bool {
return rcv._tab.MutateFloat32(rcv._tab.Pos+flatbuffers.UOffsetT(4), n)
}
func (rcv *Dimensions) Length() float32 {
return rcv._tab.GetFloat32(rcv._tab.Pos + flatbuffers.UOffsetT(8))
}
func (rcv *Dimensions) MutateLength(n float32) bool {
return rcv._tab.MutateFloat32(rcv._tab.Pos+flatbuffers.UOffsetT(8), n)
}
func CreateDimensions(builder *flatbuffers.Builder, width float32, height float32, length float32) flatbuffers.UOffsetT {
builder.Prep(4, 12)
builder.PrependFloat32(length)
builder.PrependFloat32(height)
builder.PrependFloat32(width)
return builder.Offset()
}

View File

@ -169,8 +169,21 @@ func (rcv *GoodItem) MutateStock(n int16) bool {
return rcv._tab.MutateInt16Slot(28, n)
}
func (rcv *GoodItem) Parameters() []byte {
func (rcv *GoodItem) Sizes(obj *Dimensions) *Dimensions {
o := flatbuffers.UOffsetT(rcv._tab.Offset(30))
if o != 0 {
x := o + rcv._tab.Pos
if obj == nil {
obj = new(Dimensions)
}
obj.Init(rcv._tab.Bytes, x)
return obj
}
return nil
}
func (rcv *GoodItem) Parameters() []byte {
o := flatbuffers.UOffsetT(rcv._tab.Offset(32))
if o != 0 {
return rcv._tab.ByteVector(o + rcv._tab.Pos)
}
@ -178,7 +191,7 @@ func (rcv *GoodItem) Parameters() []byte {
}
func (rcv *GoodItem) CreatedAt() int64 {
o := flatbuffers.UOffsetT(rcv._tab.Offset(32))
o := flatbuffers.UOffsetT(rcv._tab.Offset(34))
if o != 0 {
return rcv._tab.GetInt64(o + rcv._tab.Pos)
}
@ -186,11 +199,11 @@ func (rcv *GoodItem) CreatedAt() int64 {
}
func (rcv *GoodItem) MutateCreatedAt(n int64) bool {
return rcv._tab.MutateInt64Slot(32, n)
return rcv._tab.MutateInt64Slot(34, n)
}
func GoodItemStart(builder *flatbuffers.Builder) {
builder.StartObject(15)
builder.StartObject(16)
}
func GoodItemAddSku(builder *flatbuffers.Builder, sku flatbuffers.UOffsetT) {
builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(sku), 0)
@ -231,11 +244,14 @@ func GoodItemAddCart(builder *flatbuffers.Builder, cart int64) {
func GoodItemAddStock(builder *flatbuffers.Builder, stock int16) {
builder.PrependInt16Slot(12, stock, 0)
}
func GoodItemAddSizes(builder *flatbuffers.Builder, sizes flatbuffers.UOffsetT) {
builder.PrependStructSlot(13, flatbuffers.UOffsetT(sizes), 0)
}
func GoodItemAddParameters(builder *flatbuffers.Builder, parameters flatbuffers.UOffsetT) {
builder.PrependUOffsetTSlot(13, flatbuffers.UOffsetT(parameters), 0)
builder.PrependUOffsetTSlot(14, flatbuffers.UOffsetT(parameters), 0)
}
func GoodItemAddCreatedAt(builder *flatbuffers.Builder, createdAt int64) {
builder.PrependInt64Slot(14, createdAt, 0)
builder.PrependInt64Slot(15, createdAt, 0)
}
func GoodItemEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
return builder.EndObject()

View File

@ -82,6 +82,14 @@ func makeDomainGoodItem(builder *flatbuffers.Builder, in entity.GoodsItem) flatb
producer := builder.CreateString(in.Producer)
var w, h, l float32
if in.Sizes != (entity.GoodsItemSize{}) {
w = float32(in.Sizes.Width.AdjustTo(entity.DimensionKindCentimeter).Value)
h = float32(in.Sizes.Height.AdjustTo(entity.DimensionKindCentimeter).Value)
l = float32(in.Sizes.Length.AdjustTo(entity.DimensionKindCentimeter).Value)
}
sizes := CreateDimensions(builder, w, h, l)
GoodItemStart(builder)
GoodItemAddSku(builder, sku)
GoodItemAddPhoto(builder, photo)
@ -98,6 +106,7 @@ func makeDomainGoodItem(builder *flatbuffers.Builder, in entity.GoodsItem) flatb
GoodItemAddTariff(builder, float32(in.TariffPrice))
GoodItemAddCart(builder, int64(in.Cart))
GoodItemAddStock(builder, int16(in.Stock))
GoodItemAddSizes(builder, sizes)
GoodItemAddParameters(builder, parameters)
GoodItemAddCreatedAt(builder, in.CreatedAt.Unix())
@ -136,6 +145,17 @@ func ParseGoodsItem(data []byte) (item entity.GoodsItem, err error) {
}
}
sizes := itemFBS.Sizes(nil)
w := float64(sizes.Width())
h := float64(sizes.Height())
l := float64(sizes.Length())
item.Sizes = entity.GoodsItemSize{
Width: entity.NewMilimeterDimension(w),
Height: entity.NewMilimeterDimension(h),
Length: entity.NewMilimeterDimension(l),
}
createdAt := itemFBS.CreatedAt()
if createdAt > 0 {
item.CreatedAt = time.Unix(createdAt, 0)

View File

@ -0,0 +1,142 @@
package entity
import (
"fmt"
"strconv"
"strings"
)
var DefaultLocale = DimensionLocalRU
type DimensionLocale uint8
const (
DimensionLocalUnspecified DimensionLocale = iota
DimensionLocalRU
dimensionLocalEnd
)
type DimensionKind uint8
func (k DimensionKind) GetPos() float64 {
switch k {
case DimensionKindMilimeter:
return 1
case DimensionKindCentimeter:
return 10
case DimensionKindMeter:
return 1000
default:
return 0
}
}
func (k DimensionKind) String() string {
m := getLocaleKindToStringMap()[DefaultLocale]
return m[k]
}
const (
DimensionKindUnspecified DimensionKind = iota
DimensionKindMilimeter
DimensionKindCentimeter
DimensionKindMeter
)
type Dimension struct {
Value float64
Kind DimensionKind
}
func (d Dimension) MarshalText() ([]byte, error) {
value := strconv.FormatFloat(d.Value, 'f', 4, 64) + " " + d.Kind.String()
return []byte(value), nil
}
func (d *Dimension) UnmarshalText(data []byte) (err error) {
*d, err = ParseDimention(string(data), DefaultLocale)
return err
}
func (d Dimension) AdjustTo(kind DimensionKind) Dimension {
from := d.Kind.GetPos()
to := kind.GetPos()
switch {
case from < to:
mult := to / from
return Dimension{
Kind: kind,
Value: d.Value / float64(mult),
}
case from > to:
mult := from / to
return Dimension{
Kind: kind,
Value: d.Value * float64(mult),
}
}
return d
}
func ParseDimention(value string, locale DimensionLocale) (Dimension, error) {
switch locale {
case DimensionLocalRU:
default:
return Dimension{}, SimpleError("unknown locale for parse")
}
dimensionStrToKind := getLocaleToKindMap()[locale]
lastSpaceIdx := strings.LastIndex(value, " ")
if lastSpaceIdx == -1 {
return Dimension{}, SimpleError("expected 2 values after split for value " + value)
}
var splitted [2]string
splitted[0] = strings.ReplaceAll(value[:lastSpaceIdx], " ", "")
splitted[1] = value[lastSpaceIdx+1:]
var out Dimension
var ok bool
out.Kind, ok = dimensionStrToKind[splitted[1]]
if !ok {
return Dimension{}, SimpleError("dimension map not found for kind " + splitted[1])
}
var err error
out.Value, err = strconv.ParseFloat(splitted[0], 64)
if err != nil {
return Dimension{}, fmt.Errorf("parsing value: %w", err)
}
return out, nil
}
func NewMilimeterDimension(value float64) Dimension {
return Dimension{
Value: value,
Kind: DimensionKindMilimeter,
}
}
func getLocaleToKindMap() map[DimensionLocale]map[string]DimensionKind {
return map[DimensionLocale]map[string]DimensionKind{
DimensionLocalRU: {
"мм": DimensionKindMilimeter,
"см": DimensionKindCentimeter,
"м": DimensionKindMeter,
},
}
}
func getLocaleKindToStringMap() map[DimensionLocale]map[DimensionKind]string {
return map[DimensionLocale]map[DimensionKind]string{
DimensionLocalRU: {
DimensionKindMilimeter: "мм",
DimensionKindCentimeter: "см",
DimensionKindMeter: "м",
},
}
}

View File

@ -0,0 +1,31 @@
package entity
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestLocaleMap(t *testing.T) {
kindToStr := getLocaleKindToStringMap()
strToKind := getLocaleToKindMap()
assert := assert.New(t)
for locale := DimensionLocalUnspecified + 1; locale < dimensionLocalEnd; locale++ {
localeKinds, ok := kindToStr[locale]
assert.True(ok)
localeStrs, ok := strToKind[locale]
assert.True(ok)
assert.Equal(len(localeKinds), len(localeStrs))
for kindKey, kindValue := range localeKinds {
strKey := kindValue
strValue, ok := localeStrs[strKey]
assert.True(ok)
assert.Equal(kindKey, strValue)
assert.Equal(strKey, kindValue)
}
}
}

View File

@ -0,0 +1,72 @@
package entity_test
import (
"errors"
"testing"
"git.loyso.art/frx/eway/internal/entity"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDimension_AdjustTo(t *testing.T) {
// Test adjusting from smaller dimension to larger one
d := entity.Dimension{Value: 5.0, Kind: entity.DimensionKindCentimeter}
expected := entity.Dimension{Value: 0.05, Kind: entity.DimensionKindMeter}
actual := d.AdjustTo(entity.DimensionKindMeter)
assert.EqualValues(t, expected.Value, actual.Value)
assert.Equal(t, expected.Kind, actual.Kind)
// Test adjusting from larger dimension to smaller one
d = entity.Dimension{Value: 0.05, Kind: entity.DimensionKindMeter}
expected = entity.Dimension{Value: 50.0, Kind: entity.DimensionKindMilimeter}
actual = d.AdjustTo(entity.DimensionKindMilimeter)
assert.EqualValues(t, expected.Value, actual.Value)
assert.Equal(t, expected.Kind, actual.Kind)
}
func TestParseDimension_Success(t *testing.T) {
// Test parsing a valid dimension string with RU locale
input := "10 см"
expected := entity.Dimension{Value: 10.0, Kind: entity.DimensionKindCentimeter}
actual, err := entity.ParseDimention(input, entity.DimensionLocalRU)
require.NoError(t, err)
assert.EqualValues(t, expected.Value, actual.Value)
assert.Equal(t, expected.Kind, actual.Kind)
}
func TestParseDimensionComplex_Success(t *testing.T) {
// Test parsing a valid dimension string with RU locale
input := "10 256.20 см"
expected := entity.Dimension{Value: 10256.20, Kind: entity.DimensionKindCentimeter}
actual, err := entity.ParseDimention(input, entity.DimensionLocalRU)
require.NoError(t, err)
assert.EqualValues(t, expected.Value, actual.Value)
assert.Equal(t, expected.Kind, actual.Kind)
}
func TestParseDimension_InvalidInputFormat(t *testing.T) {
// Test parsing an invalid dimension string with RU locale
input := "invalid value 2"
expectedErr := errors.New("expected 2 values after split for value invalid value 2")
_, err := entity.ParseDimention(input, entity.DimensionLocalRU)
assert.Error(t, err)
assert.EqualError(t, err, expectedErr.Error())
}
func TestParseDimension_InvalidLocale(t *testing.T) {
// Test parsing a dimension string with an unsupported locale
input := "10 мм"
expectedErr := errors.New("unknown locale for parse")
_, err := entity.ParseDimention(input, 3) // An invalid locale value is used here for demonstration purposes
assert.EqualError(t, err, expectedErr.Error())
}

3
internal/entity/empty.go Normal file
View File

@ -0,0 +1,3 @@
package entity
type Empty struct{}

View File

@ -8,6 +8,26 @@ import (
"unicode"
)
type GoodsItemSize struct {
Width Dimension
Height Dimension
Length Dimension
}
func (s GoodsItemSize) GetSum(kind DimensionKind) float64 {
var value float64
sum := func(ds ...Dimension) {
for _, d := range ds {
value += d.AdjustTo(kind).Value
}
}
sum(s.Height, s.Length, s.Length)
return value
}
type GoodsItem struct {
Articul string `json:"sku"`
PhotoURLs []string `json:"photo"`
@ -22,6 +42,7 @@ type GoodsItem struct {
TariffPrice float64 `json:"tariff_price"`
Cart int64 `json:"cart"`
Stock int `json:"stock"`
Sizes GoodsItemSize `json:"sizes"`
Parameters map[string]string `json:"parameters"`
CreatedAt time.Time `json:"created_at"`
}