Fs02/grimoire

View on GitHub
c/condition.go

Summary

Maintainability
A
55 mins
Test Coverage
A
100%
// Package c defines function for building condition in query.
package c

// ConditionType defines enumeration of all supported condition types.
type ConditionType int

const (
    // ConditionAnd is condition type for and operator.
    ConditionAnd ConditionType = iota
    // ConditionOr is condition type for or operator.
    ConditionOr
    // ConditionNot is condition type for not operator.
    ConditionNot

    // ConditionEq is condition type for equal comparison.
    ConditionEq
    // ConditionNe is condition type for not equal comparison.
    ConditionNe

    // ConditionLt is condition type for less than comparison.
    ConditionLt
    // ConditionLte is condition type for less than or equal comparison.
    ConditionLte
    // ConditionGt is condition type for greater than comparison.
    ConditionGt
    // ConditionGte is condition type for greter than or equal comparison.
    ConditionGte

    // ConditionNil is condition type for nil check.
    ConditionNil
    // ConditionNotNil is condition type for not nil check.
    ConditionNotNil

    // ConditionIn is condition type for inclusion comparison.
    ConditionIn
    // ConditionNin is condition type for not inclusion comparison.
    ConditionNin

    // ConditionLike is condition type for like comparison.
    ConditionLike
    // ConditionNotLike is condition type for not like comparison.
    ConditionNotLike

    // ConditionFragment is condition type for custom condition.
    ConditionFragment
)

// I identifies database variable such as column name or table name.
type I string

// Operand defines information about condition's operand.
type Operand struct {
    Column I
    Values []interface{}
}

// NewOperand create new operand.
func NewOperand(o ...interface{}) Operand {
    if len(o) == 1 {
        if c, ok := o[0].(I); ok {
            return Operand{Column: c}
        }
    }

    return Operand{Values: o}
}

// Condition defines details of a coundition type.
type Condition struct {
    Type  ConditionType
    Left  Operand
    Right Operand
    Inner []Condition
}

// None returns true if no condition is specified.
func (c Condition) None() bool {
    return (c.Type == ConditionAnd ||
        c.Type == ConditionOr ||
        c.Type == ConditionNot) &&
        len(c.Inner) == 0
}

// And wraps conditions using and.
func (c Condition) And(condition ...Condition) Condition {
    if c.None() && len(condition) == 1 {
        return condition[0]
    } else if c.Type == ConditionAnd {
        c.Inner = append(c.Inner, condition...)
        return c
    }

    inner := append([]Condition{c}, condition...)
    return And(inner...)
}

// Or wraps conditions using or.
func (c Condition) Or(condition ...Condition) Condition {
    if c.None() && len(condition) == 1 {
        return condition[0]
    } else if c.Type == ConditionOr || c.None() {
        c.Type = ConditionOr
        c.Inner = append(c.Inner, condition...)
        return c
    }

    inner := append([]Condition{c}, condition...)
    return Or(inner...)
}

// And compares other conditions using and.
func And(inner ...Condition) Condition {
    if len(inner) == 1 {
        return inner[0]
    }

    return Condition{
        Type:  ConditionAnd,
        Inner: inner,
    }
}

// Or compares other conditions using and.
func Or(inner ...Condition) Condition {
    if len(inner) == 1 {
        return inner[0]
    }

    return Condition{
        Type:  ConditionOr,
        Inner: inner,
    }
}

// Not wraps conditions using not.
// It'll negate the condition type if possible.
func Not(inner ...Condition) Condition {
    if len(inner) == 1 {
        c := inner[0]
        switch c.Type {
        case ConditionEq:
            c.Type = ConditionNe
            return c
        case ConditionLt:
            c.Type = ConditionGte
            return c
        case ConditionLte:
            c.Type = ConditionGt
            return c
        case ConditionGt:
            c.Type = ConditionLte
            return c
        case ConditionGte:
            c.Type = ConditionLt
            return c
        case ConditionNil:
            c.Type = ConditionNotNil
            return c
        case ConditionIn:
            c.Type = ConditionNin
            return c
        case ConditionLike:
            c.Type = ConditionNotLike
            return c
        }
    }

    return Condition{
        Type:  ConditionNot,
        Inner: inner,
    }
}

// Eq compares that left value is equal to right value.
func Eq(left, right interface{}) Condition {
    return Condition{
        Type:  ConditionEq,
        Left:  NewOperand(left),
        Right: NewOperand(right),
    }
}

// Ne compares that left value is not equal to right value.
func Ne(left, right interface{}) Condition {
    return Condition{
        Type:  ConditionNe,
        Left:  NewOperand(left),
        Right: NewOperand(right),
    }
}

// Lt compares that left value is less than to right value.
func Lt(left, right interface{}) Condition {
    return Condition{
        Type:  ConditionLt,
        Left:  NewOperand(left),
        Right: NewOperand(right),
    }
}

// Lte compares that left value is less than or equal to right value.
func Lte(left, right interface{}) Condition {
    return Condition{
        Type:  ConditionLte,
        Left:  NewOperand(left),
        Right: NewOperand(right),
    }
}

// Gt compares that left value is greater than to right value.
func Gt(left, right interface{}) Condition {
    return Condition{
        Type:  ConditionGt,
        Left:  NewOperand(left),
        Right: NewOperand(right),
    }
}

// Gte compares that left value is greater than or equal to right value.
func Gte(left, right interface{}) Condition {
    return Condition{
        Type:  ConditionGte,
        Left:  NewOperand(left),
        Right: NewOperand(right),
    }
}

// Nil check whether column is nil.
func Nil(col I) Condition {
    return Condition{
        Type: ConditionNil,
        Left: NewOperand(col),
    }
}

// NotNil check whether column is not nil.
func NotNil(col I) Condition {
    return Condition{
        Type: ConditionNotNil,
        Left: NewOperand(col),
    }
}

// In check whethers value of the column is included in values.
func In(col I, values ...interface{}) Condition {
    return Condition{
        Type:  ConditionIn,
        Left:  NewOperand(col),
        Right: NewOperand(values...),
    }
}

// Nin check whethers value of the column is not included in values.
func Nin(col I, values ...interface{}) Condition {
    return Condition{
        Type:  ConditionNin,
        Left:  NewOperand(col),
        Right: NewOperand(values...),
    }
}

// Like compares value of column to match string pattern.
func Like(col I, pattern string) Condition {
    return Condition{
        Type:  ConditionLike,
        Left:  NewOperand(col),
        Right: NewOperand(pattern),
    }
}

// NotLike compares value of column to not match string pattern.
func NotLike(col I, pattern string) Condition {
    return Condition{
        Type:  ConditionNotLike,
        Left:  NewOperand(col),
        Right: NewOperand(pattern),
    }
}

// Fragment add custom condition.
func Fragment(expr I, values ...interface{}) Condition {
    return Condition{
        Type:  ConditionFragment,
        Left:  NewOperand(expr),
        Right: NewOperand(values...),
    }
}