@@ -3,6 +3,7 @@ package decoder
33import (
44 "testing"
55
6+ "github.com/stretchr/testify/assert"
67 "github.com/stretchr/testify/require"
78)
89
@@ -220,3 +221,82 @@ func TestNestedUnmarshalerInMap(t *testing.T) {
220221 require .Equal (t , "map:value2" , result ["key2" ].Data )
221222 })
222223}
224+
225+ // testMapIterator uses ReadMap() iterator to simulate mmdbtype.Map behavior.
226+ type testMapIterator struct {
227+ Values map [string ]string
228+ custom bool
229+ }
230+
231+ func (m * testMapIterator ) UnmarshalMaxMindDB (d * Decoder ) error {
232+ m .custom = true
233+ iter , size , err := d .ReadMap ()
234+ if err != nil {
235+ return err
236+ }
237+
238+ m .Values = make (map [string ]string , size )
239+ for key , iterErr := range iter {
240+ if iterErr != nil {
241+ return iterErr
242+ }
243+
244+ // Read the value as a string
245+ value , err := d .ReadString ()
246+ if err != nil {
247+ return err
248+ }
249+
250+ m .Values [string (key )] = value
251+ }
252+ return nil
253+ }
254+
255+ // TestCustomUnmarshalerWithIterator tests that custom unmarshalers using iterators
256+ // work correctly in struct fields. This reproduces the original "no next offset available"
257+ // issue that occurred when mmdbtype.Map was used in structs.
258+ func TestCustomUnmarshalerWithIterator (t * testing.T ) {
259+ type Record struct {
260+ Name string
261+ Location testMapIterator // This field uses ReadMap() iterator
262+ Country string
263+ }
264+
265+ data := []byte {
266+ // Map with 3 items
267+ 0xe3 ,
268+ // Key "Name"
269+ 0x44 , 'N' , 'a' , 'm' , 'e' ,
270+ // Value "Test" (string)
271+ 0x44 , 'T' , 'e' , 's' , 't' ,
272+ // Key "Location"
273+ 0x48 , 'L' , 'o' , 'c' , 'a' , 't' , 'i' , 'o' , 'n' ,
274+ // Value: Map with 2 items (latitude and longitude)
275+ 0xe2 ,
276+ // Key "lat"
277+ 0x43 , 'l' , 'a' , 't' ,
278+ // Value "40.7"
279+ 0x44 , '4' , '0' , '.' , '7' ,
280+ // Key "lng"
281+ 0x43 , 'l' , 'n' , 'g' ,
282+ // Value "-74.0"
283+ 0x45 , '-' , '7' , '4' , '.' , '0' ,
284+ // Key "Country"
285+ 0x47 , 'C' , 'o' , 'u' , 'n' , 't' , 'r' , 'y' ,
286+ // Value "US"
287+ 0x42 , 'U' , 'S' ,
288+ }
289+
290+ d := New (data )
291+ var result Record
292+
293+ err := d .Decode (0 , & result )
294+ require .NoError (t , err )
295+
296+ require .Equal (t , "Test" , result .Name )
297+ assert .True (t , result .Location .custom , "Custom unmarshaler should be called" )
298+ assert .Len (t , result .Location .Values , 2 )
299+ assert .Equal (t , "40.7" , result .Location .Values ["lat" ])
300+ assert .Equal (t , "-74.0" , result .Location .Values ["lng" ])
301+ assert .Equal (t , "US" , result .Country )
302+ }
0 commit comments