huyderman/querylicious

View on GitHub
lib/querylicious/transform.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

require 'parslet'
require 'date'
require 'querylicious/types'
require 'querylicious/key_value_pair'

module Querylicious
  # Transformer for turning a parsed query string into a list of KeyValuePair
  class Transform < Parslet::Transform
    rule('') { nil }
    rule(string: simple(:it)) { Types::Coercible::String[it].gsub('\"', '"') }
    rule(integer: simple(:it)) { Types::Params::Integer[it] }
    rule(date: simple(:it)) { Types::Params::Date[it] }
    rule(datetime: simple(:it)) { Types::Params::DateTime[it] }

    rule(symbol: simple(:it)) do
      symbol = Types::Coercible::Symbol[it.to_s.downcase]

      # Symbol normalization
      case symbol
      when :not then :not_eql
      when :- then :not_eql
      when :> then :gt
      when :>= then :gteq
      when :< then :lt
      when :<= then :lteq
      else symbol
      end
    end

    rule(list: sequence(:it)) { Types::Params::Array[it] }

    rule(range: { start: simple(:first), end: simple(:last) }) do
      if first == :* && last == :*
        :*
      elsif first == :*
        { object: last,  op: :lteq }
      elsif last == :*
        { object: first, op: :gteq }
      else
        Range.new(first, last)
      end
    end

    rule(phrase: simple(:text)) do
      KeyValuePair.new(
        key: 'phrase',
        value: Types::Coercible::String[text]
      )
    end
    rule(phrase: { object: simple(:text), op: simple(:op) }) do
      KeyValuePair.new(
        key: 'phrase',
        value: Types::Coercible::String[text],
        op: op
      )
    end

    rule(pair: { key: simple(:key), value: simple(:value) }) do
      KeyValuePair.new(key: key, value: value)
    end
    rule(pair: { key: simple(:key), value: simple(:value), op: simple(:op) }) do
      KeyValuePair.new(key: key, value: value, op: op)
    end

    rule(pair: { key: simple(:key), value: sequence(:value) }) do
      KeyValuePair.new(key: key, value: value)
    end
    rule(
      pair: { key: simple(:key), value: sequence(:value), op: simple(:op) }
    ) { KeyValuePair.new(key: key, value: value, op: op) }

    rule(op: simple(:op), string: simple(:it)) do
      { object: Types::Coercible::String[it], op: op }
    end
    rule(op: simple(:op), integer: simple(:it)) do
      { object: Types::Params::Integer[it], op: op }
    end
    rule(op: simple(:op), date: simple(:it)) do
      { object: Types::Params::Date[it], op: op }
    end
    rule(op: simple(:op), datetime: simple(:it)) do
      { object: Types::Params::DateTime[it], op: op }
    end
    rule(pair: {
           key: simple(:key),
           value: { op: simple(:op), object: simple(:it) }
         }) do
      KeyValuePair.new(key: key, value: it, op: op)
    end

    NEGATIONS = {
      not_eql: :eql,
      eql:     :not_eql,
      gt:      :lteq,
      gteq:    :lt,
      lt:      :gteq,
      lteq:    :gt
    }.freeze

    rule pair: {
      key: simple(:key),
      op: :not_eql,
      value: { object: simple(:obj), op: simple(:op) }
    } do
      KeyValuePair.new(key: key, value: obj, op: NEGATIONS[op])
    end
  end
end