lipp/node-jet

View on GitHub
src/3_jet/daemon/path_matcher.ts

Summary

Maintainability
C
1 day
Test Coverage
import { InvalidArgument } from '../errors.js'
import { FetchParams } from '../messages.js'
import { PathRule, pathRules } from '../types.js'

type functionGenerator =
  | ((what: string) => (path: string) => boolean)
  | ((what: string[]) => (path: string) => boolean)

const contains = (what: string) => (path: string) => path.indexOf(what) !== -1

const containsAllOf = (whatArray: string[]) => {
  return (path: string) => {
    let i
    for (i = 0; i < whatArray.length; i = i + 1) {
      if (path.indexOf(whatArray[i]) === -1) {
        return false
      }
    }
    return true
  }
}

const containsOneOf = (whatArray: string[]) => {
  return (path: string) => {
    let i
    for (i = 0; i < whatArray.length; i = i + 1) {
      if (path.indexOf(whatArray[i]) !== -1) {
        return true
      }
    }
    return false
  }
}

const startsWith = (what: string) => (path: string) =>
  path.substring(0, what.length) === what

const endsWith = (what: string) => (path: string) =>
  path.lastIndexOf(what) === path.length - what.length

const equals = (what: string) => (path: string) => path === what

const equalsOneOf = (whatArray: string[]) => (path: string) => {
  let i
  for (i = 0; i < whatArray.length; i = i + 1) {
    if (path === whatArray[i]) {
      return true
    }
  }
  return false
}

const negate = (gen: functionGenerator): functionGenerator =>
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  ((args: any) => () => !gen(args)) as functionGenerator

const generators: Record<PathRule, functionGenerator> = {
  equals: equals,
  equalsNot: negate(equals),
  contains: contains,
  containsNot: negate(contains),
  containsAllOf: containsAllOf,
  containsOneOf: containsOneOf,
  startsWith: startsWith,
  startsNotWith: negate(startsWith),
  endsWith: endsWith,
  endsNotWith: negate(endsWith),
  equalsOneOf: equalsOneOf,
  equalsNotOneOf: negate(equalsOneOf)
}

export const createPathMatcher = (options: FetchParams) => {
  if (!options.path) {
    return () => true
  }
  const po = options.path
  Object.keys(po).forEach((key) => {
    if (!(key in generators) && key !== 'caseInsensitive')
      throw new InvalidArgument('unknown rule ' + key)
  })
  const predicates: ((path: string) => boolean)[] = []
  pathRules.forEach((name) => {
    let option = po[name]
    if (option) {
      const gen = generators[name]
      if (po.caseInsensitive) {
        if (Array.isArray(option)) {
          option = option.map((op) => op.toLowerCase())
        } else {
          option = option.toLowerCase()
        }
      }
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      predicates.push(gen(option as any))
    }
  })

  const applyPredicates = (path: string) => {
    for (let i = 0; i < predicates.length; ++i) {
      if (!predicates[i](path)) {
        return false
      }
    }
    return true
  }

  return predicates.length === 1
    ? (path: string) => predicates[0](path)
    : (path: string) => applyPredicates(path)
}