# frozen_string_literal: true require "shellwords" require_relative "process_sip/version" module ProcessSip require_relative "process_sip/refinements" using Refinements # ProcessSip.load_each :git, :curl def self.load_each(*names) names.each do require "lib/process_sip/adapters/#{_1}" end end def self.method_missing(name, &) Class.new(Adapter, &).new(name).tap do |adapter| define_singleton_method(name) do |&block| adapter.class.class_eval(&block) if block adapter end end end class Adapter def initialize(name) @name, @context, @preprint = name.dasherize, Context.new, false end def omit(*keys) = clone_with(context: @context.except(*keys)) def with(**options) = clone_with(context: @context.merge(**options)) def call(name, *, **, &block) chain = [@name, @context.arguments, name.to_s.dasherize, process(*, **)].flatten.map(&:shellescape).join(" ") puts chain if @preprint IO.popen(chain, &block || -> { _1.read.chomp }) end ruby2_keywords def method_missing(name, *arguments, &block) Subcommand.new(self, name).then do |apply| if block_given? || arguments.any? apply.call(*arguments, &block) else apply end end end def preprint = clone_with(preprint: true) def silent = clone_with(preprint: false) private def process(*arguments, **options) arguments.map { option_name _1 } + options.transform_keys { option_name _1 }.flatten end def option_name(name) name.is_a?(Symbol) ? "#{name.size > 1 ? "--" : "-"}#{name.dasherize}" : name end end class Subcommand < Data.define(:adapter, :name) include Enumerable def each(&) = stream(&) alias each_line each def match?(...) = call.match?(...) def readlines(chomp: true) = call { _1.readlines(chomp:) } def stream(*, **) return enum_for(:stream, *, **) unless block_given? call(*, **) do |io| while line = io.gets&.chomp; yield line; end end end def call(...) = adapter.call(name, ...) alias method_missing call alias read call end class Context def initialize(**options) @options = options @arguments = @options.map { ["--#{_1.dasherize}", _2.shellescape].join("=") } end attr_reader :arguments def except(*) = self.class.new(**@options.except(*)) def merge(**options) = self.class.new(**@options, **options) end end