Files
GitJournal/lib/core/file/file_storage.dart
Vishesh Handa e1ea7a4953 Fetch the modified + created time from git
Fixes #78

This is probably the largest commit that I have ever made. From now on -
every File always has an mtime and ctime which is fetched from git.
Notes can optionally override that time by providing yaml metadata.

Additionally the 'filePath' and 'folderPath' is now relative to the
repoPath instead of being the full path.

This will slow down GitJournal like crazy as all the mtimes and ctime
still need to be cached. For my test repo it takes about 23 seconds for
GitJournal to become responsive.
2021-10-26 17:49:08 +02:00

115 lines
3.1 KiB
Dart

/*
* SPDX-FileCopyrightText: 2019-2021 Vishesh Handa <me@vhanda.in>
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import 'package:dart_git/blob_ctime_builder.dart';
import 'package:dart_git/dart_git.dart';
import 'package:dart_git/file_mtime_builder.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:universal_io/io.dart' as io;
import 'file.dart';
class FileStorage {
final String repoPath;
final GitRepository gitRepo;
final BlobCTimeBuilder blobCTimeBuilder;
final FileMTimeBuilder fileMTimeBuilder;
FileStorage({
required this.gitRepo,
required this.blobCTimeBuilder,
required this.fileMTimeBuilder,
}) : repoPath = gitRepo.workTree;
Future<Result<File>> load(String filePath) async {
assert(!filePath.startsWith(p.separator));
var fullFilePath = p.join(repoPath, filePath);
assert(fileMTimeBuilder.map.isNotEmpty);
assert(blobCTimeBuilder.map.isNotEmpty);
var ioFile = io.File(fullFilePath);
var stat = ioFile.statSync();
if (stat.type == io.FileSystemEntityType.notFound) {
var ex = Exception("File note found - $fullFilePath");
return Result.fail(ex);
}
if (stat.type != io.FileSystemEntityType.file) {
// FIXME: Better error!
var ex = Exception('File is not file. Is ${stat.type}');
return Result.fail(ex);
}
var mTimeInfo = fileMTimeBuilder.info(filePath);
if (mTimeInfo == null) {
var ex = Exception('fileMTimeBuilder failed to find path');
return Result.fail(ex);
}
var oid = mTimeInfo.hash;
var modified = mTimeInfo.dt;
var created = blobCTimeBuilder.cTime(oid);
if (created == null) {
var ex = Exception('when can this happen?');
return Result.fail(ex);
}
return Result(File(
oid: oid,
filePath: filePath,
repoPath: repoPath,
fileLastModified: stat.modified,
created: created,
modified: modified,
));
}
@visibleForTesting
static Future<FileStorage> fake(String rootFolder) async {
assert(rootFolder.startsWith(p.separator));
await GitRepository.init(rootFolder).throwOnError();
var blobVisitor = BlobCTimeBuilder();
var mTimeBuilder = FileMTimeBuilder();
var repo = await GitRepository.load(rootFolder).getOrThrow();
var result = await repo.headHash();
if (result.isSuccess) {
var multi = MultiTreeEntryVisitor([blobVisitor, mTimeBuilder]);
await repo
.visitTree(fromCommitHash: result.getOrThrow(), visitor: multi)
.throwOnError();
}
// assert(!headHashR.isFailure, "Failed to get head hash");
return FileStorage(
gitRepo: repo,
blobCTimeBuilder: blobVisitor,
fileMTimeBuilder: mTimeBuilder,
);
}
@visibleForTesting
Future<Result<void>> reload() async {
var result = await gitRepo.headHash();
if (result.isFailure) {
return fail(result);
}
var headHash = result.getOrThrow();
var multi = MultiTreeEntryVisitor([blobCTimeBuilder, fileMTimeBuilder]);
await gitRepo
.visitTree(fromCommitHash: headHash, visitor: multi)
.throwOnError();
return Result(null);
}
}