<commit_before>require "base64"
require "json"
require "compress/gzip"

module Raven
  # Encodes events and sends them to the Sentry server.
  class Client
    PROTOCOL_VERSION = 7
    USER_AGENT       = "raven.cr/#{Raven::VERSION}"

    property configuration : Configuration

    @state : State
    @processors : Array(Processor)

    getter transport : Transport do
      case configuration.scheme
      when "http", "https"
        Transport::HTTP.new(configuration)
      when "dummy"
        Transport::Dummy.new(configuration)
      else
        raise "Unknown transport scheme '#{configuration.scheme}'"
      end
    end

    def initialize(@configuration)
      @state = State.new
      @processors = [] of Processor

      @configuration.processors.each do |klass|
        @processors << klass.new(self)
      end
    end

    def send_feedback(event_id : String, data : Hash)
      unless configuration.valid?
        Log.debug {
          "Client#send_feedback with event id '#{event_id}' failed: #{configuration.error_messages}"
        }
        return false
      end
      transport.send_feedback(event_id, data)
    end

    def send_event(event : Event | Event::HashType, hint : Event::Hint? = nil)
      unless configuration.valid?
        Log.debug {
          "Client#send_event with event '#{event}' failed: #{configuration.error_messages}"
        }
        return false
      end
      if event.is_a?(Event)
        configuration.before_send.try do |before_send|
          event = before_send.call(event, hint)
          unless event
            Log.info { "Discarded event because before_send returned nil" }
            return
          end
        end
      end
      event = event.is_a?(Event) ? event.to_hash : event
      unless @state.should_try?
        failed_send nil, event
        return
      end
      Log.info { "Sending event #{event[:event_id]} to Sentry" }

      content_type, encoded_data = encode(event)
      begin
        options = {content_type: content_type}
        transport.send_event(generate_auth_header, encoded_data, **options).tap do
          successful_send
        end
      rescue ex
        failed_send ex, event
      end
    end

    private def encode(data)
      data = @processors.reduce(data) { |v, p| p.process(v) }

      io = IO::Memory.new
      data.to_json(io)
      io.rewind

      case configuration.encoding
      when .gzip?
        io_gzipped = IO::Memory.new
        Compress::Gzip::Writer.open(io_gzipped) do |gzip|
          IO.copy(io, gzip)
        end
        io_gzipped.rewind
        {"application/octet-stream", io_gzipped}
      when .json?
        {"application/json", io}
      else
        raise "Invalid configuration encoding"
      end
    end
<commit_msg>Use exhaustive case<commit_after>require "base64"
require "json"
require "compress/gzip"

module Raven
  # Encodes events and sends them to the Sentry server.
  class Client
    PROTOCOL_VERSION = 7
    USER_AGENT       = "raven.cr/#{Raven::VERSION}"

    property configuration : Configuration

    @state : State
    @processors : Array(Processor)

    getter transport : Transport do
      case configuration.scheme
      when "http", "https"
        Transport::HTTP.new(configuration)
      when "dummy"
        Transport::Dummy.new(configuration)
      else
        raise "Unknown transport scheme '#{configuration.scheme}'"
      end
    end

    def initialize(@configuration)
      @state = State.new
      @processors = [] of Processor

      @configuration.processors.each do |klass|
        @processors << klass.new(self)
      end
    end

    def send_feedback(event_id : String, data : Hash)
      unless configuration.valid?
        Log.debug {
          "Client#send_feedback with event id '#{event_id}' failed: #{configuration.error_messages}"
        }
        return false
      end
      transport.send_feedback(event_id, data)
    end

    def send_event(event : Event | Event::HashType, hint : Event::Hint? = nil)
      unless configuration.valid?
        Log.debug {
          "Client#send_event with event '#{event}' failed: #{configuration.error_messages}"
        }
        return false
      end
      if event.is_a?(Event)
        configuration.before_send.try do |before_send|
          event = before_send.call(event, hint)
          unless event
            Log.info { "Discarded event because before_send returned nil" }
            return
          end
        end
      end
      event = event.is_a?(Event) ? event.to_hash : event
      unless @state.should_try?
        failed_send nil, event
        return
      end
      Log.info { "Sending event #{event[:event_id]} to Sentry" }

      content_type, encoded_data = encode(event)
      begin
        options = {content_type: content_type}
        transport.send_event(generate_auth_header, encoded_data, **options).tap do
          successful_send
        end
      rescue ex
        failed_send ex, event
      end
    end

    private def encode(data)
      data = @processors.reduce(data) { |v, p| p.process(v) }

      io = IO::Memory.new
      data.to_json(io)
      io.rewind

      case configuration.encoding
      in .gzip?
        io_gzipped = IO::Memory.new
        Compress::Gzip::Writer.open(io_gzipped) do |gzip|
          IO.copy(io, gzip)
        end
        io_gzipped.rewind
        {"application/octet-stream", io_gzipped}
      in .json?
        {"application/json", io}
      end
    end

{'fraction_non_alphanumeric': 0.09284403669724771, 'fraction_numerical': 0.0011009174311926607, 'mean_word_length': 2.2140330188679247, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, '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': '766936', 'n_tokens_mistral': 1589, 'n_tokens_neox': 1559, 'n_words': 399}