github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/tools/list.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package tools
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"strings"
    10  
    11  	"github.com/juju/utils/set"
    12  
    13  	"github.com/juju/juju/version"
    14  )
    15  
    16  // List holds tools available in an environment. The order of tools within
    17  // a List is not significant.
    18  type List []*Tools
    19  
    20  var ErrNoMatches = errors.New("no matching tools available")
    21  
    22  // String returns the versions of the tools in src, separated by semicolons.
    23  func (src List) String() string {
    24  	names := make([]string, len(src))
    25  	for i, tools := range src {
    26  		names[i] = tools.Version.String()
    27  	}
    28  	return strings.Join(names, ";")
    29  }
    30  
    31  // AllSeries returns all series for which some tools in src were built.
    32  func (src List) AllSeries() []string {
    33  	return src.collect(func(tools *Tools) string {
    34  		return tools.Version.Series
    35  	})
    36  }
    37  
    38  // OneSeries returns the single series for which all tools in src were built.
    39  func (src List) OneSeries() string {
    40  	series := src.AllSeries()
    41  	if len(series) != 1 {
    42  		panic(fmt.Errorf("should have gotten tools for one series, got %v", series))
    43  	}
    44  	return series[0]
    45  }
    46  
    47  // Arches returns all architectures for which some tools in src were built.
    48  func (src List) Arches() []string {
    49  	return src.collect(func(tools *Tools) string {
    50  		return tools.Version.Arch
    51  	})
    52  }
    53  
    54  // collect calls f on all values in src and returns an alphabetically
    55  // ordered list of the returned results without duplicates.
    56  func (src List) collect(f func(*Tools) string) []string {
    57  	seen := make(set.Strings)
    58  	for _, tools := range src {
    59  		seen.Add(f(tools))
    60  	}
    61  	return seen.SortedValues()
    62  }
    63  
    64  // URLs returns download URLs for the tools in src, keyed by binary version.
    65  func (src List) URLs() map[version.Binary]string {
    66  	result := map[version.Binary]string{}
    67  	for _, tools := range src {
    68  		result[tools.Version] = tools.URL
    69  	}
    70  	return result
    71  }
    72  
    73  // Newest returns the greatest version in src, and the tools with that version.
    74  func (src List) Newest() (version.Number, List) {
    75  	var result List
    76  	var best version.Number
    77  	for _, tools := range src {
    78  		if best.Compare(tools.Version.Number) < 0 {
    79  			// Found new best number; reset result list.
    80  			best = tools.Version.Number
    81  			result = append(result[:0], tools)
    82  		} else if tools.Version.Number == best {
    83  			result = append(result, tools)
    84  		}
    85  	}
    86  	return best, result
    87  }
    88  
    89  // NewestCompatible returns the most recent version compatible with
    90  // base, i.e. with the same major and minor numbers and greater or
    91  // equal patch and build numbers.
    92  func (src List) NewestCompatible(base version.Number) (newest version.Number, found bool) {
    93  	newest = base
    94  	found = false
    95  	for _, tool := range src {
    96  		toolVersion := tool.Version.Number
    97  		if newest == toolVersion {
    98  			found = true
    99  		} else if newest.Compare(toolVersion) < 0 &&
   100  			toolVersion.Major == newest.Major &&
   101  			toolVersion.Minor == newest.Minor {
   102  			newest = toolVersion
   103  			found = true
   104  		}
   105  	}
   106  	return newest, found
   107  }
   108  
   109  // Exclude returns the tools in src that are not in excluded.
   110  func (src List) Exclude(excluded List) List {
   111  	ignore := make(map[version.Binary]bool, len(excluded))
   112  	for _, tool := range excluded {
   113  		ignore[tool.Version] = true
   114  	}
   115  	var result List
   116  	for _, tool := range src {
   117  		if !ignore[tool.Version] {
   118  			result = append(result, tool)
   119  		}
   120  	}
   121  	return result
   122  }
   123  
   124  // Match returns a List, derived from src, containing only those tools that
   125  // match the supplied Filter. If no tools match, it returns ErrNoMatches.
   126  func (src List) Match(f Filter) (List, error) {
   127  	var result List
   128  	for _, tools := range src {
   129  		if f.match(tools) {
   130  			result = append(result, tools)
   131  		}
   132  	}
   133  	if len(result) == 0 {
   134  		return nil, ErrNoMatches
   135  	}
   136  	return result, nil
   137  }
   138  
   139  func (l List) Len() int           { return len(l) }
   140  func (l List) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }
   141  func (l List) Less(i, j int) bool { return l[i].Version.String() < l[j].Version.String() }
   142  
   143  // Filter holds criteria for choosing tools.
   144  type Filter struct {
   145  	// Number, if non-zero, causes the filter to match only tools with
   146  	// that exact version number.
   147  	Number version.Number
   148  
   149  	// Series, if not empty, causes the filter to match only tools with
   150  	// that series.
   151  	Series string
   152  
   153  	// Arch, if not empty, causes the filter to match only tools with
   154  	// that architecture.
   155  	Arch string
   156  }
   157  
   158  // match returns true if the supplied tools match f.
   159  func (f Filter) match(tools *Tools) bool {
   160  	if f.Number != version.Zero && tools.Version.Number != f.Number {
   161  		return false
   162  	}
   163  	if f.Series != "" && tools.Version.Series != f.Series {
   164  		return false
   165  	}
   166  	if f.Arch != "" && tools.Version.Arch != f.Arch {
   167  		return false
   168  	}
   169  	return true
   170  }