Skip to content

Commit

Permalink
Merge pull request #56 from Nexus-Mods/nx-extra-extensions
Browse files Browse the repository at this point in the history
Added: Extensions for Remaining High Level Nx APIs
  • Loading branch information
Sewer56 authored Jul 30, 2024
2 parents 3f26d06 + b2f97fe commit 6c7b575
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using JetBrains.Annotations;
using NexusMods.Archives.Nx.Enums;
using NexusMods.Archives.Nx.Packing;
using NexusMods.Archives.Nx.Structs;
using NexusMods.Paths.Extensions.Nx.FileProviders;
namespace NexusMods.Paths.Extensions.Nx.Extensions;

/// <summary>
/// Extension methods for NxPackerBuilder to integrate AbsolutePath-based APIs.
/// </summary>
[PublicAPI]
public static class NxPackerBuilderExtensions
{
/// <summary>
/// Adds a file to be packed using an <see cref="AbsolutePath"/>.
/// </summary>
/// <param name="builder">The <see cref="NxPackerBuilder"/> instance.</param>
/// <param name="absolutePath">The <see cref="AbsolutePath"/> of the file to add.</param>
/// <param name="options">The options for adding the file.</param>
/// <returns>The builder instance for method chaining.</returns>
public static NxPackerBuilder AddFile(this NxPackerBuilder builder, AbsolutePath absolutePath, AddFileParams options)
{
var packerFile = new PackerFile
{
FileDataProvider = new FromAbsolutePathProvider
{
FilePath = absolutePath
},
RelativePath = options.RelativePath,
FileSize = (long)absolutePath.FileInfo.Size.Value,
SolidType = options.SolidType,
CompressionPreference = options.CompressionPreference
};

return builder.AddPackerFile(packerFile);
}

/// <summary>
/// Sets the output (archive) to an <see cref="AbsolutePath"/>.
/// </summary>
/// <param name="builder">The <see cref="NxPackerBuilder"/> instance.</param>
/// <param name="outputPath">The <see cref="AbsolutePath"/> where the packed archive will be saved.</param>
/// <returns>The builder instance for method chaining.</returns>
public static NxPackerBuilder WithOutput(this NxPackerBuilder builder, AbsolutePath outputPath)
{
var stream = outputPath.FileSystem.CreateFile(outputPath);
return builder.WithOutput(stream);
}

/// <summary>
/// Adds all files under a given folder to the output using <see cref="AbsolutePath"/>.
/// </summary>
/// <param name="builder">The <see cref="NxPackerBuilder"/> instance.</param>
/// <param name="folderPath">The <see cref="AbsolutePath"/> of the folder to add items from.</param>
/// <param name="solidType">The solid type preference for the files.</param>
/// <param name="compressionPreference">The compression preference for the files.</param>
/// <returns>The builder instance for method chaining.</returns>
public static NxPackerBuilder AddFolder(this NxPackerBuilder builder,
AbsolutePath folderPath,
SolidPreference solidType = SolidPreference.Default,
CompressionPreference compressionPreference = CompressionPreference.NoPreference)
{
foreach (var file in folderPath.EnumerateFiles())
{
var options = new AddFileParams
{
RelativePath = file.RelativeTo(folderPath).ToString(),
SolidType = solidType,
CompressionPreference = compressionPreference
};

builder.AddFile(file, options);
}
return builder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using JetBrains.Annotations;
using NexusMods.Archives.Nx.Packing;
using NexusMods.Archives.Nx.Packing.Unpack;
using NexusMods.Paths.Extensions.Nx.FileProviders;
namespace NexusMods.Paths.Extensions.Nx.Extensions;

/// <summary>
/// Extension methods for <see cref="NxUnpackerBuilder"/> to integrate <see cref="AbsolutePath"/>-based APIs.
/// </summary>
[PublicAPI]
public static class NxUnpackerBuilderExtensions
{
/// <summary>
/// Creates an <see cref="NxUnpackerBuilder"/> instance using an AbsolutePath.
/// </summary>
/// <param name="archivePath">The <see cref="AbsolutePath"/> of the .nx archive file.</param>
/// <param name="hasLotsOfFiles">Hint whether the archive contains lots of files (100+).</param>
/// <returns>A new instance of <see cref="NxUnpackerBuilder"/>.</returns>
public static NxUnpackerBuilder FromFile(AbsolutePath archivePath, bool hasLotsOfFiles = false)
{
return new NxUnpackerBuilder(new FromAbsolutePathProvider
{
FilePath = archivePath
}, hasLotsOfFiles);
}

/// <summary>
/// Extracts files to a specified directory using <see cref="AbsolutePath"/>.
/// </summary>
/// <param name="builder">The <see cref="NxUnpackerBuilder"/> instance.</param>
/// <param name="outputDirectory">The <see cref="AbsolutePath"/> of the directory to extract files to.</param>
/// <param name="entries">The file entries to extract.</param>
/// <returns>The builder instance for method chaining.</returns>
public static NxUnpackerBuilder AddFilesWithFileSystemOutput(this NxUnpackerBuilder builder, AbsolutePath outputDirectory, PathedFileEntry[] entries)
{
var outputProviders = new OutputAbsolutePathProvider[entries.Length];
for (var x = 0; x < entries.Length; x++)
{
var entry = entries[x];
var outputPath = outputDirectory.Combine(entry.FilePath);
outputProviders[x] = new OutputAbsolutePathProvider(outputPath, entry.FilePath, entry.Entry);
}
builder.Outputs.AddRange(outputProviders);
return builder;
}

/// <summary>
/// Extracts all files to a specified directory using AbsolutePath.
/// </summary>
/// <param name="builder">The <see cref="NxUnpackerBuilder"/> instance.</param>
/// <param name="outputDirectory">The <see cref="AbsolutePath"/> of the directory to extract files to.</param>
/// <returns>The builder instance for method chaining.</returns>
public static NxUnpackerBuilder AddAllFilesWithFileSystemOutput(this NxUnpackerBuilder builder, AbsolutePath outputDirectory)
{
return builder.AddFilesWithFileSystemOutput(outputDirectory, builder.GetPathedFileEntries());
}

/// <summary>
/// Extracts a single file to a specified path using <see cref="AbsolutePath"/>.
/// </summary>
/// <param name="builder">The <see cref="NxUnpackerBuilder"/> instance.</param>
/// <param name="entry">The file entry to extract.</param>
/// <param name="outputPath">The <see cref="AbsolutePath"/> where the file should be extracted.</param>
/// <returns>The builder instance for method chaining.</returns>
public static NxUnpackerBuilder AddFileWithFileSystemOutput(this NxUnpackerBuilder builder, PathedFileEntry entry, AbsolutePath outputPath)
{
// Output provider disposed during extract.
builder.Outputs.Add(new OutputAbsolutePathProvider(outputPath, entry.FilePath, entry.Entry));
return builder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using FluentAssertions;
using NexusMods.Archives.Nx.Packing;
using NexusMods.Paths.Extensions.Nx.Extensions;
using NexusMods.Paths.TestingHelpers;
using Xunit;
namespace NexusMods.Paths.Extensions.Nx.Tests.Extensions;

/// <summary>
/// These tests are more of a 'sanity' check to ensure that our extensions
/// integrate correctly with Nx. They don't have much substance, other than
/// confirming that stuff 'just works'.
/// </summary>
public class NxBuilderExtensionsTests
{
[Theory, AutoFileSystem]
public async Task NxPackerBuilder_CanAddFolderFromIFileSystem_AndExtractToIFileSystem(InMemoryFileSystem fs, AbsolutePath folderPath)
{
// Arrange
var file1 = folderPath.Combine("file1.txt");
var file2 = folderPath.Combine("subfolder/file2.txt");
await fs.WriteAllTextAsync(file1, "Content 1");
await fs.WriteAllTextAsync(file2, "Content 2");

var builder = new NxPackerBuilder();
var outputPath = folderPath.Parent.Combine("output.nx");

// Act
builder.AddFolder(folderPath)
.WithOutput(outputPath)
.Build();

// Assert
fs.FileExists(outputPath).Should().BeTrue();
var unpacker = NxUnpackerBuilderExtensions.FromFile(outputPath);
var entries = unpacker.GetPathedFileEntries();
entries.Should().HaveCount(2);
entries.Should().Contain(e => e.FilePath == "file1.txt");
entries.Should().Contain(e => e.FilePath == "subfolder/file2.txt");

// Verify we can extract all files
var extractFolder = folderPath.Parent.Combine("extracted");
unpacker.AddAllFilesWithFileSystemOutput(extractFolder).Extract();

var extractedFile1 = extractFolder.Combine("file1.txt");
var extractedFile2 = extractFolder.Combine("subfolder/file2.txt");

fs.FileExists(extractedFile1).Should().BeTrue();
fs.FileExists(extractedFile2).Should().BeTrue();

(await fs.ReadAllTextAsync(extractedFile1)).Should().Be("Content 1");
(await fs.ReadAllTextAsync(extractedFile2)).Should().Be("Content 2");

// Verify we can extract a single file.
unpacker = NxUnpackerBuilderExtensions.FromFile(outputPath);
var extractedFile1Copy = extractFolder.Combine("file1-copy.txt");
var file1Entry = entries.First(x => x.FilePath == "file1.txt");
unpacker.AddFileWithFileSystemOutput(file1Entry, extractedFile1Copy).Extract();
(await fs.ReadAllTextAsync(extractedFile1Copy)).Should().Be("Content 1");
}
}

0 comments on commit 6c7b575

Please sign in to comment.