restore method

void restore(
  1. Snapshot snapshot, {
  2. List<String>? keys,
  3. bool continueOnError = false,
})

Restores state from a snapshot.

This method:

  1. Gets all keys from the snapshot's states in sorted order (deterministic)
  2. For each key, calls the registered importer function with the state data
  3. Logs restore operations for debugging
  4. Handles errors gracefully (logs and continues or throws based on severity)

Parameters:

  • snapshot: The snapshot containing state to restore
  • keys: Optional list of keys to restore. If provided, only the specified state contributors will be restored. If null, all registered contributors that exist in the snapshot will be restored.
  • continueOnError: If true, continue restoring other keys even if one fails. Defaults to false. When true, errors are logged but don't stop the restore process.

Throws:

  • StateError if no state contributors are registered
  • ArgumentError if keys contains a key that doesn't exist in the snapshot
  • Any exception thrown by an importer function (unless continueOnError is true)

Deterministic Order: State is restored in alphabetical order of keys, ensuring deterministic restore behavior regardless of snapshot creation order.

Partial Restore: If a key in the snapshot is not registered, it will be logged and skipped. This allows restoring snapshots that may have been created with additional state contributors that are no longer registered.

Implementation

void restore(
  Snapshot snapshot, {
  List<String>? keys,
  bool continueOnError = false,
}) {
  final registry = SnapshotRegistry.instance;
  final registeredKeys = registry.getAllKeys();

  if (registeredKeys.isEmpty) {
    throw StateError(
      'No state contributors registered. Register at least one state '
      'contributor before restoring a snapshot.',
    );
  }

  // Determine which keys to restore
  final snapshotKeys = (snapshot.states.keys.toList()..sort());
  final keysToRestore = keys != null
      ? (keys.toList()..sort())
      : snapshotKeys;

  // Validate that all requested keys exist in the snapshot
  for (final key in keysToRestore) {
    if (!snapshot.states.containsKey(key)) {
      throw ArgumentError.value(
        key,
        'keys',
        'Key "$key" does not exist in the snapshot.',
      );
    }
  }

  if (keysToRestore.isEmpty) {
    debugPrint('[Checkpoint] No keys to restore');
    return;
  }

  debugPrint(
    '[Checkpoint] Starting restore of ${keysToRestore.length} state(s)',
  );

  final errors = <String>[];

  // Restore state from specified keys in deterministic order
  for (final key in keysToRestore) {
    final stateData = snapshot.states[key];

    // Check if this key is registered
    if (!registry.isRegistered(key)) {
      final message = 'Skipping restore for unregistered key "$key"';
      debugPrint('[Checkpoint] Warning: $message');
      if (!continueOnError) {
        errors.add(message);
      }
      continue;
    }

    final importer = registry.getImporter(key);
    if (importer == null) {
      // This shouldn't happen if registry is consistent, but handle it
      final message = 'No importer found for registered key "$key"';
      debugPrint('[Checkpoint] Warning: $message');
      if (!continueOnError) {
        errors.add(message);
      }
      continue;
    }

    try {
      debugPrint('[Checkpoint] Restoring state for key "$key"');
      importer(stateData);
      debugPrint('[Checkpoint] Successfully restored state for key "$key"');
    } catch (e, stackTrace) {
      final errorMessage = 'Failed to restore state for key "$key": $e';
      debugPrint('[Checkpoint] Error: $errorMessage\n$stackTrace');

      if (continueOnError) {
        errors.add(errorMessage);
      } else {
        // Propagate errors from importer functions
        // This ensures developers know if their importer has issues
        throw StateError(errorMessage);
      }
    }
  }

  if (errors.isNotEmpty) {
    debugPrint(
      '[Checkpoint] Restore completed with ${errors.length} error(s):\n'
      '${errors.join('\n')}',
    );
  } else {
    debugPrint('[Checkpoint] Restore completed successfully');
  }
}