@@ -28,6 +28,10 @@ func NewAnonymousUser(key string) User {
2828//     user := NewUserBuilder("user-key").Name("Bob").AsPrivateAttribute().Build() // Name is now private 
2929// 
3030// A UserBuilder should not be accessed by multiple goroutines at once. 
31+ // 
32+ // This is defined as an interface rather than a concrete type only for syntactic convenience (see 
33+ // UserBuilderCanMakeAttributePrivate). Applications should not implement this interface since the package 
34+ // may add methods to it in the future. 
3135type  UserBuilder  interface  {
3236	// Key changes the unique key for the user being built. 
3337	Key (value  string ) UserBuilder 
@@ -73,6 +77,13 @@ type UserBuilder interface {
7377	//         Build() 
7478	Custom (attribute  string , value  ldvalue.Value ) UserBuilderCanMakeAttributePrivate 
7579
80+ 	// CustomAll sets all of the user's custom attributes at once from a ValueMap. 
81+ 	// 
82+ 	// UserBuilder has copy-on-write behavior to make this method efficient: if you do not make any 
83+ 	// changes to custom attributes after this, it reuses the original map rather than allocating a 
84+ 	// new one. 
85+ 	CustomAll (ldvalue.ValueMap ) UserBuilderCanMakeAttributePrivate 
86+ 
7687	// SetAttribute sets any attribute of the user being built, specified as a UserAttribute, to a value 
7788	// of type ldvalue.Value. 
7889	// 
@@ -151,8 +162,9 @@ type userBuilderImpl struct {
151162	avatar                       ldvalue.OptionalString 
152163	name                         ldvalue.OptionalString 
153164	anonymous                    ldvalue.OptionalBool 
154- 	custom                       ldvalue.ObjectBuilder 
165+ 	custom                       ldvalue.ValueMapBuilder 
155166	privateAttrs                 map [UserAttribute ]struct {}
167+ 	privateAttrsCopyOnWrite      bool 
156168	lastAttributeCanMakePrivate  UserAttribute 
157169}
158170
@@ -166,31 +178,28 @@ func NewUserBuilder(key string) UserBuilder {
166178
167179// NewUserBuilderFromUser constructs a new UserBuilder, copying all attributes from an existing user. You may 
168180// then call setter methods on the new UserBuilder to modify those attributes. 
181+ // 
182+ // Custom attributes, and the set of attribute names that are private, are implemented internally as maps. 
183+ // Since the User struct does not expose these maps, they are in effect immutable and will be reused from the 
184+ // original User rather than copied whenever possible. The UserBuilder has copy-on-write behavior so that it 
185+ // only makes copies of these data structures if you actually modify them. 
169186func  NewUserBuilderFromUser (fromUser  User ) UserBuilder  {
170187	builder  :=  & userBuilderImpl {
171- 		key :       fromUser .key ,
172- 		secondary : fromUser .secondary ,
173- 		ip :        fromUser .ip ,
174- 		country :   fromUser .country ,
175- 		email :     fromUser .email ,
176- 		firstName : fromUser .firstName ,
177- 		lastName :  fromUser .lastName ,
178- 		avatar :    fromUser .avatar ,
179- 		name :      fromUser .name ,
180- 		anonymous : fromUser .anonymous ,
188+ 		key :                     fromUser .key ,
189+ 		secondary :               fromUser .secondary ,
190+ 		ip :                      fromUser .ip ,
191+ 		country :                 fromUser .country ,
192+ 		email :                   fromUser .email ,
193+ 		firstName :               fromUser .firstName ,
194+ 		lastName :                fromUser .lastName ,
195+ 		avatar :                  fromUser .avatar ,
196+ 		name :                    fromUser .name ,
197+ 		anonymous :               fromUser .anonymous ,
198+ 		privateAttrs :            fromUser .privateAttributes ,
199+ 		privateAttrsCopyOnWrite : true ,
181200	}
182201	if  fromUser .custom .Count () >  0  {
183- 		builder .custom  =  ldvalue .ObjectBuildWithCapacity (fromUser .custom .Count ())
184- 		fromUser .custom .Enumerate (func (index  int , key  string , value  ldvalue.Value ) bool  {
185- 			builder .custom .Set (key , value )
186- 			return  true 
187- 		})
188- 	}
189- 	if  len (fromUser .privateAttributes ) >  0  {
190- 		builder .privateAttrs  =  make (map [UserAttribute ]struct {}, len (fromUser .privateAttributes ))
191- 		for  name  :=  range  fromUser .privateAttributes  {
192- 			builder .privateAttrs [name ] =  struct {}{}
193- 		}
202+ 		builder .custom  =  ldvalue .ValueMapBuildFromMap (fromUser .custom )
194203	}
195204	return  builder 
196205}
@@ -252,12 +261,22 @@ func (b *userBuilderImpl) Anonymous(value bool) UserBuilder {
252261
253262func  (b  * userBuilderImpl ) Custom (attribute  string , value  ldvalue.Value ) UserBuilderCanMakeAttributePrivate  {
254263	if  b .custom  ==  nil  {
255- 		b .custom  =  ldvalue .ObjectBuild ()
264+ 		b .custom  =  ldvalue .ValueMapBuild ()
256265	}
257266	b .custom .Set (attribute , value )
258267	return  b .canMakeAttributePrivate (UserAttribute (attribute ))
259268}
260269
270+ func  (b  * userBuilderImpl ) CustomAll (valueMap  ldvalue.ValueMap ) UserBuilderCanMakeAttributePrivate  {
271+ 	if  valueMap .Count () ==  0  {
272+ 		b .custom  =  nil 
273+ 	} else  {
274+ 		b .custom  =  ldvalue .ValueMapBuildFromMap (valueMap )
275+ 	}
276+ 	b .lastAttributeCanMakePrivate  =  "" 
277+ 	return  b 
278+ }
279+ 
261280func  (b  * userBuilderImpl ) SetAttribute (
262281	attribute  UserAttribute ,
263282	value  ldvalue.Value ,
@@ -312,36 +331,38 @@ func (b *userBuilderImpl) SetAttribute(
312331
313332func  (b  * userBuilderImpl ) Build () User  {
314333	u  :=  User {
315- 		key :       b .key ,
316- 		secondary : b .secondary ,
317- 		ip :        b .ip ,
318- 		country :   b .country ,
319- 		email :     b .email ,
320- 		firstName : b .firstName ,
321- 		lastName :  b .lastName ,
322- 		avatar :    b .avatar ,
323- 		name :      b .name ,
324- 		anonymous : b .anonymous ,
334+ 		key :               b .key ,
335+ 		secondary :         b .secondary ,
336+ 		ip :                b .ip ,
337+ 		country :           b .country ,
338+ 		email :             b .email ,
339+ 		firstName :         b .firstName ,
340+ 		lastName :          b .lastName ,
341+ 		avatar :            b .avatar ,
342+ 		name :              b .name ,
343+ 		anonymous :         b .anonymous ,
344+ 		privateAttributes : b .privateAttrs ,
325345	}
326346	if  b .custom  !=  nil  {
327347		u .custom  =  b .custom .Build ()
328348	}
329- 	if  len (b .privateAttrs ) >  0  {
330- 		p  :=  make (map [UserAttribute ]struct {}, len (b .privateAttrs ))
331- 		for  key  :=  range  b .privateAttrs  {
332- 			p [key ] =  struct {}{}
333- 		}
334- 		u .privateAttributes  =  p 
335- 	}
349+ 	b .privateAttrsCopyOnWrite  =  true 
336350	return  u 
337351}
338352
339353func  (b  * userBuilderImpl ) AsPrivateAttribute () UserBuilder  {
340354	if  b .lastAttributeCanMakePrivate  !=  ""  {
341355		if  b .privateAttrs  ==  nil  {
342356			b .privateAttrs  =  make (map [UserAttribute ]struct {})
357+ 		} else  if  b .privateAttrsCopyOnWrite  {
358+ 			copied  :=  make (map [UserAttribute ]struct {}, len (b .privateAttrs ))
359+ 			for  name  :=  range  b .privateAttrs  {
360+ 				copied [name ] =  struct {}{}
361+ 			}
362+ 			b .privateAttrs  =  copied 
343363		}
344364		b .privateAttrs [b .lastAttributeCanMakePrivate ] =  struct {}{}
365+ 		b .privateAttrsCopyOnWrite  =  false 
345366	}
346367	return  b 
347368}
0 commit comments