diff --git a/AGENTS.md b/AGENTS.md index 794808542..ef872d0c8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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. diff --git a/Directory.Packages.props b/Directory.Packages.props index dff2b3320..ca0f52608 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,5 +18,6 @@ Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.14.15" /> + diff --git a/build/packages.lock.json b/build/packages.lock.json index 395d8f2a4..e0b23c843 100644 --- a/build/packages.lock.json +++ b/build/packages.lock.json @@ -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, )", diff --git a/src/SharpCompress/Archives/ArchiveInformation.cs b/src/SharpCompress/Archives/ArchiveInformation.cs index adc0dfa2b..75d3cb5a6 100644 --- a/src/SharpCompress/Archives/ArchiveInformation.cs +++ b/src/SharpCompress/Archives/ArchiveInformation.cs @@ -10,13 +10,29 @@ namespace SharpCompress.Archives; /// /// to obtain an instance of this record. /// -/// -/// The type of archive detected, or when the format is not a registered well-known type. -/// -/// -/// when this archive format supports random access via the API, -/// meaning the full file listing can be retrieved without decompressing the entire archive. -/// when only the API is available, -/// which reads entries sequentially and can only report per-entry progress. -/// -public record ArchiveInformation(ArchiveType? Type, bool SupportsRandomAccess); +public record ArchiveInformation +{ + /// + /// The type of archive detected, or when the format is not a registered well-known type. + /// + public ArchiveType? Type { get; set; } + + /// + /// when this archive format supports random access via the API, + /// meaning the full file listing can be retrieved without decompressing the entire archive. + /// when only the API is available, + /// which reads entries sequentially and can only report per-entry progress. + /// + public bool SupportsRandomAccess { get; set; } + + /// + /// Creates a new archive information instance. + /// + /// The detected archive type. + /// Whether the detected format supports random access. + public ArchiveInformation(ArchiveType? type, bool supportsRandomAccess) + { + Type = type; + SupportsRandomAccess = supportsRandomAccess; + } +} diff --git a/src/SharpCompress/Common/ExtractionOptions.cs b/src/SharpCompress/Common/ExtractionOptions.cs index ea84ed53e..8a3b4859a 100644 --- a/src/SharpCompress/Common/ExtractionOptions.cs +++ b/src/SharpCompress/Common/ExtractionOptions.cs @@ -7,7 +7,7 @@ namespace SharpCompress.Common; /// Options for configuring extraction behavior when extracting archive entries. /// /// -/// This class is immutable. Use the with expression to create modified copies: +/// Configure extraction behavior with constructors, property setters, or the with expression: /// /// 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. /// Breaking change: Default changed from false to true in version 0.40.0. /// - public bool Overwrite { get; init; } = true; + public bool Overwrite { get; set; } = true; /// /// Extract with internal directory structure. /// Breaking change: Default changed from false to true in version 0.40.0. /// - public bool ExtractFullPath { get; init; } = true; + public bool ExtractFullPath { get; set; } = true; /// /// Preserve file time. /// Breaking change: Default changed from false to true in version 0.40.0. /// - public bool PreserveFileTime { get; init; } = true; + public bool PreserveFileTime { get; set; } = true; /// /// Preserve windows file attributes. /// - public bool PreserveAttributes { get; init; } + public bool PreserveAttributes { get; set; } /// /// 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). /// /// - /// Breaking change: Changed from field to init-only property in version 0.40.0. + /// Breaking change: Changed from field to property in version 0.40.0. /// If no handler is provided, symbolic links are silently skipped during extraction. /// - public Action? SymbolicLinkHandler { get; init; } + public Action? SymbolicLinkHandler { get; set; } /// /// Creates a new ExtractionOptions instance with default values. diff --git a/src/SharpCompress/Common/Options/IEncodingOptions.cs b/src/SharpCompress/Common/Options/IEncodingOptions.cs index d9020a468..47fb56f89 100644 --- a/src/SharpCompress/Common/Options/IEncodingOptions.cs +++ b/src/SharpCompress/Common/Options/IEncodingOptions.cs @@ -2,5 +2,5 @@ namespace SharpCompress.Common.Options; public interface IEncodingOptions { - IArchiveEncoding ArchiveEncoding { get; init; } + IArchiveEncoding ArchiveEncoding { get; set; } } diff --git a/src/SharpCompress/Common/Options/IExtractionOptions.cs b/src/SharpCompress/Common/Options/IExtractionOptions.cs index 3e88d2473..8aac6a651 100644 --- a/src/SharpCompress/Common/Options/IExtractionOptions.cs +++ b/src/SharpCompress/Common/Options/IExtractionOptions.cs @@ -11,29 +11,29 @@ public interface IExtractionOptions /// Overwrite target if it exists. /// Breaking change: Default changed from false to true in version 0.40.0. /// - bool Overwrite { get; init; } + bool Overwrite { get; set; } /// /// Extract with internal directory structure. /// Breaking change: Default changed from false to true in version 0.40.0. /// - bool ExtractFullPath { get; init; } + bool ExtractFullPath { get; set; } /// /// Preserve file time. /// Breaking change: Default changed from false to true in version 0.40.0. /// - bool PreserveFileTime { get; init; } + bool PreserveFileTime { get; set; } /// /// Preserve windows file attributes. /// - bool PreserveAttributes { get; init; } + bool PreserveAttributes { get; set; } /// /// 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). /// - Action? SymbolicLinkHandler { get; init; } + Action? SymbolicLinkHandler { get; set; } } diff --git a/src/SharpCompress/Common/Options/IProgressOptions.cs b/src/SharpCompress/Common/Options/IProgressOptions.cs index 76bab1bf5..80e9dd16a 100644 --- a/src/SharpCompress/Common/Options/IProgressOptions.cs +++ b/src/SharpCompress/Common/Options/IProgressOptions.cs @@ -4,5 +4,5 @@ namespace SharpCompress.Common.Options; public interface IProgressOptions { - IProgress? Progress { get; init; } + IProgress? Progress { get; set; } } diff --git a/src/SharpCompress/Common/Options/IReaderOptions.cs b/src/SharpCompress/Common/Options/IReaderOptions.cs index 74d00de58..c25f48251 100644 --- a/src/SharpCompress/Common/Options/IReaderOptions.cs +++ b/src/SharpCompress/Common/Options/IReaderOptions.cs @@ -8,37 +8,37 @@ public interface IReaderOptions : IStreamOptions, IEncodingOptions, IProgressOpt /// /// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file) /// - bool LookForHeader { get; init; } + bool LookForHeader { get; set; } /// /// Password for encrypted archives. /// - string? Password { get; init; } + string? Password { get; set; } /// /// Disable checking for incomplete archives. /// - bool DisableCheckIncomplete { get; init; } + bool DisableCheckIncomplete { get; set; } /// /// Buffer size for stream operations. /// - int BufferSize { get; init; } + int BufferSize { get; set; } /// /// Provide a hint for the extension of the archive being read, can speed up finding the correct decoder. /// - string? ExtensionHint { get; init; } + string? ExtensionHint { get; set; } /// /// Size of the rewindable buffer for non-seekable streams. /// - int? RewindableBufferSize { get; init; } + int? RewindableBufferSize { get; set; } /// /// Registry of compression providers. /// Defaults to but can be replaced with custom providers. /// Use this to provide alternative decompression implementations. /// - CompressionProviderRegistry Providers { get; init; } + CompressionProviderRegistry Providers { get; set; } } diff --git a/src/SharpCompress/Common/Options/IStreamOptions.cs b/src/SharpCompress/Common/Options/IStreamOptions.cs index be0bbb9fe..94a607901 100644 --- a/src/SharpCompress/Common/Options/IStreamOptions.cs +++ b/src/SharpCompress/Common/Options/IStreamOptions.cs @@ -2,5 +2,5 @@ namespace SharpCompress.Common.Options; public interface IStreamOptions { - bool LeaveStreamOpen { get; init; } + bool LeaveStreamOpen { get; set; } } diff --git a/src/SharpCompress/Common/Options/IWriterOptions.cs b/src/SharpCompress/Common/Options/IWriterOptions.cs index 73dd1ec38..a7a1d719f 100644 --- a/src/SharpCompress/Common/Options/IWriterOptions.cs +++ b/src/SharpCompress/Common/Options/IWriterOptions.cs @@ -12,17 +12,17 @@ public interface IWriterOptions : IStreamOptions, IEncodingOptions, IProgressOpt /// /// The compression type to use for the archive. /// - CompressionType CompressionType { get; init; } + CompressionType CompressionType { get; set; } /// /// The compression level to be used when the compression type supports variable levels. /// - int CompressionLevel { get; init; } + int CompressionLevel { get; set; } /// /// Registry of compression providers. /// Defaults to but can be replaced with custom providers, such as /// System.IO.Compression for Deflate/GZip on modern .NET. /// - CompressionProviderRegistry Providers { get; init; } + CompressionProviderRegistry Providers { get; set; } } diff --git a/src/SharpCompress/Providers/CompressionContext.cs b/src/SharpCompress/Providers/CompressionContext.cs index e0680646a..e74a7e9c7 100644 --- a/src/SharpCompress/Providers/CompressionContext.cs +++ b/src/SharpCompress/Providers/CompressionContext.cs @@ -12,22 +12,22 @@ public sealed record CompressionContext /// /// The size of the input data, or -1 if unknown. /// - public long InputSize { get; init; } = -1; + public long InputSize { get; set; } = -1; /// /// The expected output size, or -1 if unknown. /// - public long OutputSize { get; init; } = -1; + public long OutputSize { get; set; } = -1; /// /// Properties bytes for the compression format (e.g., LZMA properties). /// - public byte[]? Properties { get; init; } + public byte[]? Properties { get; set; } /// /// Whether the underlying stream supports seeking. /// - public bool CanSeek { get; init; } + public bool CanSeek { get; set; } /// /// 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. /// - public object? FormatOptions { get; init; } + public object? FormatOptions { get; set; } /// /// Creates a CompressionContext from a stream. @@ -51,7 +51,7 @@ public static CompressionContext FromStream(Stream stream) => /// /// Reader options for accessing archive metadata such as header encoding. /// - public IReaderOptions? ReaderOptions { get; init; } + public IReaderOptions? ReaderOptions { get; set; } /// /// Returns a new with the specified reader options. diff --git a/src/SharpCompress/Readers/ReaderOptions.cs b/src/SharpCompress/Readers/ReaderOptions.cs index 109e3e932..aced7169e 100644 --- a/src/SharpCompress/Readers/ReaderOptions.cs +++ b/src/SharpCompress/Readers/ReaderOptions.cs @@ -10,7 +10,7 @@ namespace SharpCompress.Readers; /// Options for configuring reader behavior when opening archives. /// /// -/// This class is immutable. Use preset properties and fluent helpers for common configurations: +/// Use preset properties, setters, and fluent helpers for common configurations: /// /// var options = ReaderOptions.ForExternalStream /// .WithPassword("secret") @@ -53,43 +53,43 @@ public sealed record ReaderOptions : IReaderOptions /// /// /// - public bool LeaveStreamOpen { get; init; } = false; + public bool LeaveStreamOpen { get; set; } = false; /// /// Encoding to use for archive entry names. /// - public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding(); + public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding(); /// /// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file) /// - public bool LookForHeader { get; init; } + public bool LookForHeader { get; set; } /// /// Password for encrypted archives. /// - public string? Password { get; init; } + public string? Password { get; set; } /// /// Disable checking for incomplete archives. /// - public bool DisableCheckIncomplete { get; init; } + public bool DisableCheckIncomplete { get; set; } /// /// Buffer size for stream operations. /// - public int BufferSize { get; init; } = Constants.BufferSize; + public int BufferSize { get; set; } = Constants.BufferSize; /// /// 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 /// - public string? ExtensionHint { get; init; } + public string? ExtensionHint { get; set; } /// /// An optional progress reporter for tracking extraction operations. /// When set, progress updates will be reported as entries are extracted. /// - public IProgress? Progress { get; init; } + public IProgress? Progress { get; set; } /// /// 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); /// /// - public int? RewindableBufferSize { get; init; } + public int? RewindableBufferSize { get; set; } /// /// Registry of compression providers. /// Defaults to but can be replaced with custom implementations, such as /// System.IO.Compression for Deflate/GZip on modern .NET. /// - public CompressionProviderRegistry Providers { get; init; } = + public CompressionProviderRegistry Providers { get; set; } = CompressionProviderRegistry.Default; /// diff --git a/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs b/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs index b70b647a1..42da70e21 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriterOptions.cs @@ -12,7 +12,7 @@ namespace SharpCompress.Writers.GZip; /// Options for configuring GZip writer behavior. /// /// -/// This class is immutable. Use factory methods for creation: +/// Use factory methods, property setters, or fluent helpers for creation: /// /// var options = WriterOptions.ForGZip().WithLeaveStreamOpen(false).WithCompressionLevel(9); /// @@ -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 CompressionType CompressionType public int CompressionLevel { get => _compressionLevel; - init + set { CompressionLevelValidation.Validate(CompressionType.GZip, value); _compressionLevel = value; @@ -56,24 +56,24 @@ public int CompressionLevel /// /// SharpCompress will keep the supplied streams open. Default is true. /// - public bool LeaveStreamOpen { get; init; } = true; + public bool LeaveStreamOpen { get; set; } = true; /// /// Encoding to use for archive entry names. /// - public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding(); + public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding(); /// /// An optional progress reporter for tracking compression operations. /// - public IProgress? Progress { get; init; } + public IProgress? Progress { get; set; } /// /// Registry of compression providers. /// Defaults to but can be replaced with custom implementations, such as /// System.IO.Compression for GZip on modern .NET. /// - public CompressionProviderRegistry Providers { get; init; } = + public CompressionProviderRegistry Providers { get; set; } = CompressionProviderRegistry.Default; /// diff --git a/src/SharpCompress/Writers/SevenZip/SevenZipWriterOptions.cs b/src/SharpCompress/Writers/SevenZip/SevenZipWriterOptions.cs index 033f07fdd..86fa09ffb 100644 --- a/src/SharpCompress/Writers/SevenZip/SevenZipWriterOptions.cs +++ b/src/SharpCompress/Writers/SevenZip/SevenZipWriterOptions.cs @@ -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 CompressionType CompressionType public int CompressionLevel { get => _compressionLevel; - init => _compressionLevel = value; + set => _compressionLevel = value; } /// /// SharpCompress will keep the supplied streams open. Default is true. /// - public bool LeaveStreamOpen { get; init; } = true; + public bool LeaveStreamOpen { get; set; } = true; /// /// Encoding to use for archive entry names. /// - public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding(); + public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding(); /// /// An optional progress reporter for tracking compression operations. /// - public IProgress? Progress { get; init; } + public IProgress? Progress { get; set; } /// /// Registry of compression providers. /// Defaults to but can be replaced with custom implementations. /// - public CompressionProviderRegistry Providers { get; init; } = + public CompressionProviderRegistry Providers { get; set; } = CompressionProviderRegistry.Default; /// /// Whether to compress the archive header itself using LZMA. /// Default is true, matching standard 7-Zip behavior. /// - public bool CompressHeader { get; init; } = true; + public bool CompressHeader { get; set; } = true; /// /// Custom LZMA encoder properties. Null uses defaults (1MB dictionary, 32 fast bytes). /// - public LzmaEncoderProperties? LzmaProperties { get; init; } + public LzmaEncoderProperties? LzmaProperties { get; set; } /// /// Creates a new SevenZipWriterOptions instance with LZMA2 compression (default). diff --git a/src/SharpCompress/Writers/Tar/TarWriterOptions.cs b/src/SharpCompress/Writers/Tar/TarWriterOptions.cs index 16e5df9f9..004d46430 100755 --- a/src/SharpCompress/Writers/Tar/TarWriterOptions.cs +++ b/src/SharpCompress/Writers/Tar/TarWriterOptions.cs @@ -11,7 +11,7 @@ namespace SharpCompress.Writers.Tar; /// Options for configuring Tar writer behavior. /// /// -/// This class is immutable. Use the with expression to create modified copies: +/// Configure tar writing with constructors, property setters, or the with expression: /// /// var options = new TarWriterOptions(CompressionType.GZip, true); /// options = options with { HeaderFormat = TarHeaderWriteFormat.V7 }; @@ -22,45 +22,44 @@ public sealed record TarWriterOptions : IWriterOptions /// /// The compression type to use for the archive. /// - public CompressionType CompressionType { get; init; } + public CompressionType CompressionType { get; set; } /// /// The compression level to be used when the compression type supports variable levels. /// - public int CompressionLevel { get; init; } + public int CompressionLevel { get; set; } /// /// SharpCompress will keep the supplied streams open. Default is true. /// - public bool LeaveStreamOpen { get; init; } = true; + public bool LeaveStreamOpen { get; set; } = true; /// /// Encoding to use for archive entry names. /// - public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding(); + public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding(); /// /// An optional progress reporter for tracking compression operations. /// - public IProgress? Progress { get; init; } + public IProgress? Progress { get; set; } /// /// Registry of compression providers. /// Defaults to but can be replaced with custom implementations. /// - public CompressionProviderRegistry Providers { get; init; } = + public CompressionProviderRegistry Providers { get; set; } = CompressionProviderRegistry.Default; /// /// Indicates if archive should be finalized (by 2 empty blocks) on close. /// - public bool FinalizeArchiveOnClose { get; init; } = true; + public bool FinalizeArchiveOnClose { get; set; } = true; /// /// The format to use when writing tar headers. /// - public TarHeaderWriteFormat HeaderFormat { get; init; } = - TarHeaderWriteFormat.GNU_TAR_LONG_LINK; + public TarHeaderWriteFormat HeaderFormat { get; set; } = TarHeaderWriteFormat.GNU_TAR_LONG_LINK; /// /// Creates a new TarWriterOptions instance with the specified compression type and finalization option. diff --git a/src/SharpCompress/Writers/WriterOptions.cs b/src/SharpCompress/Writers/WriterOptions.cs index 6415ca3e0..e4dd4ac06 100644 --- a/src/SharpCompress/Writers/WriterOptions.cs +++ b/src/SharpCompress/Writers/WriterOptions.cs @@ -10,7 +10,7 @@ namespace SharpCompress.Writers; /// Options for configuring writer behavior when creating archives. /// /// -/// This class is immutable. Use factory methods for creation: +/// Use factory methods, property setters, or fluent helpers for creation: /// /// var options = WriterOptions.ForZip().WithLeaveStreamOpen(false).WithCompressionLevel(9); /// @@ -20,7 +20,7 @@ public sealed record WriterOptions : IWriterOptions /// /// The compression type to use for the archive. /// - public CompressionType CompressionType { get; init; } + public CompressionType CompressionType { get; set; } /// /// 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 int CompressionLevel /// /// SharpCompress will keep the supplied streams open. Default is true. /// - public bool LeaveStreamOpen { get; init; } = true; + public bool LeaveStreamOpen { get; set; } = true; /// /// Encoding to use for archive entry names. /// - public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding(); + public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding(); /// /// An optional progress reporter for tracking compression operations. /// When set, progress updates will be reported as entries are written. /// - public IProgress? Progress { get; init; } + public IProgress? Progress { get; set; } /// /// Registry of compression providers. /// Defaults to but can be replaced with custom implementations, such as /// System.IO.Compression for Deflate/GZip on modern .NET. /// - public CompressionProviderRegistry Providers { get; init; } = + public CompressionProviderRegistry Providers { get; set; } = CompressionProviderRegistry.Default; /// diff --git a/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs b/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs index 0089d7c82..e3bf28343 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriterOptions.cs @@ -13,7 +13,7 @@ namespace SharpCompress.Writers.Zip; /// Options for configuring Zip writer behavior. /// /// -/// This class is immutable. Use the with expression to create modified copies: +/// Configure zip writing with constructors, property setters, or the with expression: /// /// 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; } /// @@ -39,7 +39,7 @@ public CompressionType CompressionType public int CompressionLevel { get => _compressionLevel; - init + set { CompressionLevelValidation.Validate(CompressionType, value); _compressionLevel = value; @@ -49,29 +49,29 @@ public int CompressionLevel /// /// SharpCompress will keep the supplied streams open. Default is true. /// - public bool LeaveStreamOpen { get; init; } = true; + public bool LeaveStreamOpen { get; set; } = true; /// /// Encoding to use for archive entry names. /// - public IArchiveEncoding ArchiveEncoding { get; init; } = new ArchiveEncoding(); + public IArchiveEncoding ArchiveEncoding { get; set; } = new ArchiveEncoding(); /// /// An optional progress reporter for tracking compression operations. /// - public IProgress? Progress { get; init; } + public IProgress? Progress { get; set; } /// /// Registry of compression providers. /// Defaults to but can be replaced with custom implementations. /// - public CompressionProviderRegistry Providers { get; init; } = + public CompressionProviderRegistry Providers { get; set; } = CompressionProviderRegistry.Default; /// /// Optional comment for the archive. /// - public string? ArchiveComment { get; init; } + public string? ArchiveComment { get; set; } /// /// Sets a value indicating if zip64 support is enabled. @@ -80,7 +80,7 @@ public int CompressionLevel /// Archives larger than 4GiB are supported as long as all streams /// are less than 4GiB in length. /// - public bool UseZip64 { get; init; } + public bool UseZip64 { get; set; } /// /// Creates a new ZipWriterOptions instance with the specified compression type. diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index f522434d0..965d59a8d 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -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", diff --git a/tests/SharpCompress.Performance/packages.lock.json b/tests/SharpCompress.Performance/packages.lock.json index 939ede421..35d18d202 100644 --- a/tests/SharpCompress.Performance/packages.lock.json +++ b/tests/SharpCompress.Performance/packages.lock.json @@ -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", diff --git a/tests/SharpCompress.Test/OptionsUsabilityTests.cs b/tests/SharpCompress.Test/OptionsUsabilityTests.cs index f9f05aed5..11376f755 100644 --- a/tests/SharpCompress.Test/OptionsUsabilityTests.cs +++ b/tests/SharpCompress.Test/OptionsUsabilityTests.cs @@ -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 void ExtractionOptions_Presets_Have_Correct_Defaults() 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() { diff --git a/tests/SharpCompress.Test/packages.lock.json b/tests/SharpCompress.Test/packages.lock.json index 9a2199223..136bb015c 100644 --- a/tests/SharpCompress.Test/packages.lock.json +++ b/tests/SharpCompress.Test/packages.lock.json @@ -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==" + } } } } \ No newline at end of file