<commit_before>require 'image_optim/bin_resolver/error'
require 'image_optim/bin_resolver/simple_version'
require 'image_optim/bin_resolver/comparable_condition'
require 'image_optim/cmd'
require 'shellwords'

class ImageOptim
  class BinResolver
    # Holds bin name and path, gets version
    class Bin
      class UnknownVersion < Error; end
      class BadVersion < Error; end

      attr_reader :name, :path, :version
      def initialize(name, path)
        @name = name.to_sym
        @path = path.to_s
        @version = detect_version
      end

      def to_s
        "#{name} #{version || '?'} at #{path}"
      end

      # Fail if version will not work properly
      def check_fail!
        fail UnknownVersion, "didn't get version of #{self}" unless version

        is = ComparableCondition.is
        case name
        when :pngcrush
          case version
          when c = is.between?('1.7.60', '1.7.65')
            fail BadVersion, "#{self} (#{c}) is known to produce broken pngs"
          end
        when :pngquant
          case version
          when c = is < '2.0'
            fail BadVersion, "#{self} (#{c}) is not supported"
          end
        end
      end

      # Run check_fail!, otherwise warn if version is known to misbehave
      def check!
        check_fail!

        is = ComparableCondition.is
        case name
        when :advpng
          case version
          when c = is < '1.17'
            warn "WARN: #{self} (#{c}) does not use zopfli"
          end
        when :pngquant
          case version
          when c = is < '2.1'
            warn "WARN: #{self} (#{c}) may be lossy even with quality `100-`"
          end
        when :gifsicle
          case version
          when c = is < '1.85'
            warn "WARN: #{self} (#{c}) doesn't support removing extension "\
                'blocks'
          end
        end
      end

    private

      # Wrap version_string with SimpleVersion
      def detect_version
        str = version_string
        str && SimpleVersion.new(str)
      end

      # Getting version of bin, will fail for an unknown name
      def version_string
        case name
        when :advpng, :gifsicle, :jpegoptim, :optipng, :pngquant
          capture("#{escaped_path} --version 2> /dev/null")[/\d+(\.\d+){1,}/]
        when :svgo
          capture("#{escaped_path} --version 2>&1")[/\d+(\.\d+){1,}/]
        when :jhead
          capture("#{escaped_path} -V 2> /dev/null")[/\d+(\.\d+){1,}/]
        when :jpegtran
          capture("#{escaped_path} -v - 2>&1")[/version (\d+\S*)/, 1]
        when :pngcrush
          capture("#{escaped_path} -version 2>&1")[/\d+(\.\d+){1,}/]
        when :pngout
          date_regexp = /[A-Z][a-z]{2} (?: |\d)\d \d{4}/
          date_str = capture("#{escaped_path} 2>&1")[date_regexp]<commit_msg>rewrite bin checks without nested case statements<commit_after>require 'image_optim/bin_resolver/error'
require 'image_optim/bin_resolver/simple_version'
require 'image_optim/bin_resolver/comparable_condition'
require 'image_optim/cmd'
require 'shellwords'

class ImageOptim
  class BinResolver
    # Holds bin name and path, gets version
    class Bin
      class UnknownVersion < Error; end
      class BadVersion < Error; end

      attr_reader :name, :path, :version
      def initialize(name, path)
        @name = name.to_sym
        @path = path.to_s
        @version = detect_version
      end

      def to_s
        "#{name} #{version || '?'} at #{path}"
      end

      is = ComparableCondition.is

      FAIL_CHECKS = [
        [:pngcrush, is.between?('1.7.60', '1.7.65'), 'is known to produce '\
              'broken pngs'],
        [:pngquant, is < '2.0', 'is not supported'],
      ]

      WARN_CHECKS = [
        [:advpng, is < '1.17', 'does not use zopfli'],
        [:pngquant, is < '2.1', 'may be lossy even with quality `100-`'],
        [:gifsicle, is < '1.85', 'does not support removing extension blocks'],
      ]

      # Fail if version will not work properly
      def check_fail!
        fail UnknownVersion, "didn't get version of #{self}" unless version

        FAIL_CHECKS.each do |bin_name, matcher, message|
          next unless bin_name == name
          next unless matcher.match(version)
          fail BadVersion, "#{self} (#{matcher}) #{message}"
        end
      end

      # Run check_fail!, otherwise warn if version is known to misbehave
      def check!
        check_fail!

        WARN_CHECKS.each do |bin_name, matcher, message|
          next unless bin_name == name
          next unless matcher.match(version)
          warn "WARN: #{self} (#{matcher}) #{message}"
        end
      end

    private

      # Wrap version_string with SimpleVersion
      def detect_version
        str = version_string
        str && SimpleVersion.new(str)
      end

      # Getting version of bin, will fail for an unknown name
      def version_string
        case name
        when :advpng, :gifsicle, :jpegoptim, :optipng, :pngquant
          capture("#{escaped_path} --version 2> /dev/null")[/\d+(\.\d+){1,}/]
        when :svgo
          capture("#{escaped_path} --version 2>&1")[/\d+(\.\d+){1,}/]
        when :jhead
          capture("#{escaped_path} -V 2> /dev/null")[/\d+(\.\d+){1,}/]
        when :jpegtran
          capture("#{escaped_path} -v - 2>&1")[/version (\d+\S*)/, 1]
        when :pngcrush
          capture("#{escaped_path} -version 2>&1")[/\d+(\.\d+){1,}/]
        when :pngout
          date_regexp = /[A-Z][a-z]{2} (?: |\d)\d \d{4}/
          date_str = capture("#{escaped_path} 2>&1")[date_regexp]
{'fraction_non_alphanumeric': 0.1332378223495702, 'fraction_numerical': 0.013610315186246419, 'mean_word_length': 2.0452562704471102, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7040495', 'n_tokens_mistral': 1822, 'n_tokens_neox': 1736, 'n_words': 492}