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 }