...

Source file src/cuelang.org/go/pkg/list/list.go

Documentation: cuelang.org/go/pkg/list

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

View as plain text