mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-07-08 18:16:30 +00:00
more fluent interface for options
This commit is contained in:
85
docs/API.md
85
docs/API.md
@@ -20,14 +20,18 @@ using (var archive = RarArchive.OpenArchive("file.rar"))
|
||||
using (var archive = SevenZipArchive.OpenArchive("file.7z"))
|
||||
using (var archive = GZipArchive.OpenArchive("file.gz"))
|
||||
|
||||
// With options
|
||||
var options = new ReaderOptions
|
||||
// With fluent options (preferred)
|
||||
var options = ReaderOptions.ForEncryptedArchive("password")
|
||||
.WithArchiveEncoding(new ArchiveEncoding { Default = Encoding.GetEncoding(932) });
|
||||
using (var archive = ZipArchive.OpenArchive("encrypted.zip", options))
|
||||
|
||||
// Alternative: object initializer
|
||||
var options2 = new ReaderOptions
|
||||
{
|
||||
Password = "password",
|
||||
LeaveStreamOpen = true,
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
|
||||
};
|
||||
using (var archive = ZipArchive.OpenArchive("encrypted.zip", options))
|
||||
```
|
||||
|
||||
### Creating Archives
|
||||
@@ -44,16 +48,21 @@ using (var archive = ZipArchive.CreateArchive())
|
||||
using (var archive = TarArchive.CreateArchive())
|
||||
using (var archive = GZipArchive.CreateArchive())
|
||||
|
||||
// With options
|
||||
var options = new WriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
CompressionLevel = 9,
|
||||
LeaveStreamOpen = false
|
||||
};
|
||||
// With fluent options (preferred)
|
||||
var options = WriterOptions.ForZip()
|
||||
.WithCompressionLevel(9)
|
||||
.WithLeaveStreamOpen(false);
|
||||
using (var archive = ZipArchive.CreateArchive())
|
||||
{
|
||||
archive.SaveTo("output.zip", options);
|
||||
}
|
||||
|
||||
// Alternative: constructor with object initializer
|
||||
var options2 = new WriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
CompressionLevel = 9,
|
||||
LeaveStreamOpen = false
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
@@ -222,33 +231,65 @@ using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, Compressio
|
||||
|
||||
### ReaderOptions
|
||||
|
||||
Use factory presets and fluent helpers for common configurations:
|
||||
|
||||
```csharp
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
Password = "password", // For encrypted archives
|
||||
LeaveStreamOpen = true, // Don't close wrapped stream
|
||||
ArchiveEncoding = new ArchiveEncoding // Custom character encoding
|
||||
{
|
||||
Default = Encoding.GetEncoding(932)
|
||||
}
|
||||
};
|
||||
// External stream with password and custom encoding
|
||||
var options = ReaderOptions.ForExternalStream()
|
||||
.WithPassword("password")
|
||||
.WithArchiveEncoding(new ArchiveEncoding { Default = Encoding.GetEncoding(932) });
|
||||
|
||||
using (var archive = ZipArchive.OpenArchive("file.zip", options))
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
// Common presets
|
||||
var safeOptions = ReaderOptions.SafeExtract; // No overwrite
|
||||
var flatOptions = ReaderOptions.FlatExtract; // No directory structure
|
||||
|
||||
// Factory defaults:
|
||||
// - file path / FileInfo overloads use LeaveStreamOpen = false
|
||||
// - stream overloads use LeaveStreamOpen = true
|
||||
```
|
||||
|
||||
Alternative: traditional object initializer:
|
||||
|
||||
```csharp
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
Password = "password",
|
||||
LeaveStreamOpen = true,
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
|
||||
};
|
||||
```
|
||||
|
||||
### WriterOptions
|
||||
|
||||
Factory methods provide a clean, discoverable way to create writer options:
|
||||
|
||||
```csharp
|
||||
// Factory methods for common archive types
|
||||
var zipOptions = WriterOptions.ForZip() // ZIP with Deflate
|
||||
.WithCompressionLevel(9) // 0-9 for Deflate
|
||||
.WithLeaveStreamOpen(false); // Close stream when done
|
||||
|
||||
var tarOptions = WriterOptions.ForTar(CompressionType.GZip) // TAR with GZip
|
||||
.WithLeaveStreamOpen(false);
|
||||
|
||||
var gzipOptions = WriterOptions.ForGZip() // GZip file
|
||||
.WithCompressionLevel(6);
|
||||
|
||||
archive.SaveTo("output.zip", zipOptions);
|
||||
```
|
||||
|
||||
Alternative: traditional constructor with object initializer:
|
||||
|
||||
```csharp
|
||||
var options = new WriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
CompressionLevel = 9, // 0-9 for Deflate
|
||||
LeaveStreamOpen = true, // Don't close stream
|
||||
CompressionLevel = 9,
|
||||
LeaveStreamOpen = true,
|
||||
};
|
||||
archive.SaveTo("output.zip", options);
|
||||
```
|
||||
@@ -326,7 +367,7 @@ ArchiveType.ZStandard
|
||||
try
|
||||
{
|
||||
using (var archive = ZipArchive.Open("archive.zip",
|
||||
new ReaderOptions { Password = "password" }))
|
||||
ReaderOptions.ForEncryptedArchive("password")))
|
||||
{
|
||||
archive.WriteToDirectory(@"C:\output");
|
||||
}
|
||||
@@ -353,7 +394,7 @@ var progress = new Progress<ProgressReport>(report =>
|
||||
Console.WriteLine($"Extracting {report.EntryPath}: {report.PercentComplete}%");
|
||||
});
|
||||
|
||||
var options = new ReaderOptions { Progress = progress };
|
||||
var options = ReaderOptions.ForOwnedFile().WithProgress(progress);
|
||||
using (var archive = ZipArchive.OpenArchive("archive.zip", options))
|
||||
{
|
||||
archive.WriteToDirectory(@"C:\output");
|
||||
|
||||
@@ -18,14 +18,9 @@ Most archive formats store filenames and metadata as bytes. SharpCompress must c
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
// Configure encoding before opening archive
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
ArchiveEncoding = new ArchiveEncoding
|
||||
{
|
||||
Default = Encoding.GetEncoding(932) // cp932 for Japanese
|
||||
}
|
||||
};
|
||||
// Configure encoding using fluent factory method (preferred)
|
||||
var options = ReaderOptions.ForEncoding(
|
||||
new ArchiveEncoding { Default = Encoding.GetEncoding(932) }); // cp932 for Japanese
|
||||
|
||||
using (var archive = ZipArchive.OpenArchive("japanese.zip", options))
|
||||
{
|
||||
@@ -34,6 +29,12 @@ using (var archive = ZipArchive.OpenArchive("japanese.zip", options))
|
||||
Console.WriteLine(entry.Key); // Now shows correct characters
|
||||
}
|
||||
}
|
||||
|
||||
// Alternative: object initializer
|
||||
var options2 = new ReaderOptions
|
||||
{
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
|
||||
};
|
||||
```
|
||||
|
||||
### ArchiveEncoding Properties
|
||||
@@ -47,10 +48,8 @@ using (var archive = ZipArchive.OpenArchive("japanese.zip", options))
|
||||
|
||||
**Archive API:**
|
||||
```csharp
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
|
||||
};
|
||||
var options = ReaderOptions.ForEncoding(
|
||||
new ArchiveEncoding { Default = Encoding.GetEncoding(932) });
|
||||
using (var archive = ZipArchive.OpenArchive("file.zip", options))
|
||||
{
|
||||
// Use archive with correct encoding
|
||||
@@ -59,10 +58,8 @@ using (var archive = ZipArchive.OpenArchive("file.zip", options))
|
||||
|
||||
**Reader API:**
|
||||
```csharp
|
||||
var options = new ReaderOptions
|
||||
{
|
||||
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
|
||||
};
|
||||
var options = ReaderOptions.ForEncoding(
|
||||
new ArchiveEncoding { Default = Encoding.GetEncoding(932) });
|
||||
using (var stream = File.OpenRead("file.zip"))
|
||||
using (var reader = ReaderFactory.OpenReader(stream, options))
|
||||
{
|
||||
|
||||
@@ -94,11 +94,11 @@ Note: Extracting a solid rar or 7z file needs to be done in sequential order to
|
||||
`ExtractAllEntries` is primarily intended for solid archives (like solid Rar) or 7Zip archives, where sequential extraction provides the best performance. For general/simple extraction with any supported archive type, use `archive.WriteToDirectory()` instead.
|
||||
|
||||
```C#
|
||||
using (var archive = RarArchive.OpenArchive("Test.rar", new ReaderOptions
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
}))
|
||||
// Using fluent factory method for extraction options
|
||||
using (var archive = RarArchive.OpenArchive("Test.rar",
|
||||
ReaderOptions.ForOwnedFile()
|
||||
.WithExtractFullPath(true)
|
||||
.WithOverwrite(true)))
|
||||
{
|
||||
// Simple extraction with RarArchive; this WriteToDirectory pattern works for all archive types
|
||||
archive.WriteToDirectory(@"D:\temp");
|
||||
@@ -130,12 +130,11 @@ var progress = new Progress<ProgressReport>(report =>
|
||||
Console.WriteLine($"Extracting {report.EntryPath}: {report.PercentComplete}%");
|
||||
});
|
||||
|
||||
using (var archive = RarArchive.OpenArchive("archive.rar", new ReaderOptions
|
||||
{
|
||||
Progress = progress,
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
})) // Must be solid Rar or 7Zip
|
||||
using (var archive = RarArchive.OpenArchive("archive.rar",
|
||||
ReaderOptions.ForOwnedFile()
|
||||
.WithProgress(progress)
|
||||
.WithExtractFullPath(true)
|
||||
.WithOverwrite(true))) // Must be solid Rar or 7Zip
|
||||
{
|
||||
archive.WriteToDirectory(@"D:\output");
|
||||
}
|
||||
@@ -181,10 +180,10 @@ using (var reader = ReaderFactory.OpenReader(stream))
|
||||
|
||||
```C#
|
||||
using (Stream stream = File.OpenWrite("C:\\temp.tgz"))
|
||||
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)
|
||||
{
|
||||
LeaveStreamOpen = true
|
||||
}))
|
||||
using (var writer = WriterFactory.OpenWriter(
|
||||
stream,
|
||||
ArchiveType.Tar,
|
||||
WriterOptions.ForTar(CompressionType.GZip).WithLeaveStreamOpen(true)))
|
||||
{
|
||||
writer.WriteAll("D:\\temp", "*", SearchOption.AllDirectories);
|
||||
}
|
||||
@@ -193,15 +192,15 @@ using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, new Writer
|
||||
### Extract zip which has non-utf8 encoded filename(cp932)
|
||||
|
||||
```C#
|
||||
var opts = new SharpCompress.Readers.ReaderOptions();
|
||||
var encoding = Encoding.GetEncoding(932);
|
||||
opts.ArchiveEncoding = new SharpCompress.Common.ArchiveEncoding();
|
||||
opts.ArchiveEncoding.CustomDecoder = (data, x, y) =>
|
||||
{
|
||||
return encoding.GetString(data);
|
||||
};
|
||||
var tr = SharpCompress.Archives.Zip.ZipArchive.OpenArchive("test.zip", opts);
|
||||
foreach(var entry in tr.Entries)
|
||||
var opts = new ReaderOptions()
|
||||
.WithArchiveEncoding(new ArchiveEncoding
|
||||
{
|
||||
CustomDecoder = (data, x, y) => encoding.GetString(data)
|
||||
});
|
||||
|
||||
using var archive = ZipArchive.OpenArchive("test.zip", opts);
|
||||
foreach(var entry in archive.Entries)
|
||||
{
|
||||
Console.WriteLine($"{entry.Key}");
|
||||
}
|
||||
|
||||
@@ -8,10 +8,15 @@ namespace SharpCompress.Readers;
|
||||
/// Options for configuring reader behavior when opening archives.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
|
||||
/// This class is immutable. Use factory presets and fluent helpers for common configurations:
|
||||
/// <code>
|
||||
/// var options = new ReaderOptions { Password = "secret" };
|
||||
/// options = options with { LeaveStreamOpen = false };
|
||||
/// var options = ReaderOptions.ForExternalStream()
|
||||
/// .WithPassword("secret")
|
||||
/// .WithLookForHeader(true);
|
||||
/// </code>
|
||||
/// Or use object initializers for simple cases:
|
||||
/// <code>
|
||||
/// var options = new ReaderOptions { Password = "secret", LeaveStreamOpen = false };
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public sealed record ReaderOptions : IReaderOptions
|
||||
@@ -169,6 +174,29 @@ public sealed record ReaderOptions : IReaderOptions
|
||||
/// </summary>
|
||||
public static ReaderOptions FlatExtract => new() { ExtractFullPath = false, Overwrite = true };
|
||||
|
||||
/// <summary>
|
||||
/// Creates ReaderOptions for reading encrypted archives.
|
||||
/// </summary>
|
||||
/// <param name="password">The password for encrypted archives.</param>
|
||||
public static ReaderOptions ForEncryptedArchive(string? password = null) =>
|
||||
new ReaderOptions().WithPassword(password);
|
||||
|
||||
/// <summary>
|
||||
/// Creates ReaderOptions for archives with custom character encoding.
|
||||
/// </summary>
|
||||
/// <param name="encoding">The encoding for archive entry names.</param>
|
||||
public static ReaderOptions ForEncoding(IArchiveEncoding encoding) =>
|
||||
new ReaderOptions().WithArchiveEncoding(encoding);
|
||||
|
||||
/// <summary>
|
||||
/// Creates ReaderOptions for self-extracting archives that require header search.
|
||||
/// </summary>
|
||||
public static ReaderOptions ForSelfExtractingArchive(string? password = null) =>
|
||||
new ReaderOptions()
|
||||
.WithLookForHeader(true)
|
||||
.WithPassword(password)
|
||||
.WithRewindableBufferSize(1_048_576); // 1MB for SFX archives
|
||||
|
||||
/// <summary>
|
||||
/// Default symbolic link handler that logs a warning message.
|
||||
/// </summary>
|
||||
@@ -179,66 +207,9 @@ public sealed record ReaderOptions : IReaderOptions
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ReaderOptions instance with the specified password.
|
||||
/// </summary>
|
||||
/// <param name="password">The password for encrypted archives.</param>
|
||||
public ReaderOptions(string? password) => Password = password;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ReaderOptions instance with the specified password and header search option.
|
||||
/// </summary>
|
||||
/// <param name="password">The password for encrypted archives.</param>
|
||||
/// <param name="lookForHeader">Whether to search for the archive header.</param>
|
||||
public ReaderOptions(string? password, bool lookForHeader)
|
||||
{
|
||||
Password = password;
|
||||
LookForHeader = lookForHeader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ReaderOptions instance with the specified encoding.
|
||||
/// </summary>
|
||||
/// <param name="encoding">The encoding for archive entry names.</param>
|
||||
public ReaderOptions(IArchiveEncoding encoding) => ArchiveEncoding = encoding;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ReaderOptions instance with the specified password and encoding.
|
||||
/// </summary>
|
||||
/// <param name="password">The password for encrypted archives.</param>
|
||||
/// <param name="encoding">The encoding for archive entry names.</param>
|
||||
public ReaderOptions(string? password, IArchiveEncoding encoding)
|
||||
{
|
||||
Password = password;
|
||||
ArchiveEncoding = encoding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ReaderOptions instance with the specified stream open behavior.
|
||||
/// </summary>
|
||||
/// <param name="leaveStreamOpen">Whether to leave the stream open after reading.</param>
|
||||
public ReaderOptions(bool leaveStreamOpen)
|
||||
{
|
||||
LeaveStreamOpen = leaveStreamOpen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ReaderOptions instance with the specified stream open behavior and password.
|
||||
/// </summary>
|
||||
/// <param name="leaveStreamOpen">Whether to leave the stream open after reading.</param>
|
||||
/// <param name="password">The password for encrypted archives.</param>
|
||||
public ReaderOptions(bool leaveStreamOpen, string? password)
|
||||
{
|
||||
LeaveStreamOpen = leaveStreamOpen;
|
||||
Password = password;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ReaderOptions instance with the specified buffer size.
|
||||
/// </summary>
|
||||
/// <param name="bufferSize">The buffer size for stream operations.</param>
|
||||
public ReaderOptions(int bufferSize)
|
||||
{
|
||||
BufferSize = bufferSize;
|
||||
}
|
||||
// Note: Parameterized constructors have been removed.
|
||||
// Use fluent With*() helpers or object initializers instead:
|
||||
// new ReaderOptions().WithPassword("secret").WithLookForHeader(true)
|
||||
// or
|
||||
// new ReaderOptions { Password = "secret", LookForHeader = true }
|
||||
}
|
||||
|
||||
127
src/SharpCompress/Readers/ReaderOptionsExtensions.cs
Normal file
127
src/SharpCompress/Readers/ReaderOptionsExtensions.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Options;
|
||||
|
||||
namespace SharpCompress.Readers;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for fluent configuration of reader options.
|
||||
/// </summary>
|
||||
public static class ReaderOptionsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified LeaveStreamOpen value.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithLeaveStreamOpen(
|
||||
this ReaderOptions options,
|
||||
bool leaveStreamOpen
|
||||
) => options with { LeaveStreamOpen = leaveStreamOpen };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified password.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithPassword(this ReaderOptions options, string? password) =>
|
||||
options with
|
||||
{
|
||||
Password = password,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified archive encoding.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithArchiveEncoding(
|
||||
this ReaderOptions options,
|
||||
IArchiveEncoding encoding
|
||||
) => options with { ArchiveEncoding = encoding };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified LookForHeader value.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithLookForHeader(this ReaderOptions options, bool lookForHeader) =>
|
||||
options with
|
||||
{
|
||||
LookForHeader = lookForHeader,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified DisableCheckIncomplete value.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithDisableCheckIncomplete(
|
||||
this ReaderOptions options,
|
||||
bool disableCheckIncomplete
|
||||
) => options with { DisableCheckIncomplete = disableCheckIncomplete };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified buffer size.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithBufferSize(this ReaderOptions options, int bufferSize) =>
|
||||
options with
|
||||
{
|
||||
BufferSize = bufferSize,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified extension hint.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithExtensionHint(
|
||||
this ReaderOptions options,
|
||||
string? extensionHint
|
||||
) => options with { ExtensionHint = extensionHint };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified progress reporter.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithProgress(
|
||||
this ReaderOptions options,
|
||||
IProgress<ProgressReport>? progress
|
||||
) => options with { Progress = progress };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified rewindable buffer size.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithRewindableBufferSize(
|
||||
this ReaderOptions options,
|
||||
int? rewindableBufferSize
|
||||
) => options with { RewindableBufferSize = rewindableBufferSize };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified overwrite setting.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithOverwrite(this ReaderOptions options, bool overwrite) =>
|
||||
options with
|
||||
{
|
||||
Overwrite = overwrite,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified extract full path setting.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithExtractFullPath(
|
||||
this ReaderOptions options,
|
||||
bool extractFullPath
|
||||
) => options with { ExtractFullPath = extractFullPath };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified preserve file time setting.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithPreserveFileTime(
|
||||
this ReaderOptions options,
|
||||
bool preserveFileTime
|
||||
) => options with { PreserveFileTime = preserveFileTime };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified preserve attributes setting.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithPreserveAttributes(
|
||||
this ReaderOptions options,
|
||||
bool preserveAttributes
|
||||
) => options with { PreserveAttributes = preserveAttributes };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified symbolic link handler.
|
||||
/// </summary>
|
||||
public static ReaderOptions WithSymbolicLinkHandler(
|
||||
this ReaderOptions options,
|
||||
Action<string, string>? handler
|
||||
) => options with { SymbolicLinkHandler = handler };
|
||||
}
|
||||
@@ -10,10 +10,9 @@ namespace SharpCompress.Writers.GZip;
|
||||
/// Options for configuring GZip writer behavior.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
|
||||
/// This class is immutable. Use factory methods for creation:
|
||||
/// <code>
|
||||
/// var options = new GZipWriterOptions { CompressionLevel = 9 };
|
||||
/// options = options with { LeaveStreamOpen = false };
|
||||
/// var options = WriterOptions.ForGZip().WithLeaveStreamOpen(false).WithCompressionLevel(9);
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public sealed record GZipWriterOptions : IWriterOptions
|
||||
@@ -90,14 +89,11 @@ public sealed record GZipWriterOptions : IWriterOptions
|
||||
CompressionLevel = (int)compressionLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GZipWriterOptions instance with the specified stream open behavior.
|
||||
/// </summary>
|
||||
/// <param name="leaveStreamOpen">Whether to leave the stream open after writing.</param>
|
||||
public GZipWriterOptions(bool leaveStreamOpen)
|
||||
{
|
||||
LeaveStreamOpen = leaveStreamOpen;
|
||||
}
|
||||
// Note: Constructor with boolean leaveStreamOpen parameter removed.
|
||||
// Use the fluent WithLeaveStreamOpen() helper or object initializer instead:
|
||||
// new GZipWriterOptions() { LeaveStreamOpen = false }
|
||||
// or
|
||||
// WriterOptions.ForGZip().WithLeaveStreamOpen(false)
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GZipWriterOptions instance from an existing WriterOptions instance.
|
||||
|
||||
@@ -9,10 +9,9 @@ namespace SharpCompress.Writers;
|
||||
/// Options for configuring writer behavior when creating archives.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
|
||||
/// This class is immutable. Use factory methods for creation:
|
||||
/// <code>
|
||||
/// var options = new WriterOptions(CompressionType.Zip);
|
||||
/// options = options with { LeaveStreamOpen = false };
|
||||
/// var options = WriterOptions.ForZip().WithLeaveStreamOpen(false).WithCompressionLevel(9);
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public sealed record WriterOptions : IWriterOptions
|
||||
@@ -92,32 +91,11 @@ public sealed record WriterOptions : IWriterOptions
|
||||
CompressionLevel = compressionLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new WriterOptions instance with the specified compression type and stream open behavior.
|
||||
/// </summary>
|
||||
/// <param name="compressionType">The compression type for the archive.</param>
|
||||
/// <param name="leaveStreamOpen">Whether to leave the stream open after writing.</param>
|
||||
public WriterOptions(CompressionType compressionType, bool leaveStreamOpen)
|
||||
: this(compressionType)
|
||||
{
|
||||
LeaveStreamOpen = leaveStreamOpen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new WriterOptions instance with the specified compression type, level, and stream open behavior.
|
||||
/// </summary>
|
||||
/// <param name="compressionType">The compression type for the archive.</param>
|
||||
/// <param name="compressionLevel">The compression level (algorithm-specific).</param>
|
||||
/// <param name="leaveStreamOpen">Whether to leave the stream open after writing.</param>
|
||||
public WriterOptions(
|
||||
CompressionType compressionType,
|
||||
int compressionLevel,
|
||||
bool leaveStreamOpen
|
||||
)
|
||||
: this(compressionType, compressionLevel)
|
||||
{
|
||||
LeaveStreamOpen = leaveStreamOpen;
|
||||
}
|
||||
// Note: Constructors with boolean leaveStreamOpen parameter removed.
|
||||
// Use the fluent WithLeaveStreamOpen() helper or object initializer instead:
|
||||
// new WriterOptions(type) { LeaveStreamOpen = false }
|
||||
// or
|
||||
// WriterOptions.ForZip().WithLeaveStreamOpen(false)
|
||||
|
||||
/// <summary>
|
||||
/// Implicit conversion from CompressionType to WriterOptions.
|
||||
@@ -125,4 +103,23 @@ public sealed record WriterOptions : IWriterOptions
|
||||
/// <param name="compressionType">The compression type.</param>
|
||||
public static implicit operator WriterOptions(CompressionType compressionType) =>
|
||||
new(compressionType);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new ZipWriterOptions for writing ZIP archives.
|
||||
/// </summary>
|
||||
/// <param name="compressionType">The compression type for the archive. Defaults to Deflate.</param>
|
||||
public static WriterOptions ForZip(CompressionType compressionType = CompressionType.Deflate) =>
|
||||
new(compressionType);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new WriterOptions for writing TAR archives.
|
||||
/// </summary>
|
||||
/// <param name="compressionType">The compression type for the archive. Defaults to None.</param>
|
||||
public static WriterOptions ForTar(CompressionType compressionType = CompressionType.None) =>
|
||||
new(compressionType);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new WriterOptions for writing GZip compressed files.
|
||||
/// </summary>
|
||||
public static WriterOptions ForGZip() => new(CompressionType.GZip);
|
||||
}
|
||||
|
||||
55
src/SharpCompress/Writers/WriterOptionsExtensions.cs
Normal file
55
src/SharpCompress/Writers/WriterOptionsExtensions.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Options;
|
||||
|
||||
namespace SharpCompress.Writers;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for fluent configuration of writer options.
|
||||
/// </summary>
|
||||
public static class WriterOptionsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified LeaveStreamOpen value.
|
||||
/// </summary>
|
||||
/// <param name="options">The source options.</param>
|
||||
/// <param name="leaveStreamOpen">Whether to leave the stream open.</param>
|
||||
/// <returns>A new options instance with the specified LeaveStreamOpen value.</returns>
|
||||
public static WriterOptions WithLeaveStreamOpen(
|
||||
this WriterOptions options,
|
||||
bool leaveStreamOpen
|
||||
) => options with { LeaveStreamOpen = leaveStreamOpen };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified compression level.
|
||||
/// </summary>
|
||||
/// <param name="options">The source options.</param>
|
||||
/// <param name="compressionLevel">The compression level (algorithm-specific).</param>
|
||||
/// <returns>A new options instance with the specified compression level.</returns>
|
||||
public static WriterOptions WithCompressionLevel(
|
||||
this WriterOptions options,
|
||||
int compressionLevel
|
||||
) => options with { CompressionLevel = compressionLevel };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified archive encoding.
|
||||
/// </summary>
|
||||
/// <param name="options">The source options.</param>
|
||||
/// <param name="archiveEncoding">The archive encoding to use.</param>
|
||||
/// <returns>A new options instance with the specified archive encoding.</returns>
|
||||
public static WriterOptions WithArchiveEncoding(
|
||||
this WriterOptions options,
|
||||
IArchiveEncoding archiveEncoding
|
||||
) => options with { ArchiveEncoding = archiveEncoding };
|
||||
|
||||
/// <summary>
|
||||
/// Creates a copy with the specified progress reporter.
|
||||
/// </summary>
|
||||
/// <param name="options">The source options.</param>
|
||||
/// <param name="progress">The progress reporter.</param>
|
||||
/// <returns>A new options instance with the specified progress reporter.</returns>
|
||||
public static WriterOptions WithProgress(
|
||||
this WriterOptions options,
|
||||
IProgress<ProgressReport> progress
|
||||
) => options with { Progress = progress };
|
||||
}
|
||||
@@ -390,7 +390,7 @@ public class ArchiveTests : ReaderTests
|
||||
)
|
||||
{
|
||||
var writerOptions = compressionLevel.HasValue
|
||||
? new WriterOptions(compressionType, compressionLevel.Value, leaveStreamOpen: true)
|
||||
? new WriterOptions(compressionType, compressionLevel.Value) { LeaveStreamOpen = true }
|
||||
: new WriterOptions(compressionType) { LeaveStreamOpen = true };
|
||||
return WriterFactory.OpenAsyncWriter(
|
||||
new AsyncOnlyStream(stream),
|
||||
|
||||
@@ -108,4 +108,141 @@ public class OptionsUsabilityTests : TestBase
|
||||
writer.Write("entry.bin", source, options)
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriterOptions_Factory_Methods_Create_Valid_Options()
|
||||
{
|
||||
// ForZip
|
||||
var zipOptions = WriterOptions.ForZip();
|
||||
Assert.Equal(CompressionType.Deflate, zipOptions.CompressionType);
|
||||
Assert.True(zipOptions.LeaveStreamOpen);
|
||||
|
||||
// ForTar
|
||||
var tarOptions = WriterOptions.ForTar();
|
||||
Assert.Equal(CompressionType.None, tarOptions.CompressionType);
|
||||
|
||||
// ForGZip
|
||||
var gzipOptions = WriterOptions.ForGZip();
|
||||
Assert.Equal(CompressionType.GZip, gzipOptions.CompressionType);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriterOptions_Fluent_Methods_Modify_Correctly()
|
||||
{
|
||||
var options = WriterOptions.ForZip().WithLeaveStreamOpen(false).WithCompressionLevel(9);
|
||||
|
||||
Assert.Equal(CompressionType.Deflate, options.CompressionType);
|
||||
Assert.Equal(9, options.CompressionLevel);
|
||||
Assert.False(options.LeaveStreamOpen);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WriterOptions_Factory_And_Fluent_Equivalent_To_Constructor()
|
||||
{
|
||||
// Factory + fluent approach
|
||||
var factoryApproach = WriterOptions
|
||||
.ForZip()
|
||||
.WithLeaveStreamOpen(false)
|
||||
.WithCompressionLevel(9);
|
||||
|
||||
// Traditional constructor approach
|
||||
var constructorApproach = new WriterOptions(CompressionType.Deflate)
|
||||
{
|
||||
CompressionLevel = 9,
|
||||
LeaveStreamOpen = false,
|
||||
};
|
||||
|
||||
Assert.Equal(factoryApproach.CompressionType, constructorApproach.CompressionType);
|
||||
Assert.Equal(factoryApproach.CompressionLevel, constructorApproach.CompressionLevel);
|
||||
Assert.Equal(factoryApproach.LeaveStreamOpen, constructorApproach.LeaveStreamOpen);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderOptions_Fluent_Methods_Modify_Correctly()
|
||||
{
|
||||
var options = new ReaderOptions()
|
||||
.WithLeaveStreamOpen(false)
|
||||
.WithPassword("secret")
|
||||
.WithLookForHeader(true)
|
||||
.WithBufferSize(65536);
|
||||
|
||||
Assert.False(options.LeaveStreamOpen);
|
||||
Assert.Equal("secret", options.Password);
|
||||
Assert.True(options.LookForHeader);
|
||||
Assert.Equal(65536, options.BufferSize);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderOptions_Fluent_And_Initializer_Equivalent()
|
||||
{
|
||||
// Fluent approach
|
||||
var fluentApproach = new ReaderOptions()
|
||||
.WithLeaveStreamOpen(false)
|
||||
.WithPassword("secret")
|
||||
.WithLookForHeader(true)
|
||||
.WithOverwrite(false);
|
||||
|
||||
// Object initializer approach
|
||||
var initializerApproach = new ReaderOptions
|
||||
{
|
||||
LeaveStreamOpen = false,
|
||||
Password = "secret",
|
||||
LookForHeader = true,
|
||||
Overwrite = false,
|
||||
};
|
||||
|
||||
Assert.Equal(fluentApproach.LeaveStreamOpen, initializerApproach.LeaveStreamOpen);
|
||||
Assert.Equal(fluentApproach.Password, initializerApproach.Password);
|
||||
Assert.Equal(fluentApproach.LookForHeader, initializerApproach.LookForHeader);
|
||||
Assert.Equal(fluentApproach.Overwrite, initializerApproach.Overwrite);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderOptions_Presets_Have_Correct_Defaults()
|
||||
{
|
||||
var external = ReaderOptions.ForExternalStream;
|
||||
Assert.True(external.LeaveStreamOpen);
|
||||
|
||||
var owned = ReaderOptions.ForOwnedFile;
|
||||
Assert.False(owned.LeaveStreamOpen);
|
||||
|
||||
var safe = ReaderOptions.SafeExtract;
|
||||
Assert.False(safe.Overwrite);
|
||||
|
||||
var flat = ReaderOptions.FlatExtract;
|
||||
Assert.False(flat.ExtractFullPath);
|
||||
Assert.True(flat.Overwrite);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderOptions_Factory_ForEncryptedArchive_Sets_Password()
|
||||
{
|
||||
var options = ReaderOptions.ForEncryptedArchive("myPassword");
|
||||
Assert.Equal("myPassword", options.Password);
|
||||
|
||||
var noPassword = ReaderOptions.ForEncryptedArchive();
|
||||
Assert.Null(noPassword.Password);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderOptions_Factory_ForEncoding_Sets_Encoding()
|
||||
{
|
||||
var encoding = new ArchiveEncoding { Default = System.Text.Encoding.UTF8 };
|
||||
var options = ReaderOptions.ForEncoding(encoding);
|
||||
Assert.Equal(encoding, options.ArchiveEncoding);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReaderOptions_Factory_ForSelfExtractingArchive_Configures_Correctly()
|
||||
{
|
||||
var options = ReaderOptions.ForSelfExtractingArchive("password");
|
||||
Assert.True(options.LookForHeader);
|
||||
Assert.Equal("password", options.Password);
|
||||
Assert.Equal(1_048_576, options.RewindableBufferSize);
|
||||
|
||||
var noPassword = ReaderOptions.ForSelfExtractingArchive();
|
||||
Assert.True(noPassword.LookForHeader);
|
||||
Assert.Null(noPassword.Password);
|
||||
Assert.Equal(1_048_576, noPassword.RewindableBufferSize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,12 @@ public class ZipReaderTests : ReaderTests
|
||||
using var stream = new TestStream(
|
||||
File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip"))
|
||||
);
|
||||
using (var reader = ReaderFactory.OpenReader(stream))
|
||||
using (
|
||||
var reader = ReaderFactory.OpenReader(
|
||||
stream,
|
||||
new ReaderOptions().WithLeaveStreamOpen(false)
|
||||
)
|
||||
)
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user