import { Direction , ExistentialCondition , Join , PropertyCondition , Quantifier , Step } from './steps' ; interface Proxy { has ( name ) : Proxy ; } export class Specification < T > { public existential = false ; public specification = true ; constructor ( public template , public conditions ) { } suchThat < U > ( condition ) { return new Specification < T > ( this . template , this . conditions . concat ( parseTemplate ( condition ) ) ) ; } } export class Condition < T > { public existential = true ; constructor ( public template , public conditions , public negative ) { } suchThat < U > ( condition ) { return new Condition < T > ( this . template , this . conditions . concat ( parseTemplate ( condition ) ) , this . negative ) ; } } export class Preposition < T , U > { constructor ( public steps ) { } param specification returns then < V > ( specification ) { return new Preposition < T , V > ( this . steps . concat ( parseTemplate ( specification ) ) ) ; } static for < T , U > ( specification ) { return new Preposition < T , U > ( parseTemplate ( specification ) ) ; } } class ParserProxy implements Proxy { constructor ( private __parent , private __role ) { } [ key ] : any ; has ( name ) { const proxy = new ParserProxy ( this , name ) ; this [ name ] = proxy ; return proxy ; } public createQuery ( ) { const currentSteps = [ ] ; for ( const name in this ) { const value = this [ name ] ; if ( name [ 0 ] != "_" && typeof this [ name ] !== "function" && ! ( value instanceof ParserProxy ) ) { currentSteps . push ( new PropertyCondition ( name , value ) ) ; } } if ( this . __parent ) { const steps = this . __parent . createQuery ( ) ; const step = new Join ( Direction . Predecessor , this . __role ) ; steps . push ( step ) ; return steps . concat ( currentSteps ) ; } else { return currentSteps ; } } } function findTarget ( spec ) { if ( spec instanceof ParserProxy ) { return spec . createQuery ( ) ; } if ( Array . isArray ( spec ) && spec . length === 1 ) { return findTarget ( spec [ 0 ] ) ; } if ( spec instanceof Object ) { const steps = [ ] ; let targetQuery = null ; for ( const field in spec ) { if ( ! targetQuery ) { targetQuery = findTarget ( spec [ field ] ) ; if ( targetQuery ) { const join = new Join ( Direction . Successor , field ) ; targetQuery . push ( join ) ; } } if ( typeof spec [ field ] === "string" || typeof spec [ field ] === "number" || typeof spec [ field ] === "boolean" ) { const step = new PropertyCondition ( field , spec [ field ] ) ; steps . push ( step ) ; } } if ( targetQuery ) { targetQuery = targetQuery . concat ( steps ) ; } return targetQuery ; } return null ; } function parseTemplate ( template ) { const target = new ParserProxy ( null , null ) ; const spec = template ( target ) ; const targetJoins = findTarget ( spec . template ) ; const steps = targetJoins . concat ( spec . conditions ) ; if ( spec . existential ) { return [ new ExistentialCondition ( spec . negative ? Quantifier . NotExists : Quantifier . Exists , steps ) ] ; } return steps ; } export class FactDescription < T > { constructor ( private fact ) { } has < K extends keyof T > ( field ) { ( < any > this . fact ) . has ( field ) ; return new FactDescription < T [ K ] > ( this . fact [ field ] ) ; } } export function ensure < T > ( fact ) { return new FactDescription < T > ( fact ) ; }