module VagrantPlugins module CommandServe module Util module FuncSpec module Client # Generate FuncSpec proto args based on provided spec # using given args # # @param spec [SDK::FuncSpec] Spec for function # @param args [Array] List of arguments to generate from # @return [SDK::FuncSpec::Args] def generate_funcspec_args(spec, *args) logger.trace("generating funcspec args for spec: #{spec}") if !spec.is_a?(SDK::FuncSpec) raise TypeError, "Expected `#{SDK::FuncSpec.name}' but received `#{spec.class.name}'" end m_args = args.dup if respond_to?(:seeds) m_args = m_args + seeds.typed.to_a + seeds.named.map { |name, val| val = mapper.unany(val) Type::NamedArgument.new( name: name, value: val, ) } end m_args += m_args.find_all { |arg| arg.is_a?(Type::Direct) }.map(&:value).flatten SDK::FuncSpec::Args.new( args: spec.args.map { |farg| logger.trace("starting funcspec generation for #{farg}") type = mapper.find_type(farg.type) gen = mapper.generate(*m_args, named: farg.name, type: type) logger.trace("generated value for type #{type.inspect} (name: #{farg.name.inspect}) -> #{gen.class}") any = Google::Protobuf::Any.pack(gen) SDK::FuncSpec::Value.new( type: any.type_name.split("/").last, value: any, name: farg.name, ) } ) end # Convert all given args to FuncSpec::Args proto # # @param extra [Array] Extra arguments to use during mapping # @param args [Array] Arguments to be mapped # @return [SDK::FuncSpec::Args] def to_funcspec(*args, extra: []) SDK::FuncSpec::Args.new( args: args.map { |arg| if arg.is_a?(Type::NamedArgument) name = arg.name arg = arg.value else name = "" end any = mapper.map(arg, *extra, to: Google::Protobuf::Any) SDK::FuncSpec::Value.new( name: name, type: any.type_name.split("/").last, value: any, ) } ) end # Call a defined func and execute the callback. # # @param name [String,Symbol] Name of method which provides a spec and callback # (defaults to name of caller method with `_func` suffix) # @param args [Array] Optional list of arguments def run_func(*args, name: nil, func_args: []) if name.nil? name = caller_locations.first.label.to_s if name.end_with?("!") || name.end_with?("?") name = name[0, name.length - 1] end name += "_func" end if !respond_to?(name) raise ArgumentError, "Class `#{self.class}' does not contain method `##{name}'" end logger.trace("running func #{name} for spec and callback") func_args = [name] + Array(func_args) spec, cb = send(*func_args) f_args = generate_funcspec_args(spec, *args) logger.trace("executing func #{name.to_s.sub(/_func$/, "")}") cb.call(f_args) end end module Service def funcspec(args: [], named: {}, name: nil, result: nil) if name.nil? name = caller_locations[1].label.to_s if name.end_with?("!") || name.end_with?("?") name = name[0, name.length - 1] end end fargs = args.map { |a| a = make_proto(a) SDK::FuncSpec::Value.new( type: a.descriptor.name ) } + named.map { |name, a| a = make_proto(a) SDK::FuncSpec::Value.new( name: name, type: a.descriptor.name ) } result = Empty if result.nil? result = make_proto(result) SDK::FuncSpec.new( name: name.to_s.sub(/_spec$/, ""), args: fargs, result: [ SDK::FuncSpec::Value.new( type: result.descriptor.name, ) ] ) end def make_proto(v) return v if v.respond_to?(:descriptor) v = Mapper::REVERSE_MAP.detect do |k, v| v if value.class.ancestors.include?(k) end if v.nil? raise TypeError, "Type `#{v.class}' is not a proto message and a mapping cannot be found" end v end end end end end end