Skip to content

Commit 3cc1671

Browse files
committed
fix #75 add Struct#Columns/ColumnsForTag and Struct#Values/ValuesForTag
1 parent cbddc98 commit 3cc1671

File tree

2 files changed

+101
-12
lines changed

2 files changed

+101
-12
lines changed

struct.go

Lines changed: 62 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -365,32 +365,32 @@ func (s *Struct) DeleteFrom(table string) *DeleteBuilder {
365365
return db
366366
}
367367

368-
// Addr takes address of all exported fields of the s from the value.
368+
// Addr takes address of all exported fields of the s from the st.
369369
// The returned result can be used in `Row#Scan` directly.
370-
func (s *Struct) Addr(value interface{}) []interface{} {
371-
return s.AddrForTag("", value)
370+
func (s *Struct) Addr(st interface{}) []interface{} {
371+
return s.AddrForTag("", st)
372372
}
373373

374-
// AddrForTag takes address of all fields of the s tagged with tag from the value.
375-
// The returned result can be used in `Row#Scan` directly.
374+
// AddrForTag takes address of all fields of the s tagged with tag from the st.
375+
// The returned value can be used in `Row#Scan` directly.
376376
//
377-
// If tag is not defined in s in advance,
378-
func (s *Struct) AddrForTag(tag string, value interface{}) []interface{} {
377+
// If tag is not defined in s in advance, returns nil.
378+
func (s *Struct) AddrForTag(tag string, st interface{}) []interface{} {
379379
sf := s.structFieldsParser()
380380
fields, ok := sf.taggedFields[tag]
381381

382382
if !ok {
383383
return nil
384384
}
385385

386-
return s.AddrWithCols(fields, value)
386+
return s.AddrWithCols(fields, st)
387387
}
388388

389-
// AddrWithCols takes address of all columns defined in cols from the value.
390-
// The returned result can be used in `Row#Scan` directly.
391-
func (s *Struct) AddrWithCols(cols []string, value interface{}) []interface{} {
389+
// AddrWithCols takes address of all columns defined in cols from the st.
390+
// The returned value can be used in `Row#Scan` directly.
391+
func (s *Struct) AddrWithCols(cols []string, st interface{}) []interface{} {
392392
sf := s.structFieldsParser()
393-
v := reflect.ValueOf(value)
393+
v := reflect.ValueOf(st)
394394
v = dereferencedValue(v)
395395

396396
if v.Type() != s.structType {
@@ -414,6 +414,56 @@ func (s *Struct) AddrWithCols(cols []string, value interface{}) []interface{} {
414414
return addrs
415415
}
416416

417+
// Columns returns column names of s for all exported struct fields.
418+
func (s *Struct) Columns() []string {
419+
return s.ColumnsForTag("")
420+
}
421+
422+
// ColumnsForTag returns column names of the s tagged with tag.
423+
func (s *Struct) ColumnsForTag(tag string) (cols []string) {
424+
sf := s.structFieldsParser()
425+
fields, ok := sf.taggedFields[tag]
426+
427+
if !ok {
428+
return
429+
}
430+
431+
cols = append(cols, fields...)
432+
return
433+
}
434+
435+
// Values returns a shadow copy of all exported fields in st.
436+
func (s *Struct) Values(st interface{}) []interface{} {
437+
return s.ValuesForTag("", st)
438+
}
439+
440+
// Values returns a shadow copy of all fields tagged with tag in st.
441+
func (s *Struct) ValuesForTag(tag string, value interface{}) (values []interface{}) {
442+
sf := s.structFieldsParser()
443+
v := reflect.ValueOf(value)
444+
v = dereferencedValue(v)
445+
446+
if v.Type() != s.structType {
447+
return
448+
}
449+
450+
cols, ok := sf.taggedFields[tag]
451+
452+
if !ok {
453+
return
454+
}
455+
456+
values = make([]interface{}, 0, len(cols))
457+
458+
for _, c := range cols {
459+
name := sf.fieldAlias[c]
460+
data := v.FieldByName(name).Interface()
461+
values = append(values, data)
462+
}
463+
464+
return
465+
}
466+
417467
func (s *Struct) quoteFields(sf *structFields, fields []string) []string {
418468
// Try best not to allocate new slice.
419469
if len(sf.quotedFields) == 0 {

struct_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type structUserForTest struct {
2020
}
2121

2222
var userForTest = NewStruct(new(structUserForTest))
23+
var _ = new(structUserForTest).unexported // disable lint warning
2324

2425
func TestStructSelectFrom(t *testing.T) {
2526
a := assert.New(t)
@@ -202,6 +203,7 @@ func TestStructAddrForTag(t *testing.T) {
202203
expected.CreatedAt = 9876543210
203204

204205
a.Equal(user, expected)
206+
a.Equal(userForTest.AddrForTag("invalid", user), nil)
205207
}
206208

207209
func TestStructAddrWithCols(t *testing.T) {
@@ -217,6 +219,43 @@ func TestStructAddrWithCols(t *testing.T) {
217219
_, _ = fmt.Sscanf(str, "%s%d%d%d", userForTest.AddrWithCols([]string{"Name", "id", "created_at", "status"}, user)...)
218220

219221
a.Equal(user, expected)
222+
a.Equal(userForTest.AddrWithCols([]string{"invalid", "non-exist"}, user), nil)
223+
}
224+
225+
func TestStructValues(t *testing.T) {
226+
a := assert.New(t)
227+
st := &structUserForTest{
228+
ID: 123,
229+
Name: "huandu",
230+
Status: 2,
231+
CreatedAt: 1234567890,
232+
}
233+
expected := fmt.Sprintf("%v %v %v %v", st.ID, st.Name, st.Status, st.CreatedAt)
234+
actual := fmt.Sprintf("%v %v %v %v", userForTest.Values(st)...)
235+
236+
a.Equal(actual, expected)
237+
}
238+
239+
func TestStructValuesForTag(t *testing.T) {
240+
a := assert.New(t)
241+
st := &structUserForTest{
242+
ID: 123,
243+
Name: "huandu",
244+
Status: 2,
245+
CreatedAt: 1234567890,
246+
}
247+
expected := fmt.Sprintf("%v %v %v", st.ID, st.Name, st.Status)
248+
actual := fmt.Sprintf("%v %v %v", userForTest.ValuesForTag("important", st)...)
249+
250+
a.Equal(actual, expected)
251+
a.Equal(userForTest.ValuesForTag("invalid", st), nil)
252+
}
253+
254+
func TestStructColumns(t *testing.T) {
255+
a := assert.New(t)
256+
a.Equal(userForTest.Columns(), []string{"id", "Name", "status", "created_at"})
257+
a.Equal(userForTest.ColumnsForTag("important"), []string{"id", "Name", "status"})
258+
a.Equal(userForTest.ColumnsForTag("invalid"), nil)
220259
}
221260

222261
type User struct {

0 commit comments

Comments
 (0)