// Copyright 2019 CUE Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package list contains functions for manipulating and examining lists. package list import ( "fmt" "sort" "cuelang.org/go/cue" "cuelang.org/go/cue/errors" "cuelang.org/go/cue/token" "cuelang.org/go/internal/core/adt" "cuelang.org/go/internal/pkg" ) // Drop reports the suffix of list x after the first n elements, // or [] if n > len(x). // // For instance: // // Drop([1, 2, 3, 4], 2) // // results in // // [3, 4] func Drop(x []cue.Value, n int) ([]cue.Value, error) { if n < 0 { return nil, fmt.Errorf("negative index") } if n > len(x) { return []cue.Value{}, nil } return x[n:], nil } // TODO: disable Flatten until we know the right default for depth. // The right time to determine is at least some point after the query // extensions are introduced, which may provide flatten functionality // natively. // // // Flatten reports a flattened sequence of the list xs by expanding any elements // // that are lists. // // // // For instance: // // // // Flatten([1, [[2, 3], []], [4]]) // // // // results in // // // // [1, 2, 3, 4] // // // func Flatten(xs cue.Value) ([]cue.Value, error) { // var flatten func(cue.Value) ([]cue.Value, error) // flatten = func(xs cue.Value) ([]cue.Value, error) { // var res []cue.Value // iter, err := xs.List() // if err != nil { // return nil, err // } // for iter.Next() { // val := iter.Value() // if val.Kind() == cue.ListKind { // vals, err := flatten(val) // if err != nil { // return nil, err // } // res = append(res, vals...) // } else { // res = append(res, val) // } // } // return res, nil // } // return flatten(xs) // } // FlattenN reports a flattened sequence of the list xs by expanding any elements // depth levels deep. If depth is negative all elements are expanded. // // For instance: // // FlattenN([1, [[2, 3], []], [4]], 1) // // results in // // [1, [2, 3], [], 4] func FlattenN(xs cue.Value, depth int) ([]cue.Value, error) { var flattenN func(cue.Value, int) ([]cue.Value, error) flattenN = func(xs cue.Value, depth int) ([]cue.Value, error) { var res []cue.Value iter, err := xs.List() if err != nil { return nil, err } for iter.Next() { val, _ := iter.Value().Default() if val.Kind() == cue.ListKind && depth != 0 { d := depth - 1 values, err := flattenN(val, d) if err != nil { return nil, err } res = append(res, values...) } else { res = append(res, val) } } return res, nil } return flattenN(xs, depth) } // Repeat returns a new list consisting of count copies of list x. // // For instance: // // Repeat([1, 2], 2) // // results in // // [1, 2, 1, 2] func Repeat(x []cue.Value, count int) ([]cue.Value, error) { if count < 0 { return nil, fmt.Errorf("negative count") } var a []cue.Value for i := 0; i < count; i++ { a = append(a, x...) } return a, nil } // Concat takes a list of lists and concatenates them. // // Concat([a, b, c]) is equivalent to // // [for x in a {x}, for x in b {x}, for x in c {x}] func Concat(a []cue.Value) ([]cue.Value, error) { var res []cue.Value for _, e := range a { iter, err := e.List() if err != nil { return nil, err } for iter.Next() { res = append(res, iter.Value()) } } return res, nil } // Take reports the prefix of length n of list x, or x itself if n > len(x). // // For instance: // // Take([1, 2, 3, 4], 2) // // results in // // [1, 2] func Take(x []cue.Value, n int) ([]cue.Value, error) { if n < 0 { return nil, fmt.Errorf("negative index") } if n > len(x) { return x, nil } return x[:n], nil } // Slice extracts the consecutive elements from list x starting from position i // up till, but not including, position j, where 0 <= i < j <= len(x). // // For instance: // // Slice([1, 2, 3, 4], 1, 3) // // results in // // [2, 3] func Slice(x []cue.Value, i, j int) ([]cue.Value, error) { if i < 0 { return nil, fmt.Errorf("negative index") } if i > j { return nil, fmt.Errorf("invalid index: %v > %v", i, j) } if i > len(x) { return nil, fmt.Errorf("slice bounds out of range") } if j > len(x) { return nil, fmt.Errorf("slice bounds out of range") } return x[i:j], nil } // MinItems reports whether a has at least n items. func MinItems(list pkg.List, n int) (bool, error) { count := len(list.Elems()) if count >= n { return true, nil } code := adt.EvalError if list.IsOpen() { code = adt.IncompleteError } return false, pkg.ValidationError{B: &adt.Bottom{ Code: code, Err: errors.Newf(token.NoPos, "len(list) < MinItems(%[2]d) (%[1]d < %[2]d)", count, n), }} } // MaxItems reports whether a has at most n items. func MaxItems(list pkg.List, n int) (bool, error) { count := len(list.Elems()) if count > n { return false, pkg.ValidationError{B: &adt.Bottom{ Code: adt.EvalError, Err: errors.Newf(token.NoPos, "len(list) > MaxItems(%[2]d) (%[1]d > %[2]d)", count, n), }} } return true, nil } // UniqueItems reports whether all elements in the list are unique. func UniqueItems(a []cue.Value) bool { b := []string{} for _, v := range a { b = append(b, fmt.Sprintf("%+v", v)) } sort.Strings(b) for i := 1; i < len(b); i++ { if b[i-1] == b[i] { return false } } return true } // Contains reports whether v is contained in a. The value must be a // comparable value. func Contains(a []cue.Value, v cue.Value) bool { for _, w := range a { if v.Equals(w) { return true } } return false }