cuelang.org/go@v0.10.1/pkg/list/list.go (about)

     1  // Copyright 2019 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package list contains functions for manipulating and examining lists.
    16  package list
    17  
    18  import (
    19  	"fmt"
    20  	"slices"
    21  	"sort"
    22  
    23  	"cuelang.org/go/cue"
    24  	"cuelang.org/go/cue/errors"
    25  	"cuelang.org/go/cue/token"
    26  	"cuelang.org/go/internal/core/adt"
    27  	"cuelang.org/go/internal/pkg"
    28  )
    29  
    30  // Drop reports the suffix of list x after the first n elements,
    31  // or [] if n > len(x).
    32  //
    33  // For instance:
    34  //
    35  //	Drop([1, 2, 3, 4], 2)
    36  //
    37  // results in
    38  //
    39  //	[3, 4]
    40  func Drop(x []cue.Value, n int) ([]cue.Value, error) {
    41  	if n < 0 {
    42  		return nil, fmt.Errorf("negative index")
    43  	}
    44  
    45  	if n > len(x) {
    46  		return []cue.Value{}, nil
    47  	}
    48  
    49  	return x[n:], nil
    50  }
    51  
    52  // TODO: disable Flatten until we know the right default for depth.
    53  //       The right time to determine is at least some point after the query
    54  //       extensions are introduced, which may provide flatten functionality
    55  //       natively.
    56  //
    57  // // Flatten reports a flattened sequence of the list xs by expanding any elements
    58  // // that are lists.
    59  // //
    60  // // For instance:
    61  // //
    62  // //    Flatten([1, [[2, 3], []], [4]])
    63  // //
    64  // // results in
    65  // //
    66  // //    [1, 2, 3, 4]
    67  // //
    68  // func Flatten(xs cue.Value) ([]cue.Value, error) {
    69  // 	var flatten func(cue.Value) ([]cue.Value, error)
    70  // 	flatten = func(xs cue.Value) ([]cue.Value, error) {
    71  // 		var res []cue.Value
    72  // 		iter, err := xs.List()
    73  // 		if err != nil {
    74  // 			return nil, err
    75  // 		}
    76  // 		for iter.Next() {
    77  // 			val := iter.Value()
    78  // 			if val.Kind() == cue.ListKind {
    79  // 				vals, err := flatten(val)
    80  // 				if err != nil {
    81  // 					return nil, err
    82  // 				}
    83  // 				res = append(res, vals...)
    84  // 			} else {
    85  // 				res = append(res, val)
    86  // 			}
    87  // 		}
    88  // 		return res, nil
    89  // 	}
    90  // 	return flatten(xs)
    91  // }
    92  
    93  // FlattenN reports a flattened sequence of the list xs by expanding any elements
    94  // depth levels deep. If depth is negative all elements are expanded.
    95  //
    96  // For instance:
    97  //
    98  //	FlattenN([1, [[2, 3], []], [4]], 1)
    99  //
   100  // results in
   101  //
   102  //	[1, [2, 3], [], 4]
   103  func FlattenN(xs cue.Value, depth int) ([]cue.Value, error) {
   104  	var flattenN func(cue.Value, int) ([]cue.Value, error)
   105  	flattenN = func(xs cue.Value, depth int) ([]cue.Value, error) {
   106  		var res []cue.Value
   107  		iter, err := xs.List()
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  		for iter.Next() {
   112  			val, _ := iter.Value().Default()
   113  			if val.Kind() == cue.ListKind && depth != 0 {
   114  				d := depth - 1
   115  				values, err := flattenN(val, d)
   116  				if err != nil {
   117  					return nil, err
   118  				}
   119  				res = append(res, values...)
   120  			} else {
   121  				res = append(res, val)
   122  			}
   123  		}
   124  		return res, nil
   125  	}
   126  	return flattenN(xs, depth)
   127  }
   128  
   129  // Repeat returns a new list consisting of count copies of list x.
   130  //
   131  // For instance:
   132  //
   133  //	Repeat([1, 2], 2)
   134  //
   135  // results in
   136  //
   137  //	[1, 2, 1, 2]
   138  func Repeat(x []cue.Value, count int) ([]cue.Value, error) {
   139  	if count < 0 {
   140  		return nil, fmt.Errorf("negative count")
   141  	}
   142  	var a []cue.Value
   143  	for range count {
   144  		a = append(a, x...)
   145  	}
   146  	return a, nil
   147  }
   148  
   149  // Concat takes a list of lists and concatenates them.
   150  //
   151  // Concat([a, b, c]) is equivalent to
   152  //
   153  //	[for x in a {x}, for x in b {x}, for x in c {x}]
   154  func Concat(a []cue.Value) ([]cue.Value, error) {
   155  	var res []cue.Value
   156  	for _, e := range a {
   157  		iter, err := e.List()
   158  		if err != nil {
   159  			return nil, err
   160  		}
   161  		for iter.Next() {
   162  			res = append(res, iter.Value())
   163  		}
   164  	}
   165  	return res, nil
   166  }
   167  
   168  // Take reports the prefix of length n of list x, or x itself if n > len(x).
   169  //
   170  // For instance:
   171  //
   172  //	Take([1, 2, 3, 4], 2)
   173  //
   174  // results in
   175  //
   176  //	[1, 2]
   177  func Take(x []cue.Value, n int) ([]cue.Value, error) {
   178  	if n < 0 {
   179  		return nil, fmt.Errorf("negative index")
   180  	}
   181  
   182  	if n > len(x) {
   183  		return x, nil
   184  	}
   185  
   186  	return x[:n], nil
   187  }
   188  
   189  // Slice extracts the consecutive elements from list x starting from position i
   190  // up till, but not including, position j, where 0 <= i < j <= len(x).
   191  //
   192  // For instance:
   193  //
   194  //	Slice([1, 2, 3, 4], 1, 3)
   195  //
   196  // results in
   197  //
   198  //	[2, 3]
   199  func Slice(x []cue.Value, i, j int) ([]cue.Value, error) {
   200  	if i < 0 {
   201  		return nil, fmt.Errorf("negative index")
   202  	}
   203  
   204  	if i > j {
   205  		return nil, fmt.Errorf("invalid index: %v > %v", i, j)
   206  	}
   207  
   208  	if i > len(x) {
   209  		return nil, fmt.Errorf("slice bounds out of range")
   210  	}
   211  
   212  	if j > len(x) {
   213  		return nil, fmt.Errorf("slice bounds out of range")
   214  	}
   215  
   216  	return x[i:j], nil
   217  }
   218  
   219  // Reverse reverses a list.
   220  //
   221  // For instance:
   222  //
   223  //	Reverse([1, 2, 3, 4])
   224  //
   225  // results in
   226  //
   227  //	[4, 3, 2, 1]
   228  func Reverse(x []cue.Value) []cue.Value {
   229  	slices.Reverse(x)
   230  	return x
   231  }
   232  
   233  // MinItems reports whether a has at least n items.
   234  func MinItems(list pkg.List, n int) (bool, error) {
   235  	count := len(list.Elems())
   236  	if count >= n {
   237  		return true, nil
   238  	}
   239  	code := adt.EvalError
   240  	if list.IsOpen() {
   241  		code = adt.IncompleteError
   242  	}
   243  	return false, pkg.ValidationError{B: &adt.Bottom{
   244  		Code: code,
   245  		Err:  errors.Newf(token.NoPos, "len(list) < MinItems(%[2]d) (%[1]d < %[2]d)", count, n),
   246  	}}
   247  }
   248  
   249  // MaxItems reports whether a has at most n items.
   250  func MaxItems(list pkg.List, n int) (bool, error) {
   251  	count := len(list.Elems())
   252  	if count > n {
   253  		return false, pkg.ValidationError{B: &adt.Bottom{
   254  			Code: adt.EvalError,
   255  			Err:  errors.Newf(token.NoPos, "len(list) > MaxItems(%[2]d) (%[1]d > %[2]d)", count, n),
   256  		}}
   257  	}
   258  
   259  	return true, nil
   260  }
   261  
   262  // UniqueItems reports whether all elements in the list are unique.
   263  func UniqueItems(a []cue.Value) bool {
   264  	b := []string{}
   265  	for _, v := range a {
   266  		b = append(b, fmt.Sprintf("%+v", v))
   267  	}
   268  	sort.Strings(b)
   269  	for i := 1; i < len(b); i++ {
   270  		if b[i-1] == b[i] {
   271  			return false
   272  		}
   273  	}
   274  	return true
   275  }
   276  
   277  // Contains reports whether v is contained in a. The value must be a
   278  // comparable value.
   279  func Contains(a []cue.Value, v cue.Value) bool {
   280  	for _, w := range a {
   281  		if v.Equals(w) {
   282  			return true
   283  		}
   284  	}
   285  	return false
   286  }