initial commit
This commit is contained in:
47
Storage/DirectorySnapshot.cs
Normal file
47
Storage/DirectorySnapshot.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using DTLib;
|
||||
using DTLib.Dtsod;
|
||||
|
||||
namespace SyncDirectory.Storage;
|
||||
|
||||
public record DirectorySnapshot(
|
||||
string Name,
|
||||
IOPath Path,
|
||||
DateTime SnapshotTimeUTC,
|
||||
IReadOnlyCollection<FileSnapshot> Files)
|
||||
{
|
||||
public static DirectorySnapshot Parse(DtsodV23 dtsod) =>
|
||||
new DirectorySnapshot(
|
||||
dtsod["name"],
|
||||
dtsod["path"],
|
||||
DateTime.ParseExact(
|
||||
(string)dtsod["snapshot_time"],
|
||||
MyTimeFormat.ForText,
|
||||
CultureInfo.InvariantCulture),
|
||||
((List<object>)dtsod["files"])
|
||||
.Select(fd=>FileSnapshot.Parse((DtsodV23)fd))
|
||||
.ToImmutableList()
|
||||
);
|
||||
|
||||
public DtsodV23 ToDtsod() =>
|
||||
new()
|
||||
{
|
||||
{"name", Name},
|
||||
{"path", Path.ToString()},
|
||||
{"snapshot_time", SnapshotTimeUTC.ToString(MyTimeFormat.ForText)},
|
||||
{"files", Files.Select(fs=>fs.ToDtsod()).ToImmutableList()}
|
||||
};
|
||||
|
||||
public static DirectorySnapshot Create(string name, IOPath dirPath)
|
||||
{
|
||||
var fileSnapshots = new List<FileSnapshot>();
|
||||
foreach (var filePath in Directory.GetAllFiles(dirPath))
|
||||
{
|
||||
fileSnapshots.Add(FileSnapshot.Create(filePath));
|
||||
}
|
||||
|
||||
return new DirectorySnapshot(name, dirPath, DateTime.UtcNow, fileSnapshots);
|
||||
}
|
||||
}
|
||||
51
Storage/FileSize.cs
Normal file
51
Storage/FileSize.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace SyncDirectory.Storage;
|
||||
|
||||
public class FileSize
|
||||
{
|
||||
public long Bytes { get; }
|
||||
public double KiloBytes => Bytes / 1024.0;
|
||||
public double MegaBytes => KiloBytes / 1024.0;
|
||||
public double GigaBytes => MegaBytes / 1024.0;
|
||||
|
||||
public FileSize(long bytes) => Bytes = bytes;
|
||||
|
||||
public static FileSize Get(IOPath filePath) => new(File.GetSize(filePath));
|
||||
|
||||
public static FileSize Parse(string str)
|
||||
{
|
||||
var caseInvariant = str.ToLowerInvariant().AsSpan();
|
||||
long multi = 1;
|
||||
int skipLast = 0;
|
||||
if (caseInvariant[^1] == 'b')
|
||||
skipLast++;
|
||||
switch (caseInvariant[^(1 + skipLast)])
|
||||
{
|
||||
case 'k':
|
||||
multi = 1024;
|
||||
skipLast++;
|
||||
break;
|
||||
case 'm':
|
||||
multi = 1024*1024;
|
||||
skipLast++;
|
||||
break;
|
||||
case 'b':
|
||||
multi = 1024*1024*1024;
|
||||
skipLast++;
|
||||
break;
|
||||
}
|
||||
|
||||
var numberStr = caseInvariant[..^skipLast].ToString();
|
||||
double number = Convert.ToDouble(numberStr);
|
||||
return new FileSize((long)(number * multi + 0.5));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (GigaBytes > 1) return GigaBytes.ToString(CultureInfo.InvariantCulture) + "G";
|
||||
if (MegaBytes > 1) return MegaBytes.ToString(CultureInfo.InvariantCulture) + "M";
|
||||
if (KiloBytes > 1) return KiloBytes.ToString(CultureInfo.InvariantCulture) + "K";
|
||||
return Bytes.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
38
Storage/FileSnapshot.cs
Normal file
38
Storage/FileSnapshot.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Globalization;
|
||||
using DTLib;
|
||||
using DTLib.Dtsod;
|
||||
|
||||
namespace SyncDirectory.Storage;
|
||||
|
||||
public record FileSnapshot(
|
||||
IOPath Path,
|
||||
FileSize Size,
|
||||
DateTime ModifyTimeUTC,
|
||||
DateTime SnapshotTimeUTC)
|
||||
{
|
||||
public static FileSnapshot Create(IOPath filePath)
|
||||
{
|
||||
var creationTime = System.IO.File.GetCreationTimeUtc(filePath.Str);
|
||||
var modifyTime = System.IO.File.GetLastWriteTimeUtc(filePath.Str);
|
||||
// sometimes LastWriteTime can be less then CreationTime when unpacking archives
|
||||
var latest = modifyTime > creationTime ? modifyTime : creationTime;
|
||||
return new FileSnapshot(filePath, FileSize.Get(filePath), latest, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
public static FileSnapshot Parse(DtsodV23 dtsod) =>
|
||||
new FileSnapshot(
|
||||
dtsod["path"],
|
||||
FileSize.Parse(dtsod["size"]),
|
||||
DateTime.ParseExact((string)dtsod["modify_time"], MyTimeFormat.ForText, CultureInfo.InvariantCulture),
|
||||
DateTime.ParseExact((string)dtsod["snapshot_time"], MyTimeFormat.ForText, CultureInfo.InvariantCulture)
|
||||
);
|
||||
|
||||
public DtsodV23 ToDtsod() =>
|
||||
new()
|
||||
{
|
||||
{"path", Path.ToString()},
|
||||
{"size", Size.ToString()},
|
||||
{"modify_time", ModifyTimeUTC.ToString(MyTimeFormat.ForText)},
|
||||
{"snapshot_time", SnapshotTimeUTC.ToString(MyTimeFormat.ForText)}
|
||||
};
|
||||
}
|
||||
79
Storage/StorageController.cs
Normal file
79
Storage/StorageController.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System.Linq;
|
||||
using DTLib;
|
||||
using DTLib.Dtsod;
|
||||
|
||||
namespace SyncDirectory.Storage;
|
||||
|
||||
public class StorageController
|
||||
{
|
||||
public readonly IOPath DataDir;
|
||||
public readonly IOPath SnapshotsDir;
|
||||
private Dictionary<string, List<DirectorySnapshot>> Snapshots;
|
||||
|
||||
/// <summary>
|
||||
/// creates StorageController and parses the program data
|
||||
/// </summary>
|
||||
/// <param name="dataDir">directory where program data is stored (default=$LocalAppData/SyncDirectory)</param>
|
||||
public StorageController(string? dataDir = null)
|
||||
{
|
||||
if (dataDir == null)
|
||||
{
|
||||
string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
DataDir = Path.Concat(localAppData, "SyncDirectory");
|
||||
}
|
||||
else DataDir = dataDir;
|
||||
SnapshotsDir = Path.Concat(DataDir, "snapshots");
|
||||
Snapshots = new Dictionary<string, List<DirectorySnapshot>>();
|
||||
|
||||
// reading snapshots from SnapshotsDir
|
||||
Directory.Create(SnapshotsDir);
|
||||
foreach (var snapshotFile in Directory.GetAllFiles(SnapshotsDir))
|
||||
{
|
||||
string content = File.ReadAllText(snapshotFile);
|
||||
DtsodV23 dtsod = new DtsodV23(content);
|
||||
var snapshot = DirectorySnapshot.Parse(dtsod);
|
||||
if (!Snapshots.TryAdd(snapshot.Name, new List<DirectorySnapshot> { snapshot }))
|
||||
Snapshots[snapshot.Name].Add(snapshot);
|
||||
}
|
||||
|
||||
// sorting snapshots by time
|
||||
foreach (var snapshotCollection in Snapshots)
|
||||
{
|
||||
Snapshots[snapshotCollection.Key] = snapshotCollection.Value
|
||||
.OrderBy(s => s.SnapshotTimeUTC)
|
||||
.AsParallel()
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetSnapshots(string name, out List<DirectorySnapshot>? snapshots)
|
||||
=> Snapshots.TryGetValue(name, out snapshots!);
|
||||
|
||||
public bool TryGetLatestSnapshot(string name, out DirectorySnapshot? snapshot)
|
||||
{
|
||||
if (TryGetSnapshots(name, out var snapshots))
|
||||
{
|
||||
snapshot = snapshots![^1];
|
||||
return true;
|
||||
}
|
||||
|
||||
snapshot = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public DirectorySnapshot CreateSnapshot(string dirName, IOPath dirPath)
|
||||
{
|
||||
var snapshot = DirectorySnapshot.Create(dirName, dirPath);
|
||||
if (!Snapshots.TryAdd(snapshot.Name, new List<DirectorySnapshot> { snapshot }))
|
||||
Snapshots[snapshot.Name].Add(snapshot);
|
||||
|
||||
// saving to file
|
||||
string fileName = $"{Path.ReplaceRestrictedChars(dirName)}-{snapshot.SnapshotTimeUTC.ToString(MyTimeFormat.ForFileNames)}.dtsod";
|
||||
IOPath filePath = Path.Concat(SnapshotsDir, dirName, fileName);
|
||||
var dtsod = snapshot.ToDtsod();
|
||||
var text = dtsod.ToString();
|
||||
File.WriteAllText(filePath, text);
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user