Skip to content

Commit 3890f72

Browse files
committed
feat: add initial support for custom entry unmarshaler
1 parent e80f029 commit 3890f72

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

v3/search.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,35 @@ func (e *Entry) Unmarshal(i interface{}) (err error) {
324324
return
325325
}
326326

327+
func (e *Entry) UnmarshalFunc(i interface{},
328+
fn func(entry *Entry, fieldType reflect.StructField, fieldValue reflect.Value) error) error {
329+
// Make sure it's a ptr
330+
if vo := reflect.ValueOf(i).Kind(); vo != reflect.Ptr {
331+
return fmt.Errorf("ldap: cannot use %s, expected pointer to a struct", vo)
332+
}
333+
334+
sv, st := reflect.ValueOf(i).Elem(), reflect.TypeOf(i).Elem()
335+
// Make sure it's pointing to a struct
336+
if sv.Kind() != reflect.Struct {
337+
return fmt.Errorf("ldap: expected pointer to a struct, got %s", sv.Kind())
338+
}
339+
340+
for n := 0; n < st.NumField(); n++ {
341+
fv, ft := sv.Field(n), st.Field(n)
342+
343+
// skip unexported fields
344+
if ft.PkgPath != "" {
345+
continue
346+
}
347+
348+
if err := fn(e, ft, fv); err != nil {
349+
return err
350+
}
351+
}
352+
353+
return nil
354+
}
355+
327356
// NewEntryAttribute returns a new EntryAttribute with the desired key-value pair
328357
func NewEntryAttribute(name string, values []string) *EntryAttribute {
329358
var bytes [][]byte

v3/search_unmarshal_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package ldap
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"testing"
7+
)
8+
9+
func customUnmarshalFunc(entry *Entry, fieldType reflect.StructField, fieldValue reflect.Value) error {
10+
tagData, ok := fieldType.Tag.Lookup("custom_tag")
11+
if !ok {
12+
return nil
13+
}
14+
15+
value := entry.GetAttributeValue(tagData)
16+
// log.Printf("Marshaling field %s with tag %s and value '%s'", fieldType.Name, tagData, value)
17+
fieldValue.SetString(value)
18+
return nil
19+
}
20+
21+
func TestEntry_UnmarshalFunc(t *testing.T) {
22+
conn, err := DialURL(ldapServer)
23+
if err != nil {
24+
t.Fatalf("Failed to connect: %s\n", err)
25+
}
26+
defer conn.Close()
27+
28+
searchResult, err := conn.Search(&SearchRequest{
29+
BaseDN: baseDN,
30+
Scope: ScopeWholeSubtree,
31+
Filter: "(cn=cis-fac)",
32+
Attributes: []string{"cn", "objectClass"},
33+
})
34+
if err != nil {
35+
t.Fatalf("Failed to search: %s\n", err)
36+
}
37+
38+
type user struct {
39+
ObjectClass string `custom_tag:"objectClass"`
40+
CN string `custom_tag:"cn"`
41+
}
42+
43+
t.Run("success", func(t *testing.T) {
44+
for _, entry := range searchResult.Entries {
45+
var u user
46+
if err := entry.UnmarshalFunc(&u, customUnmarshalFunc); err != nil {
47+
t.Errorf("Failed to unmarshal entry: %s\n", err)
48+
}
49+
50+
if u.CN != entry.GetAttributeValue("cn") {
51+
t.Errorf("UnmarshalFunc did not set the field correctly. Expected: %s, got: %s", entry.GetAttributeValue("cn"), u.CN)
52+
}
53+
}
54+
})
55+
56+
t.Run("error", func(t *testing.T) {
57+
for _, entry := range searchResult.Entries {
58+
var u user
59+
err := entry.UnmarshalFunc(&u, func(entry *Entry, fieldType reflect.StructField, fieldValue reflect.Value) error {
60+
return fmt.Errorf("error from custom unmarshal func on field: %s", fieldType.Name)
61+
})
62+
if err == nil {
63+
t.Errorf("UnmarshalFunc should have returned an error")
64+
}
65+
}
66+
})
67+
}

0 commit comments

Comments
 (0)