initial commit

This commit is contained in:
2023-09-28 03:51:05 +06:00
commit 6fc40bb831
15 changed files with 336 additions and 0 deletions

View 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
View 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
View 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)}
};
}

View 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;
}
}