2022-04-25 12:24:44 -05:00

163 lines
5.4 KiB
Ruby

require "digest/sha2"
module VagrantPlugins
module CommandServe
# Provides value mapping to ease interaction
# with protobuf and clients
class Mappers
autoload :Internal, Vagrant.source_root.join("plugins/commands/serve/mappers/internal").to_s
autoload :Mapper, Vagrant.source_root.join("plugins/commands/serve/mappers/mapper").to_s
# @return [Array<Object>] arguments provided to all mapper calls
attr_reader :known_arguments
# @return [Array<Mapper>] list of mappers
attr_reader :mappers
# @return [Util::Cacher] cached mapped values
attr_accessor :cacher
# Create a new mappers instance. Any arguments provided will be
# available to all mapper calls
def initialize(*args)
@known_arguments = args
@mappers = Mapper.registered.map(&:new)
@identifiers = []
end
# Add an argument to be included with mapping calls
#
# @param v [Object] Argument value
# @return [Object]
def add_argument(v)
known_arguments << v
@identifiers << identify(v)
v
end
def cached(key)
return false if cacher.nil?
cacher[key]
end
def cache_key(*args)
key = Digest::SHA256.new
@identifiers.each { |i| key << i }
args.each do |a|
key << identify(a)
end
key.hexdigest
end
def identify(thing)
thing.respond_to?(:cacher_id) ?
thing.cacher_id.to_s :
thing.object_id.to_s
end
# Map a given value
#
# @param value [Object] Value to map
# @param to [Class] Resultant type (optional)
# @return [Object]
def map(value, *extra_args, to: nil)
# If we already processed this value, return cached value
cache_key = cache_key(value, *extra_args, to)
if c = cached(cache_key)
return c
end
if value.nil? && to
val = (extra_args + known_arguments).detect do |item|
item.is_a?(to)
end
return val if val
end
# If we don't have a desired final type, test for mappers
# that are satisfied by the arguments we have and run that
# directly
if to.nil?
args = ([value] + extra_args + known_arguments).compact
matched_mappers = mappers.find_all do |m|
if m.satisfied_by?(*args)
if to
m.output.ancestors.include?(to)
else
true
end
else
false
end
end
if matched_mappers.empty?
raise ArgumentError,
"Failed to locate valid mapper. (source: #{value ? value.class : 'none'} " \
"destination: #{to ? to : 'undefined'} - args: #{args.map(&:class).inspect} )" \
end
if matched_mappers.size > 1
m = matched_mappers.detect do |mp|
mp.inputs.detect do |mi|
mi.valid?(args.first)
end
end
if m.nil?
raise ArgumentError,
"Multiple valid mappers found: #{matched_mappers.map(&:class).inspect} (source: #{value ? value.class : 'none'} " \
"destination: #{to ? to : 'undefined'} - args: #{args.map(&:class).inspect} )"
end
matched_mappers = [m]
end
mapper = matched_mappers.first
margs = mapper.determine_inputs(*args)
result = mapper.call(*margs)
else
args = ([value] + extra_args).compact
m_graph = Internal::Graph::Mappers.new(
output_type: to,
mappers: self,
input_values: args,
)
result = m_graph.execute
end
cacher[cache_key] = result if cacher
result
end
# Generate the given type based on given and/or
# added arguments
def generate(*args, type:)
map(nil, *args, to: type)
end
# Map values provided by a FuncSpec request into
# actual values
#
# @param spec [SDK::FuncSpec::Spec]
# @return [Array<Object>, Object]
def funcspec_map(spec)
result = spec.args.map do |arg|
map(arg)
end
if result.size == 1
return result.first
end
# NOTE: the spec will have the order of the arguments
# shifted one. not sure why, but we can just work around
# it here for now.
result.push(result.shift)
end
end
end
end
require Vagrant.source_root.join("plugins/commands/serve/mappers/guest.rb").to_s
require Vagrant.source_root.join("plugins/commands/serve/mappers/machine.rb").to_s
require Vagrant.source_root.join("plugins/commands/serve/mappers/project.rb").to_s
require Vagrant.source_root.join("plugins/commands/serve/mappers/target.rb").to_s
require Vagrant.source_root.join("plugins/commands/serve/mappers/target_index.rb").to_s
require Vagrant.source_root.join("plugins/commands/serve/mappers/terminal.rb").to_s
require Vagrant.source_root.join("plugins/commands/serve/mappers/command.rb").to_s
require Vagrant.source_root.join("plugins/commands/serve/mappers/capability.rb").to_s
require Vagrant.source_root.join("plugins/commands/serve/mappers/state_bag.rb").to_s
require Vagrant.source_root.join("plugins/commands/serve/mappers/ui.rb").to_s
require Vagrant.source_root.join("plugins/commands/serve/mappers/environment.rb").to_s