manual method

Map<String, Future<ImportResult>> manual(
  1. List<File> inputFiles, {
  2. FutureOr<bool> collisionHandler(
    1. String filename,
    2. String storeName
    )?,
})

Import stores from specified inputFiles

Setting collisionHandler allows for custom behaviour in the event that a store with the same name already exists. If it returns true, the store will be overwritten, otherwise (and by default) the import will fail.

Note that there are no checks to confirm that the listed inputFiles are valid Isar databases. Attempting to import other files or corrupted databases may cause the app to crash.

Also see withGUI for a prebuilt solution to allow the user to select files to import.

Returns a Map of the input filename to its corresponding ImportResult.

Implementation

Map<String, Future<ImportResult>> manual(
  List<File> inputFiles, {
  FutureOr<bool> Function(String filename, String storeName)?
      collisionHandler,
}) =>
    Map.fromEntries(
      inputFiles.map(
        (f) => MapEntry(
          p.basename(f.path),
          () async {
            // Quit if the input file no longer exists
            if (!await f.exists()) {
              return const ImportResult._(storeName: null, successful: false);
            }

            // Create the temporary directory
            final tmpDir = FMTC.instance.rootDirectory.directory >> 'import';
            await tmpDir.create();

            // Construct temporary structures to read the Isar database at an
            // appropriate location
            final tmpPath = tmpDir >
                '.import${DateTime.now().millisecondsSinceEpoch}.isar';
            final tmpFile = File(tmpPath);
            Isar? tmpDb;

            // Copy the target file to the temporary file and try to open it
            await f.copy(tmpPath);
            try {
              tmpDb = await Isar.open(
                [DbStoreDescriptorSchema, DbTileSchema, DbMetadataSchema],
                name: tmpPath.replaceAll('.isar', ''),
                directory: tmpDir.absolute.path,
                maxSizeMiB: FMTC.instance.settings.databaseMaxSize,
                compactOnLaunch:
                    FMTC.instance.settings.databaseCompactCondition,
                inspector: FMTC.instance.debugMode,
              );
            } catch (_) {
              if (tmpDb != null && tmpDb.isOpen) await tmpDb.close();
              if (await tmpDir.exists()) await tmpDir.delete(recursive: true);
              return const ImportResult._(storeName: null, successful: false);
            }

            // Read the store name from within the temporary database, closing
            // it afterward
            final storeName = (await tmpDb.storeDescriptor.get(0))?.name;
            if (storeName == null) {
              return const ImportResult._(storeName: null, successful: false);
            }
            if (tmpDb.isOpen) await tmpDb.close();

            // Check if there is a conflict with an existing store
            if (FMTC.instance(storeName).manage.ready) {
              if (!await (collisionHandler?.call(
                    p.basename(f.path),
                    storeName,
                  ) ??
                  false)) {
                if (await tmpDir.exists()) {
                  await tmpDir.delete(recursive: true);
                }
                return ImportResult._(
                  storeName: storeName,
                  successful: false,
                );
              }
              await FMTC.instance(storeName).manage.delete();
            }

            // Calculate the store's ID, and rename the temporary file to it
            final storeId = DatabaseTools.hash(storeName);
            await tmpFile.rename(
              (FMTC.instance.rootDirectory.directory >> '$storeId.isar')
                  .absolute
                  .path,
            );

            // Register the new store instance
            // Doesn't require error catching, as the same database has already
            // been opened.
            FMTCRegistry.instance.register(
              storeId,
              await Isar.open(
                [DbStoreDescriptorSchema, DbTileSchema, DbMetadataSchema],
                name: storeId.toString(),
                directory: FMTC.instance.rootDirectory.directory.path,
                maxSizeMiB: FMTC.instance.settings.databaseMaxSize,
                compactOnLaunch:
                    FMTC.instance.settings.databaseCompactCondition,
                inspector: FMTC.instance.debugMode,
              ),
            );

            // Delete temporary structures
            if (await tmpDir.exists()) await tmpDir.delete(recursive: true);

            return ImportResult._(storeName: storeName, successful: true);
          }(),
        ),
      ),
    );