Code coverage report for src/state.js

Statements: 100% (27 / 27)      Branches: 93.75% (15 / 16)      Functions: 100% (5 / 5)      Lines: 100% (23 / 23)      Ignored: none     

All files » src/ » state.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84                                    1 285 285       258 11     11 1         258 258         1                         26                 200 200   200 217 217   217 137 124   80 80 74         200      
// A State has a character specification and (`charSpec`) and a list of possible
// subsequent states (`nextStates`).
//
// If a State is an accepting state, it will also have several additional
// properties:
//
// * `regex`: A regular expression that is used to extract parameters from paths
//   that reached this accepting state.
// * `handlers`: Information on how to convert the list of captures into calls
//   to registered handlers with the specified parameters.
// * `types`: How many static, dynamic, or star segments in this route. Used to
//   decide which route to use if multiple registered routes match a path.
//
// Currently, State is implemented naively by looping over `nextStates` and
// comparing a character specification against a character. A more efficient
// implementation would use a hash of keys pointing at one or more next states.
 
export class State {
  constructor(charSpec: CharSpec) {
    this.charSpec = charSpec;
    this.nextStates = [];
  }
 
  get(charSpec: CharSpec): State {
    for (let child of this.nextStates) {
      let isEqual = child.charSpec.validChars === charSpec.validChars
        && child.charSpec.invalidChars === charSpec.invalidChars;
 
      if (isEqual) {
        return child;
      }
    }
  }
 
  put(charSpec: CharSpec): State {
    let state = this.get(charSpec);
 
    // If the character specification already exists in a child of the current
    // state, just return that state.
    if (state) {
      return state;
    }
 
    // Make a new state for the character spec
    state = new State(charSpec);
 
    // Insert the new state as a child of the current state
    this.nextStates.push(state);
 
    // If this character specification repeats, insert the new state as a child
    // of itself. Note that this will not trigger an infinite loop because each
    // transition during recognition consumes a character.
    if (charSpec.repeat) {
      state.nextStates.push(state);
    }
 
    // Return the new state
    return state;
  }
 
  // Find a list of child states matching the next character
  match(ch: string): State[] {
    let nextStates = this.nextStates;
    let results = [];
 
    for (let i = 0, l = nextStates.length; i < l; i++) {
      let child = nextStates[i];
      let charSpec = child.charSpec;
 
      if (charSpec.validChars !== undefined) {
        if (charSpec.validChars.indexOf(ch) !== -1) {
          results.push(child);
        }
      } else Eif (charSpec.invalidChars !== undefined) {
        if (charSpec.invalidChars.indexOf(ch) === -1) {
          results.push(child);
        }
      }
    }
 
    return results;
  }
}