import type { QueryCustomType } from './builder' ; import { Field , functions , ODataFunctions , ODataOperators , operators , Renderable , } from './syntax' ; import { syntax } from './syntax' ; export type Connector = 'and' | 'or' ; export class Expression < T > implements Renderable { private _connector : Connector ; private _negated : boolean ; private _children : Renderable [ ] ; constructor ( { children , connector , negated , } = { } ) { this . _children = children || [ ] ; this . _connector = connector || 'and' ; this . _negated = negated || false ; } get [ Symbol . toStringTag ] ( ) { return 'Expression' ; } static s < T extends object > ( ) { return Field . factory < T > ( ) ; } static e < T > ( connector = 'and' ) { return new Expression < T > ( { connector } ) ; } static o < T > ( ) { return operators ; } static f < T > ( ) { return functions ; } static filter < T extends object > ( opts ) { return opts ( { s : Expression . s < T > ( ) , e : Expression . e , o : Expression . o < T > ( ) , f : Expression . f < T > ( ) , } ) as Expression < T > ; } toJSON ( ) { return { children : this . _children . map ( ( c ) => c . toJSON ( ) ) , connector : this . _connector , negated : this . _negated , } ; } children ( ) { return this . _children ; } connector ( ) { return this . _connector ; } negated ( ) { return this . _negated ; } length ( ) { return this . _children . length ; } render ( { aliases , escape , prefix , } = { } ) { let content = this . _children . map ( ( n ) => n . render ( { aliases , escape , prefix } ) ) . join ( ` ${ this . _connector } ` ) ; if ( this . _negated ) { content = `not (${ content })` ; } return content ; } private _add ( node , connector ? ) { if ( connector !== undefined && this . _connector !== connector ) { let children = [ ] ; if ( this . _children . length > 0 ) { if ( this . _children . length === 1 ) { children = [ ... this . _children ] ; } else { let exp = new Expression < T > ( { children : this . _children , connector : this . _connector , negated : this . _negated , } ) ; if ( exp . length ( ) > 1 ) { children . push ( syntax . grouping ( exp ) ) ; } else { children . push ( exp ) ; } } } if ( node instanceof Expression && ( node . connector ( ) === connector || node . length ( ) === 1 ) ) { children = [ ... children , ... node . children ( ) ] ; } else { children . push ( syntax . grouping ( node ) ) ; } this . _connector = connector ; this . _children = children ; } else if ( node instanceof Expression && ! node . negated ( ) && ( node . connector ( ) === connector || node . length ( ) === 1 ) ) { this . _children = [ ... this . _children , ... node . children ( ) ] ; } else { this . _children . push ( node instanceof Expression && ! node . negated ( ) ? syntax . grouping ( node ) : node ) ; } return this ; } or ( exp ) { return this . _add ( exp , 'or' ) ; } and ( exp ) { return this . _add ( exp , 'and' ) ; } not ( exp ) { const notExp = new Expression < T > ( { children : exp . children ( ) , connector : exp . connector ( ) , negated : true , } ) ; return this . _add ( notExp , this . _connector ) ; } eq ( left , right , normalize ? ) { return this . _add ( operators . eq ( left , right , normalize ) ) ; } ne ( left , right , normalize ? ) { return this . _add ( operators . ne ( left , right , normalize ) ) ; } gt ( left , right , normalize ? ) { return this . _add ( operators . gt ( left , right , normalize ) ) ; } ge ( left , right , normalize ? ) { return this . _add ( operators . ge ( left , right , normalize ) ) ; } lt ( left , right , normalize ? ) { return this . _add ( operators . lt ( left , right , normalize ) ) ; } le ( left , right , normalize ? ) { return this . _add ( operators . le ( left , right , normalize ) ) ; } has ( left , right , normalize ? ) { return this . _add ( operators . has ( left , right , normalize ) ) ; } in ( left , right , normalize ? ) { return this . _add ( operators . in ( left , right , normalize ) ) ; } contains ( left , right , normalize ? ) { return this . _add ( functions . contains ( left , right , normalize ) ) ; } startsWith ( left , right , normalize ? ) { return this . _add ( functions . startsWith ( left , right , normalize ) ) ; } endsWith ( left , right , normalize ? ) { return this . _add ( functions . endsWith ( left , right , normalize ) ) ; } any < N extends object > ( left , opts , alias ? ) { const exp = opts ( { s : Field . factory < N > ( ) , e : Expression . e , } ) as Expression < N > ; return this . _add ( syntax . any ( left , exp , alias ) ) ; } all < N extends object > ( left , opts , alias ? ) { const exp = opts ( { s : Field . factory < N > ( ) , e : Expression . e , } ) as Expression < N > ; return this . _add ( syntax . all ( left , exp , alias ) ) ; } isof ( type ) ; isof ( left , type ) ; isof ( left , type ? ) { return this . _add ( syntax . isof ( left , type ) ) ; } }