...

Source file src/github.com/launchdarkly/go-sdk-common/v3/ldvalue/value_array.go

Documentation: github.com/launchdarkly/go-sdk-common/v3/ldvalue

     1  package ldvalue
     2  
     3  import (
     4  	"golang.org/x/exp/slices"
     5  )
     6  
     7  // we reuse this for all non-nil zero-length ValueArray instances
     8  var emptyArray = []Value{} //nolint:gochecknoglobals
     9  
    10  // ValueArray is an immutable array of [Value]s.
    11  //
    12  // This is used internally to hold the contents of a JSON array in a Value. You can also use it
    13  // separately in any context where you know that the data must be array-like, rather than any of the
    14  // other types that a Value can be.
    15  //
    16  // The wrapped slice is not directly accessible, so it cannot be modified. You can obtain a copy of
    17  // it with [ValueArray.AsSlice] if necessary.
    18  //
    19  // Like a Go slice, there is a distinction between an array in a nil state-- which is the zero
    20  // value of ValueArray{}-- and a non-nil aray that is empty. The former is represented in JSON as a
    21  // null; the latter is an empty JSON array [].
    22  type ValueArray struct {
    23  	data []Value
    24  }
    25  
    26  // ValueArrayBuilder is a builder created by [ValueArrayBuild], for creating immutable JSON arrays.
    27  //
    28  // A ValueArrayBuilder should not be accessed by multiple goroutines at once.
    29  type ValueArrayBuilder struct {
    30  	copyOnWrite bool
    31  	output      []Value
    32  }
    33  
    34  // Add appends an element to the array builder.
    35  func (b *ValueArrayBuilder) Add(value Value) *ValueArrayBuilder {
    36  	if b == nil {
    37  		return b
    38  	}
    39  	if b.copyOnWrite {
    40  		n := len(b.output)
    41  		newSlice := make([]Value, n, n+1)
    42  		copy(newSlice[0:n], b.output)
    43  		b.output = newSlice
    44  		b.copyOnWrite = false
    45  	}
    46  	if b.output == nil {
    47  		b.output = make([]Value, 0, 1)
    48  	}
    49  	b.output = append(b.output, value)
    50  	return b
    51  }
    52  
    53  // AddAllFromValueArray appends all elements from an existing ValueArray.
    54  func (b *ValueArrayBuilder) AddAllFromValueArray(a ValueArray) *ValueArrayBuilder {
    55  	for _, v := range a.data {
    56  		b.Add(v)
    57  	}
    58  	return b
    59  }
    60  
    61  // Build creates a ValueArray containing the previously added array elements. Continuing to modify the
    62  // same builder by calling Add after that point does not affect the returned array.
    63  func (b *ValueArrayBuilder) Build() ValueArray {
    64  	if b == nil {
    65  		return ValueArray{}
    66  	}
    67  	if b.output == nil {
    68  		return ValueArray{emptyArray}
    69  	}
    70  	b.copyOnWrite = true
    71  	return ValueArray{b.output}
    72  }
    73  
    74  // ValueArrayBuild creates a builder for constructing an immutable [ValueArray].
    75  //
    76  //	ValueArray := ldvalue.ValueArrayBuild().Add(ldvalue.Int(100)).Add(ldvalue.Int(200)).Build()
    77  func ValueArrayBuild() *ValueArrayBuilder {
    78  	return &ValueArrayBuilder{}
    79  }
    80  
    81  // ValueArrayBuildWithCapacity creates a builder for constructing an immutable [ValueArray].
    82  //
    83  // The capacity parameter is the same as the capacity of a slice, allowing you to preallocate space
    84  // if you know the number of elements; otherwise you can pass zero.
    85  //
    86  //	arrayValue := ldvalue.ValueArrayBuildWithCapacity(2).Add(ldvalue.Int(100)).Add(ldvalue.Int(200)).Build()
    87  func ValueArrayBuildWithCapacity(capacity int) *ValueArrayBuilder {
    88  	return &ValueArrayBuilder{output: make([]Value, 0, capacity)}
    89  }
    90  
    91  // ValueArrayBuildFromArray creates a builder for constructing an immutable [ValueArray], initializing it
    92  // from an existing ValueArray.
    93  //
    94  // The builder has copy-on-write behavior, so if you make no changes before calling Build(), the
    95  // original array is used as-is.
    96  func ValueArrayBuildFromArray(a ValueArray) *ValueArrayBuilder {
    97  	return &ValueArrayBuilder{output: a.data, copyOnWrite: true}
    98  }
    99  
   100  // ValueArrayOf creates a ValueArray from a list of [Value]s.
   101  //
   102  // This requires a slice copy to ensure immutability; otherwise, an existing slice could be passed
   103  // using the spread operator, and then modified. However, since Value is itself immutable, it does
   104  // not need to deep-copy each item.
   105  func ValueArrayOf(items ...Value) ValueArray {
   106  	// ValueArrayOf() with no parameters will pass nil rather than a zero-length slice; logically we
   107  	// still want it to create a non-nil array.
   108  	if items == nil {
   109  		return ValueArray{emptyArray}
   110  	}
   111  	return CopyValueArray(items)
   112  }
   113  
   114  // CopyValueArray copies an existing ordinary map to a ValueArray.
   115  //
   116  // If the parameter is nil, an uninitialized ValueArray{} is returned instead of a zero-length array.
   117  func CopyValueArray(data []Value) ValueArray {
   118  	if data == nil {
   119  		return ValueArray{}
   120  	}
   121  	if len(data) == 0 {
   122  		return ValueArray{emptyArray}
   123  	}
   124  	return ValueArray{data: slices.Clone(data)}
   125  }
   126  
   127  // CopyArbitraryValueArray copies an existing ordinary slice of values of any type to a ValueArray.
   128  // The behavior for each value is the same as [CopyArbitraryValue].
   129  //
   130  // If the parameter is nil, an uninitialized ValueArray{} is returned instead of a zero-length map.
   131  func CopyArbitraryValueArray(data []any) ValueArray {
   132  	if data == nil {
   133  		return ValueArray{}
   134  	}
   135  	a := make([]Value, len(data))
   136  	for i, v := range data {
   137  		a[i] = CopyArbitraryValue(v)
   138  	}
   139  	return ValueArray{data: a}
   140  }
   141  
   142  // IsDefined returns true if the array is non-nil.
   143  func (a ValueArray) IsDefined() bool {
   144  	return a.data != nil
   145  }
   146  
   147  // Count returns the number of elements in the array. For an uninitialized ValueArray{}, this is zero.
   148  func (a ValueArray) Count() int {
   149  	return len(a.data)
   150  }
   151  
   152  // AsValue converts the ValueArray to a Value which is either [Null]() or an array. This does not
   153  // cause any new allocations.
   154  func (a ValueArray) AsValue() Value {
   155  	if a.data == nil {
   156  		return Null()
   157  	}
   158  	return Value{valueType: ArrayType, arrayValue: a}
   159  }
   160  
   161  // Get gets a value from the array by index.
   162  //
   163  // If the index is out of range, it returns [Null]().
   164  func (a ValueArray) Get(index int) Value {
   165  	if index < 0 || index >= len(a.data) {
   166  		return Null()
   167  	}
   168  	return a.data[index]
   169  }
   170  
   171  // TryGet gets a value from the map by index, with a second return value of true if successful.
   172  //
   173  // If the index is out of range, it returns ([Null](), false).
   174  func (a ValueArray) TryGet(index int) (Value, bool) {
   175  	if index < 0 || index >= len(a.data) {
   176  		return Null(), false
   177  	}
   178  	return a.data[index], true
   179  }
   180  
   181  // AsSlice returns a copy of the wrapped data as a simple Go slice whose values are of type [Value].
   182  //
   183  // For an uninitialized ValueArray{}, this returns nil.
   184  func (a ValueArray) AsSlice() []Value {
   185  	return slices.Clone(a.data)
   186  }
   187  
   188  // AsArbitraryValueSlice returns a copy of the wrapped data as a simple Go slice whose values are
   189  // of any type. The behavior for each value is the same as [Value.AsArbitraryValue].
   190  //
   191  // For an uninitialized ValueArray{}, this returns nil.
   192  func (a ValueArray) AsArbitraryValueSlice() []any {
   193  	if a.data == nil {
   194  		return nil
   195  	}
   196  	ret := make([]any, len(a.data))
   197  	for i, v := range a.data {
   198  		ret[i] = v.AsArbitraryValue()
   199  	}
   200  	return ret
   201  }
   202  
   203  // Equal returns true if the two arrays are deeply equal. Nil and zero-length arrays are not considered
   204  // equal to each other.
   205  func (a ValueArray) Equal(other ValueArray) bool {
   206  	if a.IsDefined() != other.IsDefined() {
   207  		return false
   208  	}
   209  	return slices.EqualFunc(a.data, other.data, Value.Equal)
   210  }
   211  
   212  // Transform applies a transformation function to a ValueArray, returning a new ValueArray.
   213  //
   214  // The behavior is as follows:
   215  //
   216  // If the input value is nil or zero-length, the result is identical and the function is not called.
   217  //
   218  // Otherwise, fn is called for each value. It should return a transformed value and true, or else
   219  // return false for the second return value if the property should be dropped.
   220  func (a ValueArray) Transform(fn func(index int, value Value) (Value, bool)) ValueArray {
   221  	if len(a.data) == 0 {
   222  		return a
   223  	}
   224  	ret := a.data
   225  	startedNewSlice := false
   226  	for i, v := range a.data {
   227  		transformedValue, ok := fn(i, v)
   228  		modified := !ok || !transformedValue.Equal(v)
   229  		if modified && !startedNewSlice {
   230  			// This is the first change we've seen, so we should start building a new slice and
   231  			// retroactively add any values to it that already passed the test without changes.
   232  			startedNewSlice = true
   233  			ret = make([]Value, i, len(a.data))
   234  			copy(ret, a.data)
   235  		}
   236  		if startedNewSlice && ok {
   237  			ret = append(ret, transformedValue)
   238  		}
   239  	}
   240  	return ValueArray{ret}
   241  }
   242  
   243  // String converts the value to a string representation, equivalent to [ValueArray.JSONString].
   244  //
   245  // This method is provided because it is common to use the Stringer interface as a quick way to
   246  // summarize the contents of a value. The simplest way to do so in this case is to use the JSON
   247  // representation.
   248  func (a ValueArray) String() string {
   249  	return a.JSONString()
   250  }
   251  

View as plain text