1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
# frozen_string_literal: true
module Bundler
class Index
include Enumerable
def self.build
i = new
yield i
i
end
attr_reader :specs, :duplicates, :sources
protected :specs, :duplicates
RUBY = "ruby"
NULL = "\0"
def initialize
@sources = []
@cache = {}
@specs = {}
@duplicates = {}
end
def initialize_copy(o)
@sources = o.sources.dup
@cache = {}
@specs = {}
@duplicates = {}
o.specs.each do |name, hash|
@specs[name] = hash.dup
end
o.duplicates.each do |name, array|
@duplicates[name] = array.dup
end
end
def inspect
"#<#{self.class}:0x#{object_id} sources=#{sources.map(&:inspect)} specs.size=#{specs.size}>"
end
def empty?
each { return false }
true
end
def search_all(name, &blk)
return enum_for(:search_all, name) unless blk
specs_by_name(name).each(&blk)
@duplicates[name]&.each(&blk)
@sources.each {|source| source.search_all(name, &blk) }
end
# Search this index's specs, and any source indexes that this index knows
# about, returning all of the results.
def search(query)
results = local_search(query)
return results unless @sources.any?
@sources.each do |source|
results = safe_concat(results, source.search(query))
end
results.uniq!(&:full_name) unless results.empty? # avoid modifying frozen EMPTY_SEARCH
results
end
alias_method :[], :search
def local_search(query)
case query
when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query)
when String then specs_by_name(query)
when Array then specs_by_name_and_version(*query)
else
raise "You can't search for a #{query.inspect}."
end
end
def add(spec)
(@specs[spec.name] ||= {}).store(spec.full_name, spec)
end
alias_method :<<, :add
def each(&blk)
return enum_for(:each) unless blk
specs.values.each do |spec_sets|
spec_sets.values.each(&blk)
end
sources.each {|s| s.each(&blk) }
self
end
def spec_names
names = specs.keys + sources.map(&:spec_names)
names.uniq!
names
end
def unmet_dependency_names
dependency_names.select do |name|
search(name).empty?
end
end
def dependency_names
names = []
each do |spec|
spec.dependencies.each do |dep|
next if dep.type == :development
names << dep.name
end
end
names.uniq
end
# Combines indexes proritizing existing specs, like `Hash#reverse_merge!`
# Duplicate specs found in `other` are stored in `@duplicates`.
def use(other)
return unless other
other.each do |spec|
exist?(spec) ? add_duplicate(spec) : add(spec)
end
self
end
# Combines indexes proritizing specs from `other`, like `Hash#merge!`
# Duplicate specs found in `self` are saved in `@duplicates`.
def merge!(other)
return unless other
other.each do |spec|
if existing = find_by_spec(spec)
add_duplicate(existing)
end
add spec
end
self
end
def size
@sources.inject(@specs.size) do |size, source|
size += source.size
end
end
# Whether all the specs in self are in other
def subset?(other)
all? do |spec|
other_spec = other[spec].first
other_spec && dependencies_eql?(spec, other_spec) && spec.source == other_spec.source
end
end
def dependencies_eql?(spec, other_spec)
deps = spec.dependencies.select {|d| d.type != :development }
other_deps = other_spec.dependencies.select {|d| d.type != :development }
deps.sort == other_deps.sort
end
def add_source(index)
raise ArgumentError, "Source must be an index, not #{index.class}" unless index.is_a?(Index)
@sources << index
@sources.uniq! # need to use uniq! here instead of checking for the item before adding
end
private
def safe_concat(a, b)
return a if b.empty?
return b if a.empty?
a.concat(b)
end
def add_duplicate(spec)
(@duplicates[spec.name] ||= []) << spec
end
def specs_by_name_and_version(name, version)
results = @specs[name]&.values
return EMPTY_SEARCH unless results
results.select! {|spec| spec.version == version }
results
end
def specs_by_name(name)
@specs[name]&.values || EMPTY_SEARCH
end
EMPTY_SEARCH = [].freeze
def search_by_spec(spec)
spec = find_by_spec(spec)
spec ? [spec] : EMPTY_SEARCH
end
def find_by_spec(spec)
@specs[spec.name]&.fetch(spec.full_name, nil)
end
def exist?(spec)
@specs[spec.name]&.key?(spec.full_name)
end
end
end
|