From 540ccdb6d0dfe933c82dd08d5f314895e9a2ea03 Mon Sep 17 00:00:00 2001 From: AliReZa Sabouri Date: Sat, 29 Jan 2022 18:41:43 +0330 Subject: [PATCH] fix: use inline caching #23 --- src/Husky/TaskRunner/StagedTask.cs | 49 ++++++++++----------------- src/Husky/TaskRunner/TemporaryFile.cs | 35 +++++++++++++++++++ src/Husky/Utils/DisposableScope.cs | 41 ++++++++++++++++++++++ 3 files changed, 93 insertions(+), 32 deletions(-) create mode 100644 src/Husky/TaskRunner/TemporaryFile.cs create mode 100644 src/Husky/Utils/DisposableScope.cs diff --git a/src/Husky/TaskRunner/StagedTask.cs b/src/Husky/TaskRunner/StagedTask.cs index 287f7c0..4778ebf 100644 --- a/src/Husky/TaskRunner/StagedTask.cs +++ b/src/Husky/TaskRunner/StagedTask.cs @@ -3,6 +3,7 @@ using CliFx.Exceptions; using Husky.Services.Contracts; using Husky.Stdout; +using Husky.Utils; namespace Husky.TaskRunner; @@ -39,38 +40,33 @@ public override async Task Execute() private async Task PartialExecution(List partialStagedFiles) { - // create tmp folder - var huskyPath = await _git.GetHuskyPathAsync(); - var cache = Path.Combine(huskyPath, "_", "cache"); - if (!_fileSystem.Directory.Exists(cache)) - _fileSystem.Directory.CreateDirectory(cache); - var arguments = TaskInfo.Arguments.ToList(); var tmpFiles = new List(); var stagedRecord = await GetStagedRecord(); - foreach (var psf in partialStagedFiles) - { - var record = stagedRecord.First(q => q.src_path == psf.RelativePath); - // first, we need to create a temporary file - var tmpFile = Path.Combine(cache, _fileSystem.FileInfo.FromFileName(psf.RelativePath).Name); + using var scope = new DisposableScope(() => "Cleaning up ...".LogVerbose()); - if (psf.PathMode == PathModes.Absolute) - tmpFile = Path.GetFullPath(tmpFile); + foreach (var psf in partialStagedFiles) + { + // create temp file + var tmpFile = scope.Using(new TemporaryFile(_fileSystem, psf)); + // find the diff record for the file + var record = stagedRecord.First(q => q.src_path == psf.RelativePath); var hash = record.dst_hash; + + // add staged content to temp file { await using var output = _fileSystem.File.Create(tmpFile); - await ( - _cliWrap.Wrap("git").WithArguments(new[] { "cat-file", "blob", hash }!) - | output - ).ExecuteAsync(); + await (_cliWrap.Wrap("git").WithArguments(new[] { "cat-file", "blob", hash }!) | output).ExecuteAsync(); } // insert the temporary file into the arguments var index = arguments.FindIndex(q => q == psf.Argument); arguments.Insert(index, tmpFile); + + // keep track of the temporary files and diff records tmpFiles.Add(record with { tmp_path = tmpFile }); } @@ -90,9 +86,6 @@ private async Task PartialExecution(List partialStaged // check if the partial hash exists if (string.IsNullOrEmpty(newHash)) { - if (_fileSystem.File.Exists(tf.tmp_path)) - _fileSystem.File.Delete(tf.tmp_path); - throw new CommandException( "Failed to hash temp file. Please check the partial staged files." ); @@ -109,23 +102,15 @@ await _git.ExecAsync( { $"file {tf.src_path} did not changed by formatters".LogVerbose(); } - - // remove the temporary file - if (_fileSystem.File.Exists(tf.tmp_path)) - _fileSystem.File.Delete(tf.tmp_path); } - // clean up the cache folder - if (_fileSystem.Directory.Exists(cache)) - _fileSystem.Directory.Delete(cache, true); - // re-staged staged files await ReStageFiles(partialStagedFiles); return executionTime; } - private async Task ReStageFiles(List partialStagedFiles) + private async Task ReStageFiles(IEnumerable partialStagedFiles) { var stagedFiles = TaskInfo.ArgumentInfo .OfType() @@ -159,7 +144,7 @@ private async Task> GetStagedRecord() return stagedRecord; } - private DiffRecord ParseDiff(string diff) + private static DiffRecord ParseDiff(string diff) { // Format: src_mode dst_mode src_hash dst_hash status/score? src_path dst_path? var diff_pat = new Regex( @@ -186,7 +171,7 @@ private DiffRecord ParseDiff(string diff) /// returns `None` /// /// - private string? UnlessZeroed(string s) + private static string? UnlessZeroed(string s) { var zeroed_pat = new Regex(@"^0+$"); return zeroed_pat.IsMatch(s) ? null : s; @@ -201,6 +186,6 @@ private record DiffRecord( int? score, string? src_path, string? dst_path, - string? tmp_path = null + TemporaryFile? tmp_path = null ); } diff --git a/src/Husky/TaskRunner/TemporaryFile.cs b/src/Husky/TaskRunner/TemporaryFile.cs new file mode 100644 index 0000000..2190b00 --- /dev/null +++ b/src/Husky/TaskRunner/TemporaryFile.cs @@ -0,0 +1,35 @@ +using System.IO.Abstractions; + +namespace Husky.TaskRunner; + +public sealed class TemporaryFile : IDisposable +{ + private readonly IFileSystem _fileSystem; + private readonly string _filePath; + + public TemporaryFile(IFileSystem fileSystem, FileArgumentInfo fileArgumentInfo) + { + _fileSystem = fileSystem; + + var path = fileArgumentInfo.PathMode == PathModes.Absolute + ? fileArgumentInfo.AbsolutePath + : fileArgumentInfo.RelativePath; + + var fileInfo = _fileSystem.FileInfo.FromFileName(path); + var guid = Guid.NewGuid().ToString()[..5]; + _filePath = path.Replace(fileInfo.Name, $"{guid}_{fileInfo.Name}"); + } + + public static implicit operator string(TemporaryFile temporaryFile) => temporaryFile._filePath; + + public void Dispose() + { + if (_fileSystem.File.Exists(_filePath)) + _fileSystem.File.Delete(_filePath); + } + + public override string ToString() + { + return _filePath; + } +} diff --git a/src/Husky/Utils/DisposableScope.cs b/src/Husky/Utils/DisposableScope.cs new file mode 100644 index 0000000..41eed47 --- /dev/null +++ b/src/Husky/Utils/DisposableScope.cs @@ -0,0 +1,41 @@ +using Husky.Stdout; + +namespace Husky.Utils; + public sealed class DisposableScope : IDisposable +{ + private readonly Action? _beforeDispose; + private readonly Action? _afterDispose; + + // you can use ConcurrentQueue if you need thread-safe solution + private readonly Queue _disposables = new(); + + public DisposableScope(Action? beforeDispose = default, Action? afterDispose = default) + { + _beforeDispose = beforeDispose; + _afterDispose = afterDispose; + } + + public T Using(T disposable) where T : IDisposable + { + _disposables.Enqueue(disposable); + return disposable; + } + + public void Dispose() + { + _beforeDispose?.Invoke(); + foreach (var item in _disposables) + { + try + { + item.Dispose(); + } + catch (Exception e) + { + e.Message.LogVerbose(ConsoleColor.DarkRed); + } + } + _afterDispose?.Invoke(); + } +} +