import { EscapedValue, HavingPreOperator, HavingStatement, SQLHavingOperator, escape, isHavingClause, isHavingGroup } from '../types.ts' import {HavingBuilderFunction} from './Select.ts' export class HavingBuilder { protected _havings: HavingStatement[] = [] get having_items() { return this._havings } havings_to_sql(havings?: HavingStatement[], level = 0): string { const indent = Array(level * 2).fill(' ').join('') let statements = [] for ( const having of havings || this._havings ) { if ( isHavingClause(having) ) { statements.push(`${indent}${statements.length < 1 ? '' : having.preop + ' '}${having.field} ${having.operator} ${having.operand}`) } else if ( isHavingGroup(having) ) { statements.push(`${indent}${statements.length < 1 ? '' : having.preop + ' '}(\n${this.havings_to_sql(having.items, level + 1)}\n${indent})`) } } return statements.filter(Boolean).join('\n') } private _createHaving(preop: HavingPreOperator, field: string | HavingBuilderFunction, operator?: SQLHavingOperator, operand?: any) { if ( typeof field === 'function' ) { const having_builder = new HavingBuilder() field(having_builder) this._havings.push({ preop, items: having_builder.having_items, }) } else if ( field && operator && typeof operand !== 'undefined' ) { this._havings.push({ field, operator, operand: escape(operand), preop }) } } having(field: string | HavingBuilderFunction, operator?: SQLHavingOperator, operand?: any) { this._createHaving('AND', field, operator, operand) return this } havingIn(field: string, values: EscapedValue) { this._havings.push({ field, operator: 'IN', operand: escape(values), preop: 'AND', }) return this } havingNot(field: string | HavingBuilderFunction, operator?: SQLHavingOperator, operand?: EscapedValue) { this._createHaving('AND NOT', field, operator, operand) return this } havingNotIn(field: string, values: EscapedValue) { this._havings.push({ field, operator: 'NOT IN', operand: escape(values), preop: 'AND' }) return this } orHaving(field: string | HavingBuilderFunction, operator?: SQLHavingOperator, operand?: EscapedValue) { this._createHaving('OR', field, operator, operand) return this } orHavingNot(field: string | HavingBuilderFunction, operator?: SQLHavingOperator, operand?: EscapedValue) { this._createHaving('OR NOT', field, operator, operand) return this } orHavingIn(field: string, values: EscapedValue) { this._havings.push({ field, operator: 'IN', operand: escape(values), preop: 'OR', }) return this } orHavingNotIn(field: string, values: EscapedValue) { this._havings.push({ field, operator: 'NOT IN', operand: escape(values), preop: 'OR', }) return this } }