github.com/docker/libcompose@v0.4.1-0.20210616120443-2a046c0bdbf2/utils/util.go (about)

     1  package utils
     2  
     3  import (
     4  	"encoding/json"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/sirupsen/logrus"
     9  
    10  	"gopkg.in/yaml.v2"
    11  )
    12  
    13  // InParallel holds a pool and a waitgroup to execute tasks in parallel and to be able
    14  // to wait for completion of all tasks.
    15  type InParallel struct {
    16  	wg   sync.WaitGroup
    17  	pool sync.Pool
    18  }
    19  
    20  // Add runs the specified task in parallel and adds it to the waitGroup.
    21  func (i *InParallel) Add(task func() error) {
    22  	i.wg.Add(1)
    23  
    24  	go func() {
    25  		defer i.wg.Done()
    26  		err := task()
    27  		if err != nil {
    28  			i.pool.Put(err)
    29  		}
    30  	}()
    31  }
    32  
    33  // Wait waits for all tasks to complete and returns the latest error encountered if any.
    34  func (i *InParallel) Wait() error {
    35  	i.wg.Wait()
    36  	obj := i.pool.Get()
    37  	if err, ok := obj.(error); ok {
    38  		return err
    39  	}
    40  	return nil
    41  }
    42  
    43  // ConvertByJSON converts a struct (src) to another one (target) using json marshalling/unmarshalling.
    44  // If the structure are not compatible, this will throw an error as the unmarshalling will fail.
    45  func ConvertByJSON(src, target interface{}) error {
    46  	newBytes, err := json.Marshal(src)
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	err = json.Unmarshal(newBytes, target)
    52  	if err != nil {
    53  		logrus.Errorf("Failed to unmarshall: %v\n%s", err, string(newBytes))
    54  	}
    55  	return err
    56  }
    57  
    58  // Convert converts a struct (src) to another one (target) using yaml marshalling/unmarshalling.
    59  // If the structure are not compatible, this will throw an error as the unmarshalling will fail.
    60  func Convert(src, target interface{}) error {
    61  	newBytes, err := yaml.Marshal(src)
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	err = yaml.Unmarshal(newBytes, target)
    67  	if err != nil {
    68  		logrus.Errorf("Failed to unmarshall: %v\n%s", err, string(newBytes))
    69  	}
    70  	return err
    71  }
    72  
    73  // CopySlice creates an exact copy of the provided string slice
    74  func CopySlice(s []string) []string {
    75  	if s == nil {
    76  		return nil
    77  	}
    78  	r := make([]string, len(s))
    79  	copy(r, s)
    80  	return r
    81  }
    82  
    83  // CopyMap creates an exact copy of the provided string-to-string map
    84  func CopyMap(m map[string]string) map[string]string {
    85  	if m == nil {
    86  		return nil
    87  	}
    88  	r := map[string]string{}
    89  	for k, v := range m {
    90  		r[k] = v
    91  	}
    92  	return r
    93  }
    94  
    95  // FilterStringSet accepts a string set `s` (in the form of `map[string]bool`) and a filtering function `f`
    96  // and returns a string set containing only the strings `x` for which `f(x) == true`
    97  func FilterStringSet(s map[string]bool, f func(x string) bool) map[string]bool {
    98  	result := map[string]bool{}
    99  	for k := range s {
   100  		if f(k) {
   101  			result[k] = true
   102  		}
   103  	}
   104  	return result
   105  }
   106  
   107  // FilterString returns a json representation of the specified map
   108  // that is used as filter for docker.
   109  func FilterString(data map[string][]string) string {
   110  	// I can't imagine this would ever fail
   111  	bytes, _ := json.Marshal(data)
   112  	return string(bytes)
   113  }
   114  
   115  // Contains checks if the specified string (key) is present in the specified collection.
   116  func Contains(collection []string, key string) bool {
   117  	for _, value := range collection {
   118  		if value == key {
   119  			return true
   120  		}
   121  	}
   122  
   123  	return false
   124  }
   125  
   126  // Merge performs a union of two string slices: the result is an unordered slice
   127  // that includes every item from either argument exactly once
   128  func Merge(coll1, coll2 []string) []string {
   129  	m := map[string]struct{}{}
   130  	for _, v := range append(coll1, coll2...) {
   131  		m[v] = struct{}{}
   132  	}
   133  	r := make([]string, 0, len(m))
   134  	for k := range m {
   135  		r = append(r, k)
   136  	}
   137  	return r
   138  }
   139  
   140  // ConvertKeysToStrings converts map[interface{}] to map[string] recursively
   141  func ConvertKeysToStrings(item interface{}) interface{} {
   142  	switch typedDatas := item.(type) {
   143  	case map[string]interface{}:
   144  		for key, value := range typedDatas {
   145  			typedDatas[key] = ConvertKeysToStrings(value)
   146  		}
   147  		return typedDatas
   148  	case map[interface{}]interface{}:
   149  		newMap := make(map[string]interface{})
   150  		for key, value := range typedDatas {
   151  			stringKey := key.(string)
   152  			newMap[stringKey] = ConvertKeysToStrings(value)
   153  		}
   154  		return newMap
   155  	case []interface{}:
   156  		for i, value := range typedDatas {
   157  			typedDatas[i] = ConvertKeysToStrings(value)
   158  		}
   159  		return typedDatas
   160  	default:
   161  		return item
   162  	}
   163  }
   164  
   165  // DurationStrToSecondsInt converts duration string to *int in seconds
   166  func DurationStrToSecondsInt(s string) *int {
   167  	if s == "" {
   168  		return nil
   169  	}
   170  	duration, err := time.ParseDuration(s)
   171  	if err != nil {
   172  		logrus.Errorf("Failed to parse duration:%v", s)
   173  		return nil
   174  	}
   175  	r := (int)(duration.Seconds())
   176  	return &r
   177  
   178  }