regexp.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import { BSONValue } from './bson_value';
  2. import { BSONError } from './error';
  3. import type { EJSONOptions } from './extended_json';
  4. function alphabetize(str: string): string {
  5. return str.split('').sort().join('');
  6. }
  7. /** @public */
  8. export interface BSONRegExpExtendedLegacy {
  9. $regex: string | BSONRegExp;
  10. $options: string;
  11. }
  12. /** @public */
  13. export interface BSONRegExpExtended {
  14. $regularExpression: {
  15. pattern: string;
  16. options: string;
  17. };
  18. }
  19. /**
  20. * A class representation of the BSON RegExp type.
  21. * @public
  22. * @category BSONType
  23. */
  24. export class BSONRegExp extends BSONValue {
  25. get _bsontype(): 'BSONRegExp' {
  26. return 'BSONRegExp';
  27. }
  28. pattern!: string;
  29. options!: string;
  30. /**
  31. * @param pattern - The regular expression pattern to match
  32. * @param options - The regular expression options
  33. */
  34. constructor(pattern: string, options?: string) {
  35. super();
  36. this.pattern = pattern;
  37. this.options = alphabetize(options ?? '');
  38. if (this.pattern.indexOf('\x00') !== -1) {
  39. throw new BSONError(
  40. `BSON Regex patterns cannot contain null bytes, found: ${JSON.stringify(this.pattern)}`
  41. );
  42. }
  43. if (this.options.indexOf('\x00') !== -1) {
  44. throw new BSONError(
  45. `BSON Regex options cannot contain null bytes, found: ${JSON.stringify(this.options)}`
  46. );
  47. }
  48. // Validate options
  49. for (let i = 0; i < this.options.length; i++) {
  50. if (
  51. !(
  52. this.options[i] === 'i' ||
  53. this.options[i] === 'm' ||
  54. this.options[i] === 'x' ||
  55. this.options[i] === 'l' ||
  56. this.options[i] === 's' ||
  57. this.options[i] === 'u'
  58. )
  59. ) {
  60. throw new BSONError(`The regular expression option [${this.options[i]}] is not supported`);
  61. }
  62. }
  63. }
  64. static parseOptions(options?: string): string {
  65. return options ? options.split('').sort().join('') : '';
  66. }
  67. /** @internal */
  68. toExtendedJSON(options?: EJSONOptions): BSONRegExpExtendedLegacy | BSONRegExpExtended {
  69. options = options || {};
  70. if (options.legacy) {
  71. return { $regex: this.pattern, $options: this.options };
  72. }
  73. return { $regularExpression: { pattern: this.pattern, options: this.options } };
  74. }
  75. /** @internal */
  76. static fromExtendedJSON(doc: BSONRegExpExtendedLegacy | BSONRegExpExtended): BSONRegExp {
  77. if ('$regex' in doc) {
  78. if (typeof doc.$regex !== 'string') {
  79. // This is for $regex query operators that have extended json values.
  80. if (doc.$regex._bsontype === 'BSONRegExp') {
  81. return doc as unknown as BSONRegExp;
  82. }
  83. } else {
  84. return new BSONRegExp(doc.$regex, BSONRegExp.parseOptions(doc.$options));
  85. }
  86. }
  87. if ('$regularExpression' in doc) {
  88. return new BSONRegExp(
  89. doc.$regularExpression.pattern,
  90. BSONRegExp.parseOptions(doc.$regularExpression.options)
  91. );
  92. }
  93. throw new BSONError(`Unexpected BSONRegExp EJSON object form: ${JSON.stringify(doc)}`);
  94. }
  95. /** @internal */
  96. [Symbol.for('nodejs.util.inspect.custom')](): string {
  97. return this.inspect();
  98. }
  99. inspect(): string {
  100. return `new BSONRegExp(${JSON.stringify(this.pattern)}, ${JSON.stringify(this.options)})`;
  101. }
  102. }