github.com/containerd/nerdctl@v1.7.7/pkg/strutil/strutil.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  /*
    18     Portions from https://github.com/moby/moby/blob/v20.10.0-rc2/runconfig/opts/parse.go
    19     Copyright (C) Docker/Moby authors.
    20     Licensed under the Apache License, Version 2.0
    21     NOTICE: https://github.com/moby/moby/blob/v20.10.0-rc2/NOTICE
    22  */
    23  
    24  /*
    25     Portions from https://github.com/moby/buildkit/blob/v0.9.1/cmd/buildkitd/config.go#L35-L42
    26     Copyright (C) BuildKit authors.
    27     Licensed under the Apache License, Version 2.0
    28  */
    29  
    30  package strutil
    31  
    32  import (
    33  	"encoding/csv"
    34  	"fmt"
    35  	"reflect"
    36  	"strconv"
    37  	"strings"
    38  
    39  	"github.com/containerd/errdefs"
    40  )
    41  
    42  // ConvertKVStringsToMap is from https://github.com/moby/moby/blob/v20.10.0-rc2/runconfig/opts/parse.go
    43  //
    44  // ConvertKVStringsToMap converts ["key=value"] to {"key":"value"}
    45  func ConvertKVStringsToMap(values []string) map[string]string {
    46  	result := make(map[string]string, len(values))
    47  	for _, value := range values {
    48  		kv := strings.SplitN(value, "=", 2)
    49  		if len(kv) == 1 {
    50  			result[kv[0]] = ""
    51  		} else {
    52  			result[kv[0]] = kv[1]
    53  		}
    54  	}
    55  
    56  	return result
    57  }
    58  
    59  // InStringSlice checks whether a string is inside a string slice.
    60  // Comparison is case insensitive.
    61  //
    62  // From https://github.com/containerd/containerd/blob/7c6d710bcfc81a30ac1e8cbb2e6a4c294184f7b7/pkg/cri/util/strings.go#L21-L30
    63  func InStringSlice(ss []string, str string) bool {
    64  	for _, s := range ss {
    65  		if strings.EqualFold(s, str) {
    66  			return true
    67  		}
    68  	}
    69  	return false
    70  }
    71  
    72  func DedupeStrSlice(in []string) []string {
    73  	m := make(map[string]struct{})
    74  	var res []string
    75  	for _, s := range in {
    76  		if _, ok := m[s]; !ok {
    77  			res = append(res, s)
    78  			m[s] = struct{}{}
    79  		}
    80  	}
    81  	return res
    82  }
    83  
    84  // SliceToSet converts a slice of strings into a set.
    85  // In Go, a set is often represented as a map with keys as the set elements and values as boolean.
    86  // This function iterates over the slice, adding each string as a key in the map.
    87  // The corresponding map value is set to true, serving as a placeholder.
    88  // The resulting map can be used to quickly check the presence of an element in the set.
    89  func SliceToSet(in []string) map[string]bool {
    90  	set := make(map[string]bool)
    91  	for _, s := range in {
    92  		set[s] = true
    93  	}
    94  	return set
    95  }
    96  
    97  // ParseCSVMap parses a string like "foo=x,bar=y" into a map
    98  func ParseCSVMap(s string) (map[string]string, error) {
    99  	csvR := csv.NewReader(strings.NewReader(s))
   100  	// s can contains quotes, but the csv reader needs LazyQuotes to recognize quotes as values.
   101  	csvR.LazyQuotes = true
   102  	ra, err := csvR.ReadAll()
   103  	if err != nil {
   104  		return nil, fmt.Errorf("cannot parse %q: %w", s, err)
   105  	}
   106  	if len(ra) != 1 {
   107  		return nil, fmt.Errorf("expected a single line, got %d lines: %w", len(ra), errdefs.ErrInvalidArgument)
   108  	}
   109  	fields := ra[0]
   110  	m := make(map[string]string)
   111  	for _, field := range fields {
   112  		kv := strings.SplitN(field, "=", 2)
   113  		if len(kv) == 2 {
   114  			m[kv[0]] = kv[1]
   115  		} else {
   116  			m[kv[0]] = ""
   117  		}
   118  	}
   119  	return m, nil
   120  }
   121  
   122  func TrimStrSliceRight(base, extra []string) []string {
   123  	for i := 0; i < len(base); i++ {
   124  		if reflect.DeepEqual(base[i:], extra) {
   125  			return base[:i]
   126  		}
   127  	}
   128  	return base
   129  }
   130  
   131  func ReverseStrSlice(in []string) []string {
   132  	out := make([]string, len(in))
   133  	for i, v := range in {
   134  		out[len(in)-i-1] = v
   135  	}
   136  	return out
   137  }
   138  
   139  // ParseBoolOrAuto returns (nil, nil) if s is "auto"
   140  // https://github.com/moby/buildkit/blob/v0.9.1/cmd/buildkitd/config.go#L35-L42
   141  func ParseBoolOrAuto(s string) (*bool, error) {
   142  	if s == "" || strings.ToLower(s) == "auto" {
   143  		return nil, nil
   144  	}
   145  	b, err := strconv.ParseBool(s)
   146  	return &b, err
   147  }