<commit_before>require "optparse"
require "ostruct"
require "pathname"

require "bashcov/bash_info"
require "bashcov/runner"
require "bashcov/version"

# Bashcov default module
# @note Keep it short!
module Bashcov
  extend Bashcov::BashInfo

  class << self
    # @return [OpenStruct] The +OpenStruct+ object representing Bashcov's
    # execution environment
    def options
      set_default_options! unless defined?(@options)
      @options
    end

    # Parses the given CLI arguments and sets +options+.
    # @param [Array] args list of arguments
    # @raise [SystemExit] if invalid arguments are given
    # @return [void]
    def parse_options!(args)
      begin
        option_parser.parse!(args)
      rescue OptionParser::ParseError, Errno::ENOENT => e
        abort "#{option_parser.program_name}: #{e.message}"
      end

      if args.empty?
        abort("You must give exactly one command to execute.")
      else
        options.command = args.unshift(bash_path)
      end
    end

    # @return [String] Program name
    def program_name
      "bashcov"
    end

    # @return [String] Program name including version for easy consistent output
    # @note +fullname+ instead of name to avoid clashing with +Module.name+
    def fullname
      "#{program_name} v#{VERSION}"
    end

    # Wipe the current options and reset default values
    def set_default_options!
      @options = OpenStruct.new

      @options.root_directory   = Dir.getwd
      @options.skip_uncovered   = false
      @options.bash_path        = "/bin/bash"
      @options.mute             = false
    end

    # Passes off +respond_to?+ to {options} for missing methods
    def respond_to_missing?(*args)
      options.respond_to?(*args)
    end

    # Dispatches missing methods to {options}
    def method_missing(method_name, *args, &block)
      options.send(method_name, *args, &block)
    end

  private

    def help(program_name)
      <<-HELP.gsub!(/^ +/, "").gsub!("\t", " " * 4)
        Usage: #{program_name} [options] [--] <command> [options]
        Examples:
        \t#{program_name} ./script.sh
        \t#{program_name} --skip-uncovered ./script.sh
        \t#{program_name} -- ./script.sh --some --flags
        \t#{program_name} --skip-uncovered -- ./script.sh --some --flags
      HELP
    end

    def option_parser
      OptionParser.new do |opts|
        opts.program_name = program_name
        opts.version = Bashcov::VERSION
        opts.banner = help opts.program_name

        opts.separator "\nSpecific options:"

        opts.on("-s", "--skip-uncovered", "Do not report uncovered files") do |s|
          options.skip_uncovered = s
        end
        opts.on("-m", "--mute", "Do not print script output") do |m|
          options.mute = m
        end
        opts.on("--bash-path PATH", "Path to Bash executable") do |p|<commit_msg>Make respond_to_missing? and method_missing private methods<commit_after>require "optparse"
require "ostruct"
require "pathname"

require "bashcov/bash_info"
require "bashcov/runner"
require "bashcov/version"

# Bashcov default module
# @note Keep it short!
module Bashcov
  extend Bashcov::BashInfo

  class << self
    # @return [OpenStruct] The +OpenStruct+ object representing Bashcov's
    # execution environment
    def options
      set_default_options! unless defined?(@options)
      @options
    end

    # Parses the given CLI arguments and sets +options+.
    # @param [Array] args list of arguments
    # @raise [SystemExit] if invalid arguments are given
    # @return [void]
    def parse_options!(args)
      begin
        option_parser.parse!(args)
      rescue OptionParser::ParseError, Errno::ENOENT => e
        abort "#{option_parser.program_name}: #{e.message}"
      end

      if args.empty?
        abort("You must give exactly one command to execute.")
      else
        options.command = args.unshift(bash_path)
      end
    end

    # @return [String] Program name
    def program_name
      "bashcov"
    end

    # @return [String] Program name including version for easy consistent output
    # @note +fullname+ instead of name to avoid clashing with +Module.name+
    def fullname
      "#{program_name} v#{VERSION}"
    end

    # Wipe the current options and reset default values
    def set_default_options!
      @options = OpenStruct.new

      @options.root_directory   = Dir.getwd
      @options.skip_uncovered   = false
      @options.bash_path        = "/bin/bash"
      @options.mute             = false
    end

  private

    # Passes off +respond_to?+ to {options} for missing methods
    def respond_to_missing?(*args)
      options.respond_to?(*args)
    end

    # Dispatches missing methods to {options}
    def method_missing(method_name, *args, &block)
      options.send(method_name, *args, &block)
    end

    def help(program_name)
      <<-HELP.gsub!(/^ +/, "").gsub!("\t", " " * 4)
        Usage: #{program_name} [options] [--] <command> [options]
        Examples:
        \t#{program_name} ./script.sh
        \t#{program_name} --skip-uncovered ./script.sh
        \t#{program_name} -- ./script.sh --some --flags
        \t#{program_name} --skip-uncovered -- ./script.sh --some --flags
      HELP
    end

    def option_parser
      OptionParser.new do |opts|
        opts.program_name = program_name
        opts.version = Bashcov::VERSION
        opts.banner = help opts.program_name

        opts.separator "\nSpecific options:"

        opts.on("-s", "--skip-uncovered", "Do not report uncovered files") do |s|
          options.skip_uncovered = s
        end
        opts.on("-m", "--mute", "Do not print script output") do |m|
          options.mute = m
        end
        opts.on("--bash-path PATH", "Path to Bash executable") do |p|
{'fraction_non_alphanumeric': 0.11332870531065602, 'fraction_numerical': 0.0003471017007983339, 'mean_word_length': 2.5140243902439026, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1249394', 'n_tokens_mistral': 1777, 'n_tokens_neox': 1717, 'n_words': 494}