decodeOp function

Op decodeOp(
  1. Object? input
)

Parses untrusted input into an op, or throws with the reason. Schema checks happen later.

Implementation

Op decodeOp(Object? input) {
  if (input is! Map) throw AccordException('op must be an object');
  final o = input;
  final opId = _str(o, 'op_id');
  final record = _str(o, 'record');
  final field = _str(o, 'field');
  final hlc = Hlc.decode(_str(o, 'hlc'));
  final device = parseOpId(opId).device;
  recordType(record);
  if (device != hlc.node) throw AccordException('op $opId carries a clock from "${hlc.node}"');
  switch (o['kind']) {
    case 'assign':
      if (!o.containsKey('value')) throw AccordException('assign needs a value');
      return AssignOp(
        opId: opId,
        record: record,
        field: field,
        hlc: hlc,
        value: o['value'],
        deps: _deps(o),
      );
    case 'inc':
      final by = o['by'];
      // JSON decoders may hand a whole number over as a double (`3.0`); JavaScript sees one number.
      final int? n = by is int
          ? by
          : (by is double && by == by.truncateToDouble() ? by.toInt() : null);
      if (n == null || n.abs() > maxSafeInteger) {
        throw AccordException('inc needs an integer "by"');
      }
      return IncOp(opId: opId, record: record, field: field, hlc: hlc, by: n);
    case 'add':
      return AddOp(
        opId: opId,
        record: record,
        field: field,
        hlc: hlc,
        element: _element(o),
        deps: o.containsKey('deps') ? _deps(o) : const [],
      );
    case 'remove':
      return RemoveOp(
        opId: opId,
        record: record,
        field: field,
        hlc: hlc,
        element: _element(o),
        deps: _deps(o),
      );
    default:
      throw AccordException('unknown op kind ${o['kind']}');
  }
}