save method

Future<SaveResult> save(
  1. T value, {
  2. required SaveContext context,
})

Saves a new value with an explicit context.

Returns a SaveResult so failures are observable and testable.

Implementation

Future<SaveResult> save(
  T value, {
  required SaveContext context,
}) async {
  final nowMs = _clock.now().millisecondsSinceEpoch;

  String? existingRaw;
  SaveEnvelope? existingEnvelope;
  try {
    existingRaw = await _store.read();
  } catch (error, stackTrace) {
    return SaveFailure(
      reason: SaveFailureReason.readFailed,
      context: context,
      error: error,
      stackTrace: stackTrace,
    );
  }

  if (existingRaw != null) {
    try {
      final decoded = _codec.decode(existingRaw);
      existingEnvelope = SaveEnvelope.fromJson(decoded);
    } catch (_) {
      existingEnvelope = null;
    }
  }

  final createdAtMs = existingEnvelope?.createdAtMs ?? nowMs;

  Map<String, dynamic> payload;
  try {
    payload = _encoder(value);
  } catch (error, stackTrace) {
    return SaveFailure(
      reason: SaveFailureReason.encodeFailed,
      context: context,
      error: error,
      stackTrace: stackTrace,
    );
  }
  if (_validatePayload) {
    try {
      JsonSafe.validate(payload);
    } on FormatException catch (error, stackTrace) {
      return SaveFailure(
        reason: SaveFailureReason.invalidPayload,
        context: context,
        error: error,
        stackTrace: stackTrace,
      );
    }
  }
  var envelope = SaveEnvelope(
    schemaVersion: _migrator.latestVersion,
    createdAtMs: createdAtMs,
    updatedAtMs: nowMs,
    payload: payload,
    saveReason: context.reason.value,
    changeSet: context.changeSet,
  );

  envelope = _applyChecksum(envelope);

  String raw;
  try {
    raw = _codec.encode(envelope.toJson());
  } catch (error, stackTrace) {
    return SaveFailure(
      reason: SaveFailureReason.encodeFailed,
      context: context,
      error: error,
      stackTrace: stackTrace,
      envelope: envelope,
    );
  }

  var backupWritten = false;
  if (_backupStore != null && existingRaw != null) {
    try {
      await _backupStore!.write(existingRaw);
      backupWritten = true;
    } catch (error, stackTrace) {
      return SaveFailure(
        reason: SaveFailureReason.backupWriteFailed,
        context: context,
        error: error,
        stackTrace: stackTrace,
        envelope: envelope,
        raw: raw,
        backupWritten: backupWritten,
      );
    }
  }

  try {
    await _store.write(raw);
  } catch (error, stackTrace) {
    return SaveFailure(
      reason: SaveFailureReason.writeFailed,
      context: context,
      error: error,
      stackTrace: stackTrace,
      envelope: envelope,
      raw: raw,
      backupWritten: backupWritten,
    );
  }

  return SaveSuccess(
    envelope: envelope,
    raw: raw,
    context: context,
    backupWritten: backupWritten,
  );
}