Skip to content

Commit

Permalink
Merge pull request #47 from twharmon/foreign-fields
Browse files Browse the repository at this point in the history
handle foreign fields better
  • Loading branch information
twharmon authored Jul 20, 2023
2 parents 4ff4a56 + 6bf145b commit 2e4c43f
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 7 deletions.
23 changes: 23 additions & 0 deletions dynago_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,29 @@ func TestUnmarshalStringCompoundFmt(t *testing.T) {
assertEq(t, want, got)
}

func TestMarshalCompoundFmt(t *testing.T) {
type UserGame struct {
User string `attr:"PK" fmt:"User#{}"`
Game string `attr:"SK" fmt:"Game#{Concluded}#{}"`
Concluded time.Time `attr:"-"`
}
now := time.Now()
p := UserGame{
User: "foo",
Game: "bar",
Concluded: now,
}
want := map[string]*dynamodb.AttributeValue{
"PK": {S: aws.String(fmt.Sprintf("User#%s", p.User))},
"SK": {S: aws.String(fmt.Sprintf("Game#%s#%s", now.Format(time.RFC3339), p.Game))},
}
client := dynago.New(nil)
got, err := client.Marshal(&p)
if err != nil {
t.Fatalf("unexpected err: %s", err)
}
assertEq(t, want, got)
}
func TestUnmarshalString(t *testing.T) {
type Person struct {
Name string
Expand Down
51 changes: 44 additions & 7 deletions field.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,30 @@ func (d *Dynago) field(sf reflect.StructField, index int) (*field, error) {
return &f, nil
}

func (f *field) format(v reflect.Value, fieldIndex int) *string {
func (f *field) format(v reflect.Value) (*string, error) {
output := f.fmt
for _, match := range fmtRegExp.FindAllString(f.fmt, -1) {
var fval reflect.Value
var refFieldLayout string
if match == "{}" {
fval = v.Field(fieldIndex)
fval = v.Field(f.index)
refFieldLayout = f.layout
} else {
fval = v.FieldByName(trimDelims(match))
cache, err := f.client.cachedStruct(v.Type()) // TODO: do this lazily
if err != nil {
return nil, err
}
fname := trimDelims(match)
vt := v.Type()
for i := 0; i < vt.NumField(); i++ {
fld := vt.Field(i)
if fld.Name == fname {
fval = v.Field(i)
ff := cache[i]
refFieldLayout = ff.layout
break
}
}
}
for fval.Kind() == reflect.Pointer {
fval = fval.Elem()
Expand All @@ -133,26 +149,43 @@ func (f *field) format(v reflect.Value, fieldIndex int) *string {
case reflect.Struct:
switch val := fval.Interface().(type) {
case time.Time:
output = strings.ReplaceAll(output, match, val.Format(f.layout))
output = strings.ReplaceAll(output, match, val.Format(refFieldLayout))
}
}
}
return &output
return &output, nil
}

func (f *field) parse(s string, v reflect.Value) error {
for _, match := range fmtRegExp.FindAllString(f.fmt, -1) {
fname := trimDelims(match)

strSubs := f.fmtRegExps[fname].FindStringSubmatch(s)
if strSubs == nil {
continue
}
str := strSubs[1]
var fval reflect.Value
var refFieldLayout string
if fname == "" {
fval = v.Field(f.index)
refFieldLayout = f.layout
} else {
fval = v.FieldByName(fname)
cache, err := f.client.cachedStruct(v.Type()) // TODO: do this lazily
if err != nil {
return err
}
vt := v.Type()
for i := 0; i < vt.NumField(); i++ {
fld := vt.Field(i)
if fld.Name == fname {
fval = v.Field(i)
ff := cache[i]
refFieldLayout = ff.layout
break
}
}
}
for fval.Kind() == reflect.Pointer {
fval = fval.Elem()
Expand Down Expand Up @@ -182,7 +215,7 @@ func (f *field) parse(s string, v reflect.Value) error {
case reflect.Struct:
switch fty {
case timeType:
t, err := time.Parse(f.layout, str)
t, err := time.Parse(refFieldLayout, str)
if err != nil {
return fmt.Errorf("time.Parse: %w", err)
}
Expand All @@ -200,7 +233,11 @@ func (f *field) attrVal(v reflect.Value) (*dynamodb.AttributeValue, error) {
}
switch f.attrType {
case "S":
return &dynamodb.AttributeValue{S: f.format(v, f.index)}, nil
s, err := f.format(v)
if err != nil {
return nil, err
}
return &dynamodb.AttributeValue{S: s}, nil
case "SS":
av := &dynamodb.AttributeValue{}
ss := fv.Interface().([]string)
Expand Down

0 comments on commit 2e4c43f

Please sign in to comment.