Add log parser to output added file list sorted by size
This commit is contained in:
parent
54decd3526
commit
afcfc0d8c7
31
ResticLogParser.sln
Normal file
31
ResticLogParser.sln
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.13.35931.197 d17.13
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResticLogParser", "ResticLogParser\ResticLogParser.csproj", "{F688B5E5-B5BD-43D2-96E9-A7286A0EB440}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResticLogParserTests", "ResticLogParserTests\ResticLogParserTests.csproj", "{B4D864B6-35C1-4890-A89E-4C13152B569C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F688B5E5-B5BD-43D2-96E9-A7286A0EB440}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F688B5E5-B5BD-43D2-96E9-A7286A0EB440}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F688B5E5-B5BD-43D2-96E9-A7286A0EB440}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F688B5E5-B5BD-43D2-96E9-A7286A0EB440}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B4D864B6-35C1-4890-A89E-4C13152B569C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B4D864B6-35C1-4890-A89E-4C13152B569C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B4D864B6-35C1-4890-A89E-4C13152B569C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B4D864B6-35C1-4890-A89E-4C13152B569C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {D90F1BCC-4C6C-48FC-8920-2B87B5019AAB}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
3
ResticLogParser/Program.cs
Normal file
3
ResticLogParser/Program.cs
Normal file
@ -0,0 +1,3 @@
|
||||
using ResticLogParser.src;
|
||||
|
||||
new LogParser(@"C:\Users\warrence\Documents\My Documents\restic\restic-logfile.txt").Run();
|
10
ResticLogParser/ResticLogParser.csproj
Normal file
10
ResticLogParser/ResticLogParser.csproj
Normal file
@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
36
ResticLogParser/src/FileLogEntry.cs
Normal file
36
ResticLogParser/src/FileLogEntry.cs
Normal file
@ -0,0 +1,36 @@
|
||||
namespace ResticLogParser.src
|
||||
{
|
||||
internal readonly struct FileLogEntry : IComparable<FileLogEntry>
|
||||
{
|
||||
public FileLogEntry(string fileName, FileLogEntryType type, long addedSize)
|
||||
: this(fileName, type, addedSize, addedSize)
|
||||
{
|
||||
}
|
||||
|
||||
public FileLogEntry(string fileName, FileLogEntryType type, long addedSize, long storedSize)
|
||||
{
|
||||
FileName = fileName;
|
||||
Type = type;
|
||||
AddedSize = addedSize;
|
||||
StoredSize = storedSize;
|
||||
}
|
||||
|
||||
public string FileName { get; init; }
|
||||
|
||||
public FileLogEntryType Type { get; init; }
|
||||
|
||||
public long AddedSize { get; init; }
|
||||
|
||||
public long StoredSize { get; init; }
|
||||
|
||||
public int CompareTo(FileLogEntry other)
|
||||
{
|
||||
return AddedSize.CompareTo(other.AddedSize);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Type}, added {AddedSize} bytes, {FileName}";
|
||||
}
|
||||
}
|
||||
}
|
8
ResticLogParser/src/FileLogEntryType.cs
Normal file
8
ResticLogParser/src/FileLogEntryType.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace ResticLogParser.src
|
||||
{
|
||||
internal enum FileLogEntryType
|
||||
{
|
||||
New,
|
||||
Modified
|
||||
}
|
||||
}
|
46
ResticLogParser/src/FileSize.cs
Normal file
46
ResticLogParser/src/FileSize.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace ResticLogParser.src
|
||||
{
|
||||
public class FileSize
|
||||
{
|
||||
public static long GetBytesFromString(string fileSizeText)
|
||||
{
|
||||
string[] split = fileSizeText.Split(" ", StringSplitOptions.TrimEntries);
|
||||
if (split.Length < 2 || split[0].Length < 1 || split[1].Length < 1 || split[1].Length > 3
|
||||
|| split[1][split[1].Length - 1] != 'B')
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var value = double.Parse(split[0], CultureInfo.InvariantCulture.NumberFormat);
|
||||
var power = split[1].Length > 1
|
||||
? split[1][0] switch
|
||||
{
|
||||
'K' => 1,
|
||||
'M' => 2,
|
||||
'G' => 3,
|
||||
'T' => 4,
|
||||
_ => 0
|
||||
}
|
||||
: 0;
|
||||
|
||||
int valueBase;
|
||||
if (split[1].Length < 3)
|
||||
{
|
||||
valueBase = 1000;
|
||||
}
|
||||
else if (split[1][1] == 'i')
|
||||
{
|
||||
valueBase = 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var s = Math.Pow(valueBase, power);
|
||||
var t = value * Math.Pow(valueBase, power);
|
||||
return (long)Math.Round(value * Math.Pow(valueBase, power));
|
||||
}
|
||||
}
|
||||
}
|
91
ResticLogParser/src/LogParser.cs
Normal file
91
ResticLogParser/src/LogParser.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ResticLogParser.src
|
||||
{
|
||||
internal class LogParser
|
||||
{
|
||||
// Example match:
|
||||
// new /E/Projects/Unity/SpriteTransfer/Images/Equipment/Belts/Belt 3 Up.png, saved in 0.000s (7.201 KiB added)
|
||||
private Regex _newFileLogEntryRegex = new(@"new\s+(.+), saved in \d+\.\d+s \((\d+(?:\.\d+)? .+) added\)");
|
||||
|
||||
// Example match:
|
||||
// modified /C/Users/warrence/AppData/Local/Log/Desktop.log, saved in 0.009s (1.135 MiB added, 1.135 MiB stored)
|
||||
private Regex _modifiedFileLogEntryRegex =
|
||||
new(@"modified\s+(.+), saved in \d+\.\d+s \((\d+(?:\.\d+)? .+) added, (\d+(?:\.\d+)? .+) stored\)");
|
||||
|
||||
private string _logFileName;
|
||||
private readonly List<FileLogEntry> _files = new();
|
||||
|
||||
public LogParser(string logFileName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(logFileName))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logFileName));
|
||||
}
|
||||
|
||||
_logFileName = logFileName;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
Console.WriteLine($"Running restic log parser for '{_logFileName}'...");
|
||||
ParseLogFile();
|
||||
_files.Sort();
|
||||
WriteFileInformation();
|
||||
}
|
||||
|
||||
private void ParseLogFile()
|
||||
{
|
||||
try
|
||||
{
|
||||
using StreamReader reader = new(_logFileName);
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
string line = reader.ReadLine()!;
|
||||
TryParseLogEntry(line);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Console.WriteLine("The file could not be read:");
|
||||
Console.WriteLine(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryParseLogEntry(string logLine)
|
||||
{
|
||||
Match match = _newFileLogEntryRegex.Match(logLine);
|
||||
if (match.Success)
|
||||
{
|
||||
var size = FileSize.GetBytesFromString(match.Groups[2].Value);
|
||||
_files.Add(new FileLogEntry(match.Groups[1].Value, FileLogEntryType.New, size));
|
||||
return true;
|
||||
}
|
||||
|
||||
match = _modifiedFileLogEntryRegex.Match(logLine);
|
||||
if (match.Success)
|
||||
{
|
||||
var addedSize = FileSize.GetBytesFromString(match.Groups[2].Value);
|
||||
var storedSize = FileSize.GetBytesFromString(match.Groups[3].Value);
|
||||
_files.Add(new FileLogEntry(match.Groups[1].Value, FileLogEntryType.Modified, addedSize, storedSize));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void WriteFileInformation()
|
||||
{
|
||||
string outputFileName = Path.Combine(Path.GetDirectoryName(_logFileName)!, "restic-added-data.txt");
|
||||
Console.WriteLine($"Writing output to '{outputFileName}'...");
|
||||
|
||||
using (StreamWriter outputFile = new StreamWriter(outputFileName))
|
||||
{
|
||||
for (int i = _files.Count - 1; i >= 0; i--)
|
||||
{
|
||||
outputFile.WriteLine(_files[i].ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
ResticLogParserTests/FileSizeTests.cs
Normal file
18
ResticLogParserTests/FileSizeTests.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using ResticLogParser.src;
|
||||
|
||||
namespace ResticLogParserTests
|
||||
{
|
||||
public class FileSizeTests
|
||||
{
|
||||
[TestCase("0 B", 0)]
|
||||
[TestCase("739 B", 739)]
|
||||
[TestCase("17.601 KiB", 18023)]
|
||||
[TestCase("17.601 KB", 17601)]
|
||||
[TestCase("107.135 MiB", 112339190)]
|
||||
[TestCase("107.135 MB", 107135000)]
|
||||
public void Test(string fileSizeText, long expected)
|
||||
{
|
||||
Assert.That(FileSize.GetBytesFromString(fileSizeText), Is.EqualTo(expected));
|
||||
}
|
||||
}
|
||||
}
|
28
ResticLogParserTests/ResticLogParserTests.csproj
Normal file
28
ResticLogParserTests/ResticLogParserTests.csproj
Normal file
@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.9.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ResticLogParser\ResticLogParser.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="NUnit.Framework" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
Loading…
x
Reference in New Issue
Block a user