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