Merge pull request #1330 from adamhathcock/adam/fix-lang-version

Add Polysharp and adjustments that do not break legacy frameworks
This commit is contained in:
Adam Hathcock 2026-05-20 16:26:50 +01:00 committed by GitHub
commit 192a947524
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 266 additions and 96 deletions

View file

@ -204,6 +204,7 @@ SharpCompress supports multiple archive and compression formats:
- Preserve existing public method signatures and behavior when possible.
- If a breaking change is unavoidable, document it and provide a migration path.
- Add or update tests that cover backward compatibility expectations.
- Avoid exposing public `init` setters, positional records, `required` members, or other metadata that forces consumers onto newer C# language versions; validate older-consumer compatibility with tests when changing exported APIs.
### Stream Ownership and Position Checklist
- Verify `LeaveStreamOpen` behavior for externally owned streams.

View file

@ -18,5 +18,6 @@
Include="Microsoft.VisualStudio.Threading.Analyzers"
Version="17.14.15"
/>
<GlobalPackageReference Include="PolySharp" Version="1.15.0" />
</ItemGroup>
</Project>

View file

@ -39,6 +39,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
"PolySharp": {
"type": "Direct",
"requested": "[1.15.0, )",
"resolved": "1.15.0",
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
},
"SimpleExec": {
"type": "Direct",
"requested": "[13.0.0, )",

View file

@ -10,13 +10,29 @@ namespace SharpCompress.Archives;
/// <see cref="ArchiveFactory.GetArchiveInformationAsync(System.IO.Stream,System.Threading.CancellationToken)"/>
/// to obtain an instance of this record.
/// </remarks>
/// <param name="Type">
/// The type of archive detected, or <see langword="null"/> when the format is not a registered well-known type.
/// </param>
/// <param name="SupportsRandomAccess">
/// <see langword="true"/> when this archive format supports random access via the <see cref="IArchive"/> API,
/// meaning the full file listing can be retrieved without decompressing the entire archive.
/// <see langword="false"/> when only the <see cref="SharpCompress.Readers.IReader"/> API is available,
/// which reads entries sequentially and can only report per-entry progress.
/// </param>
public record ArchiveInformation(ArchiveType? Type, bool SupportsRandomAccess);
public record ArchiveInformation
{
/// <summary>
/// The type of archive detected, or <see langword="null"/> when the format is not a registered well-known type.
/// </summary>
public ArchiveType? Type { get; set; }
/// <summary>
/// <see langword="true"/> when this archive format supports random access via the <see cref="IArchive"/> API,
/// meaning the full file listing can be retrieved without decompressing the entire archive.
/// <see langword="false"/> when only the <see cref="SharpCompress.Readers.IReader"/> API is available,
/// which reads entries sequentially and can only report per-entry progress.
/// </summary>
public bool SupportsRandomAccess { get; set; }
/// <summary>
/// Creates a new archive information instance.
/// </summary>
/// <param name="type">The detected archive type.</param>
/// <param name="supportsRandomAccess">Whether the detected format supports random access.</param>
public ArchiveInformation(ArchiveType? type, bool supportsRandomAccess)
{
Type = type;
SupportsRandomAccess = supportsRandomAccess;
}
}

View file

@ -7,7 +7,7 @@ namespace SharpCompress.Common;
/// Options for configuring extraction behavior when extracting archive entries.
/// </summary>
/// <remarks>
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
/// Configure extraction behavior with constructors, property setters, or the <c>with</c> expression:
/// <code>
/// var options = new ExtractionOptions { Overwrite = false };
/// options = options with { PreserveFileTime = true };
@ -19,24 +19,24 @@ public sealed record ExtractionOptions : IExtractionOptions
/// Overwrite target if it exists.
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
/// </summary>
public bool Overwrite { get; init; } = true;
public bool Overwrite { get; set; } = true;
/// <summary>
/// Extract with internal directory structure.
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
/// </summary>
public bool ExtractFullPath { get; init; } = true;
public bool ExtractFullPath { get; set; } = true;
/// <summary>
/// Preserve file time.
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
/// </summary>
public bool PreserveFileTime { get; init; } = true;
public bool PreserveFileTime { get; set; } = true;
/// <summary>
/// Preserve windows file attributes.
/// </summary>
public bool PreserveAttributes { get; init; }
public bool PreserveAttributes { get; set; }
/// <summary>
/// Delegate for writing symbolic links to disk.
@ -44,10 +44,10 @@ public sealed record ExtractionOptions : IExtractionOptions
/// The second parameter is the target path (what the symlink refers to).
/// </summary>
/// <remarks>
/// <b>Breaking change:</b> Changed from field to init-only property in version 0.40.0.
/// <b>Breaking change:</b> Changed from field to property in version 0.40.0.
/// If no handler is provided, symbolic links are silently skipped during extraction.
/// </remarks>
public Action<string, string>? SymbolicLinkHandler { get; init; }
public Action<string, string>? SymbolicLinkHandler { get; set; }
/// <summary>
/// Creates a new ExtractionOptions instance with default values.

View file

@ -2,5 +2,5 @@ namespace SharpCompress.Common.Options;
public interface IEncodingOptions
{
IArchiveEncoding ArchiveEncoding { get; init; }
IArchiveEncoding ArchiveEncoding { get; set; }
}

View file

@ -11,29 +11,29 @@ public interface IExtractionOptions
/// Overwrite target if it exists.
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
/// </summary>
bool Overwrite { get; init; }
bool Overwrite { get; set; }
/// <summary>
/// Extract with internal directory structure.
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
/// </summary>
bool ExtractFullPath { get; init; }
bool ExtractFullPath { get; set; }
/// <summary>
/// Preserve file time.
/// <para><b>Breaking change:</b> Default changed from false to true in version 0.40.0.</para>
/// </summary>
bool PreserveFileTime { get; init; }
bool PreserveFileTime { get; set; }
/// <summary>
/// Preserve windows file attributes.
/// </summary>
bool PreserveAttributes { get; init; }
bool PreserveAttributes { get; set; }
/// <summary>
/// Delegate for writing symbolic links to disk.
/// The first parameter is the source path (where the symlink is created).
/// The second parameter is the target path (what the symlink refers to).
/// </summary>
Action<string, string>? SymbolicLinkHandler { get; init; }
Action<string, string>? SymbolicLinkHandler { get; set; }
}

View file

@ -4,5 +4,5 @@ namespace SharpCompress.Common.Options;
public interface IProgressOptions
{
IProgress<ProgressReport>? Progress { get; init; }
IProgress<ProgressReport>? Progress { get; set; }
}

View file

@ -8,37 +8,37 @@ public interface IReaderOptions : IStreamOptions, IEncodingOptions, IProgressOpt
/// <summary>
/// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file)
/// </summary>
bool LookForHeader { get; init; }
bool LookForHeader { get; set; }
/// <summary>
/// Password for encrypted archives.
/// </summary>
string? Password { get; init; }
string? Password { get; set; }
/// <summary>
/// Disable checking for incomplete archives.
/// </summary>
bool DisableCheckIncomplete { get; init; }
bool DisableCheckIncomplete { get; set; }
/// <summary>
/// Buffer size for stream operations.
/// </summary>
int BufferSize { get; init; }
int BufferSize { get; set; }
/// <summary>
/// Provide a hint for the extension of the archive being read, can speed up finding the correct decoder.
/// </summary>
string? ExtensionHint { get; init; }
string? ExtensionHint { get; set; }
/// <summary>
/// Size of the rewindable buffer for non-seekable streams.
/// </summary>
int? RewindableBufferSize { get; init; }
int? RewindableBufferSize { get; set; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom providers.
/// Use this to provide alternative decompression implementations.
/// </summary>
CompressionProviderRegistry Providers { get; init; }
CompressionProviderRegistry Providers { get; set; }
}

View file

@ -2,5 +2,5 @@ namespace SharpCompress.Common.Options;
public interface IStreamOptions
{
bool LeaveStreamOpen { get; init; }
bool LeaveStreamOpen { get; set; }
}

View file

@ -12,17 +12,17 @@ public interface IWriterOptions : IStreamOptions, IEncodingOptions, IProgressOpt
/// <summary>
/// The compression type to use for the archive.
/// </summary>
CompressionType CompressionType { get; init; }
CompressionType CompressionType { get; set; }
/// <summary>
/// The compression level to be used when the compression type supports variable levels.
/// </summary>
int CompressionLevel { get; init; }
int CompressionLevel { get; set; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom providers, such as
/// System.IO.Compression for Deflate/GZip on modern .NET.
/// </summary>
CompressionProviderRegistry Providers { get; init; }
CompressionProviderRegistry Providers { get; set; }
}

View file

@ -12,22 +12,22 @@ public sealed record CompressionContext
/// <summary>
/// The size of the input data, or -1 if unknown.
/// </summary>
public long InputSize { get; init; } = -1;
public long InputSize { get; set; } = -1;
/// <summary>
/// The expected output size, or -1 if unknown.
/// </summary>
public long OutputSize { get; init; } = -1;
public long OutputSize { get; set; } = -1;
/// <summary>
/// Properties bytes for the compression format (e.g., LZMA properties).
/// </summary>
public byte[]? Properties { get; init; }
public byte[]? Properties { get; set; }
/// <summary>
/// Whether the underlying stream supports seeking.
/// </summary>
public bool CanSeek { get; init; }
public bool CanSeek { get; set; }
/// <summary>
/// Additional format-specific options.
@ -38,7 +38,7 @@ public sealed record CompressionContext
/// Examples of valid FormatOptions values include compression properties (e.g., LZMA properties),
/// format flags, or algorithm-specific configuration.
/// </remarks>
public object? FormatOptions { get; init; }
public object? FormatOptions { get; set; }
/// <summary>
/// Creates a CompressionContext from a stream.
@ -51,7 +51,7 @@ public sealed record CompressionContext
/// <summary>
/// Reader options for accessing archive metadata such as header encoding.
/// </summary>
public IReaderOptions? ReaderOptions { get; init; }
public IReaderOptions? ReaderOptions { get; set; }
/// <summary>
/// Returns a new <see cref="CompressionContext"/> with the specified reader options.

View file

@ -10,7 +10,7 @@ namespace SharpCompress.Readers;
/// Options for configuring reader behavior when opening archives.
/// </summary>
/// <remarks>
/// This class is immutable. Use preset properties and fluent helpers for common configurations:
/// Use preset properties, setters, and fluent helpers for common configurations:
/// <code>
/// var options = ReaderOptions.ForExternalStream
/// .WithPassword("secret")
@ -53,43 +53,43 @@ public sealed record ReaderOptions : IReaderOptions
/// </code>
/// </para>
/// </remarks>
public bool LeaveStreamOpen { get; init; } = false;
public bool LeaveStreamOpen { get; set; } = false;
/// <summary>
/// Encoding to use for archive entry names.
/// </summary>
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
/// <summary>
/// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file)
/// </summary>
public bool LookForHeader { get; init; }
public bool LookForHeader { get; set; }
/// <summary>
/// Password for encrypted archives.
/// </summary>
public string? Password { get; init; }
public string? Password { get; set; }
/// <summary>
/// Disable checking for incomplete archives.
/// </summary>
public bool DisableCheckIncomplete { get; init; }
public bool DisableCheckIncomplete { get; set; }
/// <summary>
/// Buffer size for stream operations.
/// </summary>
public int BufferSize { get; init; } = Constants.BufferSize;
public int BufferSize { get; set; } = Constants.BufferSize;
/// <summary>
/// Provide a hint for the extension of the archive being read, can speed up finding the correct decoder. Should be without the leading period in the form like: tar.gz or zip
/// </summary>
public string? ExtensionHint { get; init; }
public string? ExtensionHint { get; set; }
/// <summary>
/// An optional progress reporter for tracking extraction operations.
/// When set, progress updates will be reported as entries are extracted.
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
public IProgress<ProgressReport>? Progress { get; set; }
/// <summary>
/// Size of the rewindable buffer for non-seekable streams.
@ -133,14 +133,14 @@ public sealed record ReaderOptions : IReaderOptions
/// using var reader = ReaderFactory.OpenReader(networkStream, options);
/// </code>
/// </example>
public int? RewindableBufferSize { get; init; }
public int? RewindableBufferSize { get; set; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom implementations, such as
/// System.IO.Compression for Deflate/GZip on modern .NET.
/// </summary>
public CompressionProviderRegistry Providers { get; init; } =
public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
/// <summary>

View file

@ -12,7 +12,7 @@ namespace SharpCompress.Writers.GZip;
/// Options for configuring GZip writer behavior.
/// </summary>
/// <remarks>
/// This class is immutable. Use factory methods for creation:
/// Use factory methods, property setters, or fluent helpers for creation:
/// <code>
/// var options = WriterOptions.ForGZip().WithLeaveStreamOpen(false).WithCompressionLevel(9);
/// </code>
@ -27,7 +27,7 @@ public sealed record GZipWriterOptions : IWriterOptions
public CompressionType CompressionType
{
get => CompressionType.GZip;
init
set
{
if (value != CompressionType.GZip)
{
@ -46,7 +46,7 @@ public sealed record GZipWriterOptions : IWriterOptions
public int CompressionLevel
{
get => _compressionLevel;
init
set
{
CompressionLevelValidation.Validate(CompressionType.GZip, value);
_compressionLevel = value;
@ -56,24 +56,24 @@ public sealed record GZipWriterOptions : IWriterOptions
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
public bool LeaveStreamOpen { get; init; } = true;
public bool LeaveStreamOpen { get; set; } = true;
/// <summary>
/// Encoding to use for archive entry names.
/// </summary>
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
/// <summary>
/// An optional progress reporter for tracking compression operations.
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
public IProgress<ProgressReport>? Progress { get; set; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom implementations, such as
/// System.IO.Compression for GZip on modern .NET.
/// </summary>
public CompressionProviderRegistry Providers { get; init; } =
public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
/// <summary>

View file

@ -20,7 +20,7 @@ public sealed record SevenZipWriterOptions : IWriterOptions
public CompressionType CompressionType
{
get => _compressionType;
init
set
{
if (value != CompressionType.LZMA && value != CompressionType.LZMA2)
{
@ -39,41 +39,41 @@ public sealed record SevenZipWriterOptions : IWriterOptions
public int CompressionLevel
{
get => _compressionLevel;
init => _compressionLevel = value;
set => _compressionLevel = value;
}
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
public bool LeaveStreamOpen { get; init; } = true;
public bool LeaveStreamOpen { get; set; } = true;
/// <summary>
/// Encoding to use for archive entry names.
/// </summary>
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
/// <summary>
/// An optional progress reporter for tracking compression operations.
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
public IProgress<ProgressReport>? Progress { get; set; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom implementations.
/// </summary>
public CompressionProviderRegistry Providers { get; init; } =
public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
/// <summary>
/// Whether to compress the archive header itself using LZMA.
/// Default is true, matching standard 7-Zip behavior.
/// </summary>
public bool CompressHeader { get; init; } = true;
public bool CompressHeader { get; set; } = true;
/// <summary>
/// Custom LZMA encoder properties. Null uses defaults (1MB dictionary, 32 fast bytes).
/// </summary>
public LzmaEncoderProperties? LzmaProperties { get; init; }
public LzmaEncoderProperties? LzmaProperties { get; set; }
/// <summary>
/// Creates a new SevenZipWriterOptions instance with LZMA2 compression (default).

View file

@ -11,7 +11,7 @@ namespace SharpCompress.Writers.Tar;
/// Options for configuring Tar writer behavior.
/// </summary>
/// <remarks>
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
/// Configure tar writing with constructors, property setters, or the <c>with</c> expression:
/// <code>
/// var options = new TarWriterOptions(CompressionType.GZip, true);
/// options = options with { HeaderFormat = TarHeaderWriteFormat.V7 };
@ -22,45 +22,44 @@ public sealed record TarWriterOptions : IWriterOptions
/// <summary>
/// The compression type to use for the archive.
/// </summary>
public CompressionType CompressionType { get; init; }
public CompressionType CompressionType { get; set; }
/// <summary>
/// The compression level to be used when the compression type supports variable levels.
/// </summary>
public int CompressionLevel { get; init; }
public int CompressionLevel { get; set; }
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
public bool LeaveStreamOpen { get; init; } = true;
public bool LeaveStreamOpen { get; set; } = true;
/// <summary>
/// Encoding to use for archive entry names.
/// </summary>
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
/// <summary>
/// An optional progress reporter for tracking compression operations.
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
public IProgress<ProgressReport>? Progress { get; set; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom implementations.
/// </summary>
public CompressionProviderRegistry Providers { get; init; } =
public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
/// <summary>
/// Indicates if archive should be finalized (by 2 empty blocks) on close.
/// </summary>
public bool FinalizeArchiveOnClose { get; init; } = true;
public bool FinalizeArchiveOnClose { get; set; } = true;
/// <summary>
/// The format to use when writing tar headers.
/// </summary>
public TarHeaderWriteFormat HeaderFormat { get; init; } =
TarHeaderWriteFormat.GNU_TAR_LONG_LINK;
public TarHeaderWriteFormat HeaderFormat { get; set; } = TarHeaderWriteFormat.GNU_TAR_LONG_LINK;
/// <summary>
/// Creates a new TarWriterOptions instance with the specified compression type and finalization option.

View file

@ -10,7 +10,7 @@ namespace SharpCompress.Writers;
/// Options for configuring writer behavior when creating archives.
/// </summary>
/// <remarks>
/// This class is immutable. Use factory methods for creation:
/// Use factory methods, property setters, or fluent helpers for creation:
/// <code>
/// var options = WriterOptions.ForZip().WithLeaveStreamOpen(false).WithCompressionLevel(9);
/// </code>
@ -20,7 +20,7 @@ public sealed record WriterOptions : IWriterOptions
/// <summary>
/// The compression type to use for the archive.
/// </summary>
public CompressionType CompressionType { get; init; }
public CompressionType CompressionType { get; set; }
/// <summary>
/// The compression level to be used when the compression type supports variable levels.
@ -33,7 +33,7 @@ public sealed record WriterOptions : IWriterOptions
public int CompressionLevel
{
get;
init
set
{
CompressionLevelValidation.Validate(CompressionType, value);
field = value;
@ -43,25 +43,25 @@ public sealed record WriterOptions : IWriterOptions
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
public bool LeaveStreamOpen { get; init; } = true;
public bool LeaveStreamOpen { get; set; } = true;
/// <summary>
/// Encoding to use for archive entry names.
/// </summary>
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
/// <summary>
/// An optional progress reporter for tracking compression operations.
/// When set, progress updates will be reported as entries are written.
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
public IProgress<ProgressReport>? Progress { get; set; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom implementations, such as
/// System.IO.Compression for Deflate/GZip on modern .NET.
/// </summary>
public CompressionProviderRegistry Providers { get; init; } =
public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
/// <summary>

View file

@ -13,7 +13,7 @@ namespace SharpCompress.Writers.Zip;
/// Options for configuring Zip writer behavior.
/// </summary>
/// <remarks>
/// This class is immutable. Use the <c>with</c> expression to create modified copies:
/// Configure zip writing with constructors, property setters, or the <c>with</c> expression:
/// <code>
/// var options = new ZipWriterOptions(CompressionType.Zip);
/// options = options with { UseZip64 = true };
@ -30,7 +30,7 @@ public sealed record ZipWriterOptions : IWriterOptions
public CompressionType CompressionType
{
get => _compressionType;
init => _compressionType = value;
set => _compressionType = value;
}
/// <summary>
@ -39,7 +39,7 @@ public sealed record ZipWriterOptions : IWriterOptions
public int CompressionLevel
{
get => _compressionLevel;
init
set
{
CompressionLevelValidation.Validate(CompressionType, value);
_compressionLevel = value;
@ -49,29 +49,29 @@ public sealed record ZipWriterOptions : IWriterOptions
/// <summary>
/// SharpCompress will keep the supplied streams open. Default is true.
/// </summary>
public bool LeaveStreamOpen { get; init; } = true;
public bool LeaveStreamOpen { get; set; } = true;
/// <summary>
/// Encoding to use for archive entry names.
/// </summary>
public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding();
public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding();
/// <summary>
/// An optional progress reporter for tracking compression operations.
/// </summary>
public IProgress<ProgressReport>? Progress { get; init; }
public IProgress<ProgressReport>? Progress { get; set; }
/// <summary>
/// Registry of compression providers.
/// Defaults to <see cref="CompressionProviderRegistry.Default" /> but can be replaced with custom implementations.
/// </summary>
public CompressionProviderRegistry Providers { get; init; } =
public CompressionProviderRegistry Providers { get; set; } =
CompressionProviderRegistry.Default;
/// <summary>
/// Optional comment for the archive.
/// </summary>
public string? ArchiveComment { get; init; }
public string? ArchiveComment { get; set; }
/// <summary>
/// Sets a value indicating if zip64 support is enabled.
@ -80,7 +80,7 @@ public sealed record ZipWriterOptions : IWriterOptions
/// Archives larger than 4GiB are supported as long as all streams
/// are less than 4GiB in length.
/// </summary>
public bool UseZip64 { get; init; }
public bool UseZip64 { get; set; }
/// <summary>
/// Creates a new ZipWriterOptions instance with the specified compression type.

View file

@ -36,6 +36,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
"PolySharp": {
"type": "Direct",
"requested": "[1.15.0, )",
"resolved": "1.15.0",
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
},
"System.Text.Encoding.CodePages": {
"type": "Direct",
"requested": "[8.0.0, )",
@ -139,6 +145,12 @@
"Microsoft.NETCore.Platforms": "1.1.0"
}
},
"PolySharp": {
"type": "Direct",
"requested": "[1.15.0, )",
"resolved": "1.15.0",
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
},
"System.Text.Encoding.CodePages": {
"type": "Direct",
"requested": "[8.0.0, )",
@ -235,6 +247,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
"PolySharp": {
"type": "Direct",
"requested": "[1.15.0, )",
"resolved": "1.15.0",
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
},
"System.Text.Encoding.CodePages": {
"type": "Direct",
"requested": "[8.0.0, )",
@ -268,9 +286,9 @@
"net10.0": {
"Microsoft.NET.ILLink.Tasks": {
"type": "Direct",
"requested": "[10.0.6, )",
"resolved": "10.0.6",
"contentHash": "QKuvS0LWX4fjFqeDkyM7Kqt8P3wYTiPD4nwU+9y59n0sCiG714fxDgbbN82vDnzq89AF/PiHl92TP2C4aFDUQA=="
"requested": "[10.0.7, )",
"resolved": "10.0.7",
"contentHash": "AA/yhzFHNtQZXLdqjzujPy25G8EWwGWsAnxOE2zYSBoT/8QHP6ketN3CToD3DFreO653ipUwnKHo22B8AlBMCw=="
},
"Microsoft.NETFramework.ReferenceAssemblies": {
"type": "Direct",
@ -297,6 +315,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
"PolySharp": {
"type": "Direct",
"requested": "[1.15.0, )",
"resolved": "1.15.0",
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "10.0.102",
@ -339,6 +363,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
"PolySharp": {
"type": "Direct",
"requested": "[1.15.0, )",
"resolved": "1.15.0",
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "10.0.102",
@ -387,6 +417,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
"PolySharp": {
"type": "Direct",
"requested": "[1.15.0, )",
"resolved": "1.15.0",
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
},
"Microsoft.Build.Tasks.Git": {
"type": "Transitive",
"resolved": "10.0.102",

View file

@ -55,6 +55,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
"PolySharp": {
"type": "Direct",
"requested": "[1.15.0, )",
"resolved": "1.15.0",
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
},
"BenchmarkDotNet.Annotations": {
"type": "Transitive",
"resolved": "0.15.8",

View file

@ -1,5 +1,8 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Common;
@ -228,6 +231,65 @@ public class OptionsUsabilityTests : TestBase
Assert.True(preserveMetadata.PreserveAttributes);
}
[Fact]
public void Public_Api_Does_Not_Expose_CSharp_9_Required_Metadata()
{
var assembly = typeof(ReaderOptions).Assembly;
const string RequiredMemberAttributeName =
"System.Runtime.CompilerServices.RequiredMemberAttribute";
const string SetsRequiredMembersAttributeName =
"System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute";
var initOnlyProperties = assembly
.GetExportedTypes()
.SelectMany(type =>
type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(property => property.SetMethod?.IsPublic == true)
.Where(property =>
property
.SetMethod!.ReturnParameter.GetRequiredCustomModifiers()
.Contains(typeof(IsExternalInit))
)
.Select(property => $"{type.FullName}.{property.Name}")
)
.ToArray();
Assert.Empty(initOnlyProperties);
var requiredMembers = assembly
.GetExportedTypes()
.SelectMany(type =>
type.GetMembers(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)
.Where(member =>
member
.GetCustomAttributesData()
.Any(attribute =>
attribute.AttributeType.FullName == RequiredMemberAttributeName
)
)
.Select(member => $"{type.FullName}.{member.Name}")
)
.ToArray();
Assert.Empty(requiredMembers);
var constructorsWithRequiredMembers = assembly
.GetExportedTypes()
.SelectMany(type =>
type.GetConstructors(BindingFlags.Instance | BindingFlags.Public)
.Where(constructor =>
constructor
.GetCustomAttributesData()
.Any(attribute =>
attribute.AttributeType.FullName == SetsRequiredMembersAttributeName
)
)
.Select(_ => $"{type.FullName}.ctor")
)
.ToArray();
Assert.Empty(constructorsWithRequiredMembers);
}
[Fact]
public void ReaderOptions_Factory_ForEncryptedArchive_Sets_Password()
{

View file

@ -45,6 +45,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
"PolySharp": {
"type": "Direct",
"requested": "[1.15.0, )",
"resolved": "1.15.0",
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
},
"xunit.runner.visualstudio": {
"type": "Direct",
"requested": "[3.1.5, )",
@ -309,6 +315,30 @@
}
}
},
".NETFramework,Version=v4.8/win-x86": {
"Microsoft.Win32.Registry": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
"dependencies": {
"System.Security.AccessControl": "5.0.0",
"System.Security.Principal.Windows": "5.0.0"
}
},
"System.Security.AccessControl": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
"dependencies": {
"System.Security.Principal.Windows": "5.0.0"
}
},
"System.Security.Principal.Windows": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
}
},
"net10.0": {
"AwesomeAssertions": {
"type": "Direct",
@ -351,6 +381,12 @@
"resolved": "17.14.15",
"contentHash": "mXQPJsbuUD2ydq4/ffd8h8tSOFCXec+2xJOVNCvXjuMOq/+5EKHq3D2m2MC2+nUaXeFMSt66VS/J4HdKBixgcw=="
},
"PolySharp": {
"type": "Direct",
"requested": "[1.15.0, )",
"resolved": "1.15.0",
"contentHash": "FbU0El+EEjdpuIX4iDbeS7ki1uzpJPx8vbqOzEtqnl1GZeAGJfq+jCbxeJL2y0EPnUNk8dRnnqR2xnYXg9Tf+g=="
},
"xunit.runner.visualstudio": {
"type": "Direct",
"requested": "[3.1.5, )",
@ -521,6 +557,13 @@
"resolved": "8.0.0",
"contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw=="
}
},
"net10.0/win-x86": {
"Microsoft.Win32.Registry": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg=="
}
}
}
}