import * as messages from '@cucumber/messages' ; import { ArrayMultimap } from '@teppeis/multimaps' ; export default class Query { private readonly sources : messages . Source [ ] = [ ] ; private readonly sourceByUri = new Map < string , messages . Source > ( ) ; private readonly gherkinDocuments : messages . GherkinDocument [ ] = [ ] ; private readonly pickles : messages . Pickle [ ] = [ ] ; private readonly locationByAstNodeId = new Map < string , messages . Location > ( ) ; private readonly gherkinStepByAstNodeId = new Map < string , messages . Step > ( ) ; private readonly pickleIdsMapByUri = new Map < string , ArrayMultimap < string , string > > ( ) ; private readonly pickleIdsByAstNodeId = new Map < string , string [ ] > ( ) ; private readonly pickleStepIdsByAstNodeId = new Map < string , string [ ] > ( ) ; private readonly featureByUriLine = new Map < string , messages . Feature > ( ) ; private readonly backgroundByUriLine = new Map < string , messages . Background > ( ) ; private readonly ruleByUriLine = new Map < string , messages . Rule > ( ) ; private readonly scenarioByUriLine = new Map < string , messages . Scenario > ( ) ; private readonly examplesByUriLine = new Map < string , messages . Examples > ( ) ; private readonly stepByUriLine = new Map < string , messages . Step > ( ) ; param astNodeId public getLocation ( astNodeId ) { return this . locationByAstNodeId . get ( astNodeId ) ; } public getSources ( ) { return this . sources ; } public getGherkinDocuments ( ) { return this . gherkinDocuments ; } public getPickles ( ) { return this . pickles ; } public getSource ( uri ) { return this . sourceByUri . get ( uri ) ; } public getFeature ( uri , line ) { return getAstNode ( this . featureByUriLine , uri , line ) ; } public getBackground ( uri , line ) { return getAstNode ( this . backgroundByUriLine , uri , line ) ; } public getRule ( uri , line ) { return getAstNode ( this . ruleByUriLine , uri , line ) ; } public getScenario ( uri , line ) { return getAstNode ( this . scenarioByUriLine , uri , line ) ; } public getExamples ( uri , line ) { return getAstNode ( this . examplesByUriLine , uri , line ) ; } public getStep ( uri , line ) { return getAstNode ( this . stepByUriLine , uri , line ) ; } param uri param astNodeId public getPickleIds ( uri , astNodeId ? ) { const pickleIdsByAstNodeId = this . pickleIdsMapByUri . get ( uri ) ; if ( ! pickleIdsByAstNodeId ) { throw new Error ( `No pickleIds for uri=${ uri }` ) ; } return astNodeId === undefined ? Array . from ( new Set ( pickleIdsByAstNodeId . values ( ) ) ) : pickleIdsByAstNodeId . get ( astNodeId ) ; } public getPickleStepIds ( astNodeId ) { return this . pickleStepIdsByAstNodeId . get ( astNodeId ) || [ ] ; } public update ( message ) { if ( message . source ) { this . sources . push ( message . source ) ; this . sourceByUri . set ( message . source . uri , message . source ) ; } if ( message . gherkinDocument ) { this . gherkinDocuments . push ( message . gherkinDocument ) ; if ( message . gherkinDocument . feature ) { this . updateGherkinFeature ( message . gherkinDocument . uri , message . gherkinDocument . feature ) ; } } if ( message . pickle ) { const pickle = message . pickle ; this . updatePickle ( pickle ) ; } return this ; } private updateGherkinFeature ( uri , feature ) { setAstNode ( this . featureByUriLine , uri , feature ) ; this . pickleIdsMapByUri . set ( uri , new ArrayMultimap < string , string > ( ) ) ; for ( const featureChild of feature . children ) { if ( featureChild . background ) { this . updateGherkinBackground ( uri , featureChild . background ) ; } if ( featureChild . scenario ) { this . updateGherkinScenario ( uri , featureChild . scenario ) ; } if ( featureChild . rule ) { this . updateGherkinRule ( uri , featureChild . rule ) ; } } } private updateGherkinBackground ( uri , background ) { setAstNode ( this . backgroundByUriLine , uri , background ) ; for ( const step of background . steps ) { this . updateGherkinStep ( uri , step ) ; } } private updateGherkinRule ( uri , rule ) { setAstNode ( this . ruleByUriLine , uri , rule ) ; for ( const ruleChild of rule . children ) { if ( ruleChild . background ) { this . updateGherkinBackground ( uri , ruleChild . background ) ; } if ( ruleChild . scenario ) { this . updateGherkinScenario ( uri , ruleChild . scenario ) ; } } } private updateGherkinScenario ( uri , scenario ) { setAstNode ( this . scenarioByUriLine , uri , scenario ) ; this . locationByAstNodeId . set ( scenario . id , scenario . location ) ; for ( const step of scenario . steps ) { this . updateGherkinStep ( uri , step ) ; } for ( const examples of scenario . examples ) { this . updateGherkinExamples ( uri , examples ) ; } } private updateGherkinExamples ( uri , examples ) { setAstNode ( this . examplesByUriLine , uri , examples ) ; for ( const tableRow of examples . tableBody || [ ] ) { this . locationByAstNodeId . set ( tableRow . id , tableRow . location ) ; } } private updateGherkinStep ( uri , step ) { setAstNode ( this . stepByUriLine , uri , step ) ; this . locationByAstNodeId . set ( step . id , step . location ) ; this . gherkinStepByAstNodeId . set ( step . id , step ) ; } private updatePickle ( pickle ) { const pickleIdsByLineNumber = this . pickleIdsMapByUri . get ( pickle . uri ) ; for ( const astNodeId of pickle . astNodeIds ) { pickleIdsByLineNumber . put ( astNodeId , pickle . id ) ; } this . updatePickleSteps ( pickle ) ; this . pickles . push ( pickle ) ; for ( const astNodeId of pickle . astNodeIds ) { if ( ! this . pickleIdsByAstNodeId . has ( astNodeId ) ) { this . pickleIdsByAstNodeId . set ( astNodeId , [ ] ) ; } this . pickleIdsByAstNodeId . get ( astNodeId ) . push ( pickle . id ) ; } } private updatePickleSteps ( pickle ) { const pickleSteps = pickle . steps ; for ( const pickleStep of pickleSteps ) { for ( const astNodeId of pickleStep . astNodeIds ) { if ( ! this . pickleStepIdsByAstNodeId . has ( astNodeId ) ) { this . pickleStepIdsByAstNodeId . set ( astNodeId , [ ] ) ; } this . pickleStepIdsByAstNodeId . get ( astNodeId ) . push ( pickleStep . id ) ; } } } } type HasLocation = { location : messages . Location ; } ; function setAstNode < AstNode extends HasLocation > ( map , uri , astNode ) { const line = astNode . location . line ; const uriLine = [ uri , line ] . join ( ':' ) ; map . set ( uriLine , astNode ) ; } function getAstNode < AstNode > ( map , uri , line ) { const uriLine = [ uri , line ] . join ( ':' ) ; return map . get ( uriLine ) ; }