All files / json-patch/op OpStrDel.ts

95% Statements 38/40
87.5% Branches 14/16
87.5% Functions 7/8
97.36% Lines 37/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 85 86 87  15x   15x 15x           15x     115x 115x 115x   115x       22x               17x       76x 76x 76x 76x 76x 76x 76x 76x 76x 76x 76x 20x 76x       18x 8x             10x                 12x 12x           6x 6x 6x 6x 6x 6x 2x   4x 4x        
import type {CompactStrDelOp, OPCODE_STR_DEL} from '../codec/compact/types';
import {AbstractOp} from './AbstractOp';
import type {OperationStrDel} from '../types';
import {find, type Path, formatJsonPointer} from '@jsonjoy.com/json-pointer';
import {OPCODE} from '../constants';
import type {IMessagePackEncoder} from '@jsonjoy.com/json-pack/lib/msgpack';
 
/**
 * @category JSON Patch Extended
 */
export class OpStrDel extends AbstractOp<'str_del'> {
  constructor(
    path: Path,
    public readonly pos: number,
    public readonly str: string | undefined,
    public readonly len: number | undefined,
  ) {
    super(path);
  }
 
  public op() {
    return 'str_del' as const;
  }
 
  public code() {
    return OPCODE.str_del;
  }
 
  public deleteLength(): number {
    return typeof this.str === 'string' ? this.str.length : this.len!;
  }
 
  public apply(doc: unknown) {
    const {val, key, obj} = find(doc, this.path);
    Iif (typeof val !== 'string') throw new Error('NOT_A_STRING');
    const length = val.length;
    const pos = Math.min(this.pos, val.length);
    const start = Math.min(pos, length);
    const deletionLength: number = this.str !== undefined ? this.str.length : this.len!;
    const end = Math.min(pos + deletionLength, length);
    const before = val.slice(0, start);
    const after = val.substr(end);
    const result = before + after;
    if (obj) (obj as any)[key as any] = result;
    else doc = result;
    return {doc, old: val};
  }
 
  public toJson(parent?: AbstractOp): OperationStrDel {
    if (typeof this.str === 'string') {
      return {
        op: 'str_del',
        path: formatJsonPointer(this.path),
        pos: this.pos,
        str: this.str,
      };
    }
    return {
      op: 'str_del',
      path: formatJsonPointer(this.path),
      pos: this.pos,
      len: this.len,
    };
  }
 
  public toCompact(parent: undefined | AbstractOp, verbose: boolean): CompactStrDelOp {
    const opcode: OPCODE_STR_DEL = verbose ? 'str_del' : OPCODE.str_del;
    return typeof this.str === 'string'
      ? [opcode, this.path, this.pos, this.str]
      : ([opcode, this.path, this.pos, 0, this.len] as CompactStrDelOp);
  }
 
  public encode(encoder: IMessagePackEncoder, parent?: AbstractOp) {
    const hasStr = typeof this.str === 'string';
    encoder.encodeArrayHeader(hasStr ? 4 : 5);
    encoder.writer.u8(OPCODE.str_del);
    encoder.encodeArray(this.path as unknown[]);
    encoder.encodeNumber(this.pos);
    if (hasStr) {
      encoder.encodeString(this.str as string);
    } else {
      encoder.writer.u8(0);
      encoder.encodeNumber(this.len!);
    }
  }
}