Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Apply Diff View #1202

Merged
merged 20 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,29 @@ public IEnumerable<KeyedBox<RelativePath, GamePathNode<TValue>>> GetAllDescenden
return _trees.Values.SelectMany(e => e.GetFiles());
}

/// <summary>
/// Enumerates all the directories recursively in this tree.
/// </summary>
public IEnumerable<KeyedBox<RelativePath, GamePathNode<TValue>>> GetAllDescendentDirectories()
{
return _trees.Values.SelectMany(e => e.GetDirectories());
}

/// <summary>
/// Enumerates all the descendants in the tree. (files and directories)
/// </summary>
public IEnumerable<KeyedBox<RelativePath, GamePathNode<TValue>>> GetAllDescendents()
{
return _trees.Values.SelectMany(e => e.GetChildrenRecursive());
}

/// <summary>
/// Enumerates all the the root nodes in the tree (usually correspond to game top level locations)
/// </summary>
public IEnumerable<KeyedBox<RelativePath, GamePathNode<TValue>>> GetRoots()
{
return _trees.Values;
}

/// <summary>
/// Returns all the files in a specific location
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ public async Task<DiskStateTree> FileTreeToDisk(FileTree fileTree, Loadout loado
toWrite.Add(KeyValuePair.Create(entry.Path, gf));
continue;
default:
throw new UnreachableException("No way to handle this file");
_logger.LogError("Unknown file type: {Type}", newEntry.Item.Value!.GetType());
break;
}
}

Expand Down Expand Up @@ -519,6 +520,109 @@ public virtual async Task<Loadout> Ingest(Loadout loadout)

return newLoadout;
}

/// <inheritdoc />
public async ValueTask<FileDiffTree> LoadoutToDiskDiff(Loadout loadout, DiskStateTree diskState)
{
var flattenedLoadout = await LoadoutToFlattenedLoadout(loadout);
return await FlattenedLoadoutToDiskDiff(flattenedLoadout, diskState);
}

private static ValueTask<FileDiffTree> FlattenedLoadoutToDiskDiff(FlattenedLoadout flattenedLoadout, DiskStateTree diskState)
{
var loadoutFiles = flattenedLoadout.GetAllDescendentFiles().ToArray();
var diskStateEntries = diskState.GetAllDescendentFiles().ToArray();

// With both deletions and additions it might be more than Max, but it's a starting point
Dictionary<GamePath, DiskDiffEntry> resultingItems = new(Math.Max(loadoutFiles.Length, diskStateEntries.Length));

// Add all the disk state entries to the result, checking for changes
foreach (var diskItem in diskStateEntries)
{
var gamePath = diskItem.GamePath();
if (flattenedLoadout.TryGetValue(gamePath, out var loadoutFileEntry))
{
switch (loadoutFileEntry.Item.Value.File)
{
case StoredFile sf:
if (sf.Hash != diskItem.Item.Value.Hash)
{
resultingItems.Add(gamePath,
new DiskDiffEntry
{
GamePath = gamePath,
Hash = sf.Hash,
Size = sf.Size,
ChangeType = FileChangeType.Modified,
}
);
}
else
{
resultingItems.Add(gamePath,
new DiskDiffEntry
{
GamePath = gamePath,
Hash = sf.Hash,
Size = sf.Size,
ChangeType = FileChangeType.None,
}
);
}

break;
case IGeneratedFile gf and IToFile:
// TODO: Implement change detection for generated files
break;
default:
throw new UnreachableException("No way to handle this file");
Al12rs marked this conversation as resolved.
Show resolved Hide resolved
}
}
else
{
resultingItems.Add(gamePath,
new DiskDiffEntry
{
GamePath = gamePath,
Hash = diskItem.Item.Value.Hash,
Size = diskItem.Item.Value.Size,
ChangeType = FileChangeType.Removed,
}
);
}
}

// Add all the new files to the result
foreach (var loadoutFile in loadoutFiles)
{
var gamePath = loadoutFile.GamePath();
switch (loadoutFile.Item.Value.File)
{
case StoredFile sf:
if (!resultingItems.TryGetValue(gamePath, out _))
{
resultingItems.Add(gamePath,
new DiskDiffEntry
{
GamePath = gamePath,
Hash = sf.Hash,
Size = sf.Size,
ChangeType = FileChangeType.Added,
}
);
}

break;
case IGeneratedFile gf and IToFile:
// TODO: Implement change detection for generated files
break;
default:
throw new UnreachableException("No way to handle this file");
}
}

return ValueTask.FromResult(FileDiffTree.Create(resultingItems));
}

/// <summary>
/// Backs up any new files in the loadout.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ public interface ILoadoutSynchronizer
/// <returns></returns>
Loadout MergeLoadouts(Loadout loadoutA, Loadout loadoutB);

#endregion

#region Diff Methods

/// <summary>
/// Computes the difference between a loadout and a disk state, assuming the loadout to be the newer state.
/// </summary>
/// <param name="loadout">Newer state, e.g. unapplied loadout</param>
/// <param name="diskState">The old state, e.g. last applied DiskState</param>
/// <returns>A tree of all the files with associated <see cref="FileChangeType"/></returns>
ValueTask<FileDiffTree> LoadoutToDiskDiff(Loadout loadout, DiskStateTree diskState);

#endregion

#region High Level Methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace NexusMods.Abstractions.Loadouts.Synchronizers;
/// <summary>
/// A file diff entry
/// </summary>
public record struct DiskDiffEntry
public record DiskDiffEntry
{
/// <summary>
/// The game path of the file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,13 @@ namespace NexusMods.Abstractions.Loadouts.Synchronizers;
/// <summary>
/// The type of change of a file at a specific path
/// </summary>
public enum FileChangeType
public enum FileChangeType : byte
{
/// <summary>
/// No change
/// </summary>
None,

/// <summary>
/// This file path was added and was not present before
/// </summary>
Added,

/// <summary>
/// This file path was removed and was present before
/// </summary>
Expand All @@ -27,4 +22,9 @@ public enum FileChangeType
/// A renamed file is not considered modified, but rather a Removed and an Added file
/// </remarks>
Modified,

/// <summary>
/// This file path was added and was not present before
/// </summary>
Added,
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NexusMods.Abstractions.GameLocators;
using NexusMods.Abstractions.Loadouts.Synchronizers;
using NexusMods.Abstractions.Serialization.DataModel.Ids;

namespace NexusMods.Abstractions.Loadouts;
Expand All @@ -15,6 +16,11 @@ public interface IApplyService
/// <param name="loadoutId"></param>
/// <returns></returns>
public Task<Loadout> Apply(LoadoutId loadoutId);

/// <summary>
/// Get the diff tree of the unapplied changes of a loadout.
/// </summary>
public ValueTask<FileDiffTree> GetApplyDiffTree(LoadoutId loadoutId);


/// <summary>
Expand Down
Loading
Loading