import type { QueryCustomType } from './builder' ; import { normalizeValue } from './builder' ; export interface Renderable { render ( { aliases , escape , prefix , } ) : string ; toString ( ) : string ; toJSON ( ) : any ; } export type Funcs < T > = ( x : ODataSyntax < T > ) => Function < T > | Operator < T > | Grouping < T > ; export class Field < T extends object > implements ProxyHandler < T > { constructor ( public name = '' ) { } static factory < T extends object > ( ) { const h = new Field < T > ( ) ; return new Proxy ( { _name : '' } as T , h ) ; } get ( target , p ) { let name = ( target as any ) [ '_name' ] ; if ( p === 'render' ) { return ( { prefix } ) => prefix ? `${ prefix }/${ name }` : name ; } name = name ? `${ name }/${ p as string }` : p ; return new Proxy ( { _name : name } as any , this ) ; } } function applyMixins ( derivedCtor , constructors ) { constructors . forEach ( ( baseCtor ) => { Object . getOwnPropertyNames ( baseCtor . prototype ) . forEach ( ( name ) => { Object . defineProperty ( derivedCtor . prototype , name , Object . getOwnPropertyDescriptor ( baseCtor . prototype , name ) || Object . create ( null ) ) ; } ) ; } ) ; } function render ( value , { aliases , normalize , escape , prefix , } = { } ) { if ( typeof value === 'function' ) { return render ( value ( syntax ) , { aliases , normalize , prefix } ) ; } if ( typeof value === 'object' && value !== null && value . render !== undefined ) { return render ( value . render ( { aliases , escape , prefix } ) , { aliases , normalize , escape , prefix , } ) ; } return normalize ? normalizeValue ( value , { aliases , escape } ) : value ; } export class Function < T > implements Renderable { constructor ( protected name , protected values , protected normalize = true , protected escape = false ) { } get [ Symbol . toStringTag ] ( ) { return 'Function' ; } toJSON ( ) { return { name : this . name , values : this . values , normalize : this . normalize , } ; } render ( { aliases , escape , prefix , } ) { let [ field , ... values ] = this . values ; field = render ( field , { aliases , escape , prefix } ) ; let params = [ field , ... values . map ( ( v ) => render ( v , { aliases , escape , prefix , normalize : this . normalize } ) ) , ] ; return `${ this . name }(${ params . join ( ', ' ) })` ; } } export class StringAndCollectionFunctions < T > { concat ( field , value , normalize ? ) { return new Function < T > ( 'concat' , [ field , value ] , normalize ) ; } contains ( field , value , normalize ? ) { return new Function < T > ( 'contains' , [ field , value ] , normalize ) ; } endsWith ( field , value , normalize ? ) { return new Function < T > ( 'endswith' , [ field , value ] , normalize ) ; } indexOf ( field , value , normalize ? ) { return new Function < T > ( 'indexof' , [ field , value ] , normalize ) ; } length ( value , normalize ? ) { return new Function < T > ( 'length' , [ value ] , normalize ) ; } startsWith ( field , value , normalize ? ) { return new Function < T > ( 'startswith' , [ field , value ] , normalize ) ; } subString ( field , start , length ? ) { let values = [ field , start ] ; if ( length !== undefined ) { values . push ( length ) ; } return new Function < T > ( 'substring' , values ) ; } } export class CollectionFunctions < T > { hasSubset ( s1 , s2 ) { return new Function < T > ( 'hassubset' , [ s1 , s2 ] ) ; } hasSubsequence ( s1 , s2 ) { return new Function < T > ( 'hassubsequence' , [ s1 , s2 ] ) ; } } export class StringFunctions < T > { matchesPattern ( value , pattern ) { return new Function < T > ( 'matchesPattern' , [ value , pattern ] ) ; } toLower ( value ) { return new Function < T > ( 'tolower' , [ value ] ) ; } toUpper ( value ) { return new Function < T > ( 'toupper' , [ value ] ) ; } trim ( value ) { return new Function < T > ( 'trim' , [ value ] ) ; } } export class DateAndTimeFunctions < T > { date ( value ) { return new Function < T > ( 'date' , [ value ] ) ; } day ( value ) { return new Function < T > ( 'day' , [ value ] ) ; } fractionalseconds ( value ) { return new Function < T > ( 'fractionalseconds' , [ value ] ) ; } hour ( value ) { return new Function < T > ( 'hour' , [ value ] ) ; } maxdatetime ( value ) { return new Function < T > ( 'maxdatetime' , [ value ] ) ; } mindatetime ( value ) { return new Function < T > ( 'mindatetime' , [ value ] ) ; } minute ( value ) { return new Function < T > ( 'minute' , [ value ] ) ; } month ( value ) { return new Function < T > ( 'month' , [ value ] ) ; } now ( ) { return new Function < T > ( 'now' , [ ] ) ; } second ( value ) { return new Function < T > ( 'second' , [ value ] ) ; } time ( value ) { return new Function < T > ( 'time' , [ value ] ) ; } totaloffsetminutes ( value ) { return new Function < T > ( 'totaloffsetminutes' , [ value ] ) ; } totalseconds ( value ) { return new Function < T > ( 'totalseconds' , [ value ] ) ; } year ( value ) { return new Function < T > ( 'year' , [ value ] ) ; } } export class ArithmeticFunctions < T > { ceiling ( value ) { return new Function < T > ( 'ceiling' , [ value ] ) ; } floor ( value ) { return new Function < T > ( 'floor' , [ value ] ) ; } round ( value ) { return new Function < T > ( 'round' , [ value ] ) ; } } export class TypeFunctions < T > { cast ( value , type ? ) { return new Function < T > ( 'cast' , [ value , type ] ) ; } isof ( value , type ? ) { return new Function < T > ( 'isof' , [ value , type ] ) ; } } export class GeoFunctions < T > { geoDistance ( value , point , normalize ? ) { return new Function < T > ( 'geo.distance' , [ value , point ] , normalize ) ; } geoIntersects ( value , polygon , normalize ? ) { return new Function < T > ( 'geo.intersects' , [ value , polygon ] , normalize ) ; } geoLength ( line , normalize ? ) { return new Function < T > ( 'geo.length' , [ line ] , normalize ) ; } } export class ConditionalFunctions < T > { case ( condition , value ) { return new Function < T > ( 'case' , [ condition , value ] ) ; } } export class Operator < T > implements Renderable { constructor ( protected op , protected values , protected normalize = true ) { } get [ Symbol . toStringTag ] ( ) { return 'Operator' ; } toJSON ( ) { return { op : this . op , values : this . values , normalize : this . normalize , } ; } render ( { aliases , escape , prefix , } ) { let [ left , right ] = this . values ; left = render ( left , { aliases , escape , prefix } ) ; if ( right !== undefined ) { right = Array . isArray ( right ) ? `(${ right . map ( ( v ) => render ( v , { aliases , escape , prefix , normalize : this . normalize , } ) ) . join ( ',' ) })` : render ( right , { aliases , escape , prefix , normalize : this . normalize , } ) ; return `${ left } ${ this . op } ${ right }` ; } return `${ this . op }(${ left })` ; } } export class LogicalOperators < T > { eq ( left , right , normalize ? ) { return new Operator ( 'eq' , [ left , right ] , normalize ) ; } ne ( left , right , normalize ? ) { return new Operator ( 'ne' , [ left , right ] , normalize ) ; } gt ( left , right , normalize ? ) { return new Operator ( 'gt' , [ left , right ] , normalize ) ; } ge ( left , right , normalize ? ) { return new Operator ( 'ge' , [ left , right ] , normalize ) ; } lt ( left , right , normalize ? ) { return new Operator ( 'lt' , [ left , right ] , normalize ) ; } le ( left , right , normalize ? ) { return new Operator ( 'le' , [ left , right ] , normalize ) ; } not ( value , normalize ? ) { return new Operator < T > ( 'not' , [ value ] , normalize ) ; } has ( left , right , normalize ? ) { return new Operator ( 'has' , [ left , right ] , normalize ) ; } in ( left , right , normalize ? ) { return new Operator ( 'in' , [ left , right ] , normalize ) ; } } export class ArithmeticOperators < T > { add ( left , right , normalize ? ) { return new Operator < T > ( 'add' , [ left , right ] , normalize ) ; } sub ( left , right , normalize ? ) { return new Operator ( 'sub' , [ left , right ] , normalize ) ; } mul ( left , right , normalize ? ) { return new Operator ( 'mul' , [ left , right ] , normalize ) ; } div ( left , right , normalize ? ) { return new Operator ( 'div' , [ left , right ] , normalize ) ; } mod ( left , right , normalize ? ) { return new Operator ( 'mod' , [ left , right ] , normalize ) ; } neg ( value , normalize ? ) { return new Operator ( '-' , [ value ] , normalize ) ; } } export class Grouping < T > implements Renderable { constructor ( protected group ) { } get [ Symbol . toStringTag ] ( ) { return 'Grouping' ; } toJSON ( ) { return { group : this . group . toJSON ( ) , } ; } render ( { aliases , escape , prefix , } ) { return `(${ render ( this . group , { aliases , escape , prefix } ) })` ; } } export class GroupingOperators < T > { grouping ( value ) { return new Grouping ( value ) ; } } export class Lambda < T > implements Renderable { constructor ( protected op , protected values , protected alias ? ) { } get [ Symbol . toStringTag ] ( ) { return 'Lambda' ; } toJSON ( ) { return { op : this . op , values : this . values , alias : this . alias , } ; } render ( { aliases , escape , prefix , } ) { let [ left , right ] = this . values ; left = render ( left , { aliases , escape , prefix } ) ; let alias = this . alias || left . split ( '/' ) . pop ( ) . toLowerCase ( ) [ 0 ] ; return `${ left }/${ this . op }(${ alias }:${ render ( right , { aliases , escape , prefix : alias , } ) })` ; } } export class LambdaOperators < T > { any ( field , value , alias ? ) { return new Lambda ( 'any' , [ field , value ] , alias ) ; } all ( field , value , alias ? ) { return new Lambda ( 'all' , [ field , value ] , alias ) ; } } export class ODataOperators < T > { } export interface ODataOperators < T > extends LogicalOperators < T > , ArithmeticOperators < T > , GroupingOperators < T > , LambdaOperators < T > { } applyMixins ( ODataOperators , [ LogicalOperators , ArithmeticOperators , GroupingOperators , LambdaOperators , ] ) ; export const operators = new ODataOperators < any > ( ) ; export class ODataFunctions < T > { } export interface ODataFunctions < T > extends StringAndCollectionFunctions < T > , CollectionFunctions < T > , StringFunctions < T > , DateAndTimeFunctions < T > , ArithmeticFunctions < T > , TypeFunctions < T > , GeoFunctions < T > , ConditionalFunctions < T > { } applyMixins ( ODataFunctions , [ StringAndCollectionFunctions , CollectionFunctions , StringFunctions , DateAndTimeFunctions , ArithmeticFunctions , TypeFunctions , GeoFunctions , ConditionalFunctions , ] ) ; export const functions = new ODataFunctions < any > ( ) ; export class ODataSyntax < T > { } export interface ODataSyntax < T > extends ODataOperators < T > , ODataFunctions < T > { } applyMixins ( ODataSyntax , [ ODataOperators , ODataFunctions ] ) ; export const syntax = new ODataSyntax < any > ( ) ;