diff --git a/src/SharpCompress/Archives/AbstractArchive.cs b/src/SharpCompress/Archives/AbstractArchive.cs index 0412f93e..844035cd 100644 --- a/src/SharpCompress/Archives/AbstractArchive.cs +++ b/src/SharpCompress/Archives/AbstractArchive.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using SharpCompress; using SharpCompress.Common; using SharpCompress.IO; using SharpCompress.Readers; diff --git a/src/SharpCompress/Archives/AbstractWritableArchive.cs b/src/SharpCompress/Archives/AbstractWritableArchive.cs index 13fb66f9..e7bb2af2 100644 --- a/src/SharpCompress/Archives/AbstractWritableArchive.cs +++ b/src/SharpCompress/Archives/AbstractWritableArchive.cs @@ -12,7 +12,7 @@ namespace SharpCompress.Archives; public abstract class AbstractWritableArchive : AbstractArchive, - IWritableArchive + IWritableArchive, IWritableAsyncArchive where TEntry : IArchiveEntry where TVolume : IVolume { @@ -83,12 +83,12 @@ public abstract class AbstractWritableArchive } } - void IWritableArchive.RemoveEntry(IArchiveEntry entry) => RemoveEntry((TEntry)entry); + void IWritableArchiveCommon.RemoveEntry(IArchiveEntry entry) => RemoveEntry((TEntry)entry); public TEntry AddEntry(string key, Stream source, long size = 0, DateTime? modified = null) => AddEntry(key, source, false, size, modified); - IArchiveEntry IWritableArchive.AddEntry( + IArchiveEntry IWritableArchiveCommon.AddEntry( string key, Stream source, bool closeStream, @@ -96,7 +96,7 @@ public abstract class AbstractWritableArchive DateTime? modified ) => AddEntry(key, source, closeStream, size, modified); - IArchiveEntry IWritableArchive.AddDirectoryEntry(string key, DateTime? modified) => + IArchiveEntry IWritableArchiveCommon.AddDirectoryEntry(string key, DateTime? modified) => AddDirectoryEntry(key, modified); public TEntry AddEntry( diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs index 82ea7f67..b4c1b86f 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchive.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs @@ -88,7 +88,7 @@ public class GZipArchive : AbstractWritableArchive /// /// /// - public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + public static IWritableArchive Open(Stream stream, ReaderOptions? readerOptions = null) { stream.NotNull(nameof(stream)); @@ -108,14 +108,14 @@ public class GZipArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( Stream stream, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(stream, readerOptions); + return (IWritableAsyncArchive)Open(stream, readerOptions); } /// @@ -124,14 +124,14 @@ public class GZipArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( FileInfo fileInfo, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(fileInfo, readerOptions); + return (IWritableAsyncArchive)Open(fileInfo, readerOptions); } /// @@ -140,14 +140,14 @@ public class GZipArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( IReadOnlyList streams, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(streams, readerOptions); + return (IWritableAsyncArchive)Open(streams, readerOptions); } /// @@ -156,14 +156,14 @@ public class GZipArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( IReadOnlyList fileInfos, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(fileInfos, readerOptions); + return (IWritableAsyncArchive)Open(fileInfos, readerOptions); } public static GZipArchive Create() => new(); diff --git a/src/SharpCompress/Archives/IArchive.cs b/src/SharpCompress/Archives/IArchive.cs index 3ed7490d..0abb2f1b 100644 --- a/src/SharpCompress/Archives/IArchive.cs +++ b/src/SharpCompress/Archives/IArchive.cs @@ -39,4 +39,6 @@ public interface IArchive : IDisposable /// The total size of the files as uncompressed in the archive. /// long TotalUncompressSize { get; } + + bool IsEncrypted { get; } } diff --git a/src/SharpCompress/Archives/IWritableArchive.cs b/src/SharpCompress/Archives/IWritableArchive.cs index 74d8da76..514b8cdc 100644 --- a/src/SharpCompress/Archives/IWritableArchive.cs +++ b/src/SharpCompress/Archives/IWritableArchive.cs @@ -6,8 +6,13 @@ using SharpCompress.Writers; namespace SharpCompress.Archives; -public interface IWritableArchive : IArchive +public interface IWritableArchiveCommon { + /// + /// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended. + /// + /// IDisposeable to resume entry rebuilding + IDisposable PauseEntryRebuilding(); void RemoveEntry(IArchiveEntry entry); IArchiveEntry AddEntry( @@ -19,18 +24,20 @@ public interface IWritableArchive : IArchive ); IArchiveEntry AddDirectoryEntry(string key, DateTime? modified = null); +} + +public interface IWritableArchive : IArchive, IWritableArchiveCommon +{ void SaveTo(Stream stream, WriterOptions options); +} + +public interface IWritableAsyncArchive : IAsyncArchive, IWritableArchiveCommon +{ ValueTask SaveToAsync( Stream stream, WriterOptions options, CancellationToken cancellationToken = default ); - - /// - /// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended. - /// - /// IDisposeable to resume entry rebuilding - IDisposable PauseEntryRebuilding(); } diff --git a/src/SharpCompress/Archives/IWritableArchiveCommonExtensions.cs b/src/SharpCompress/Archives/IWritableArchiveCommonExtensions.cs new file mode 100644 index 00000000..2fa86ee3 --- /dev/null +++ b/src/SharpCompress/Archives/IWritableArchiveCommonExtensions.cs @@ -0,0 +1,75 @@ +using System; +using System.IO; + +namespace SharpCompress.Archives; + +public static class IWritableArchiveCommonExtensions +{ + public static void AddEntry( + this IWritableArchive writableArchive, + string entryPath, + string filePath + ) + { + var fileInfo = new FileInfo(filePath); + if (!fileInfo.Exists) + { + throw new FileNotFoundException("Could not AddEntry: " + filePath); + } + writableArchive.AddEntry( + entryPath, + new FileInfo(filePath).OpenRead(), + true, + fileInfo.Length, + fileInfo.LastWriteTime + ); + } + + public static void AddAllFromDirectory( + this IWritableArchiveCommon writableArchive, + string filePath, + string searchPattern = "*.*", + SearchOption searchOption = SearchOption.AllDirectories + ) + { + using (writableArchive.PauseEntryRebuilding()) + { + foreach (var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption)) + { + var fileInfo = new FileInfo(path); + writableArchive.AddEntry( + path.Substring(filePath.Length), + fileInfo.OpenRead(), + true, + fileInfo.Length, + fileInfo.LastWriteTime + ); + } + } + } + + public static IArchiveEntry AddEntry( + this IWritableArchiveCommon writableArchive, + string key, + string file + ) => AddEntry(writableArchive, key, new FileInfo(file)); + + public static IArchiveEntry AddEntry( + this IWritableArchiveCommon writableArchive, + string key, + FileInfo fileInfo + ) + { + if (!fileInfo.Exists) + { + throw new ArgumentException("FileInfo does not exist."); + } + return writableArchive.AddEntry( + key, + fileInfo.OpenRead(), + true, + fileInfo.Length, + fileInfo.LastWriteTime + ); + } +} diff --git a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs index 60ec83d8..07b642ae 100644 --- a/src/SharpCompress/Archives/IWritableArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IWritableArchiveExtensions.cs @@ -1,106 +1,25 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; +using System.IO; +using SharpCompress.Common; using SharpCompress.Writers; namespace SharpCompress.Archives; public static class IWritableArchiveExtensions { - public static void AddEntry( - this IWritableArchive writableArchive, - string entryPath, - string filePath - ) - { - var fileInfo = new FileInfo(filePath); - if (!fileInfo.Exists) - { - throw new FileNotFoundException("Could not AddEntry: " + filePath); - } - writableArchive.AddEntry( - entryPath, - new FileInfo(filePath).OpenRead(), - true, - fileInfo.Length, - fileInfo.LastWriteTime - ); - } public static void SaveTo( this IWritableArchive writableArchive, string filePath, - WriterOptions options - ) => writableArchive.SaveTo(new FileInfo(filePath), options); + WriterOptions? options = null + ) => writableArchive.SaveTo(new FileInfo(filePath), options ?? new (CompressionType.Deflate)); public static void SaveTo( this IWritableArchive writableArchive, FileInfo fileInfo, - WriterOptions options + WriterOptions? options = null ) { using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write); - writableArchive.SaveTo(stream, options); - } - - public static ValueTask SaveToAsync( - this IWritableArchive writableArchive, - string filePath, - WriterOptions options, - CancellationToken cancellationToken = default - ) => writableArchive.SaveToAsync(new FileInfo(filePath), options, cancellationToken); - - public static async ValueTask SaveToAsync( - this IWritableArchive writableArchive, - FileInfo fileInfo, - WriterOptions options, - CancellationToken cancellationToken = default - ) - { - using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write); - await writableArchive.SaveToAsync(stream, options, cancellationToken).ConfigureAwait(false); - } - - public static void AddAllFromDirectory( - this IWritableArchive writableArchive, - string filePath, - string searchPattern = "*.*", - SearchOption searchOption = SearchOption.AllDirectories - ) - { - using (writableArchive.PauseEntryRebuilding()) - { - foreach (var path in Directory.EnumerateFiles(filePath, searchPattern, searchOption)) - { - var fileInfo = new FileInfo(path); - writableArchive.AddEntry( - path.Substring(filePath.Length), - fileInfo.OpenRead(), - true, - fileInfo.Length, - fileInfo.LastWriteTime - ); - } - } - } - - public static IArchiveEntry AddEntry( - this IWritableArchive writableArchive, - string key, - FileInfo fileInfo - ) - { - if (!fileInfo.Exists) - { - throw new ArgumentException("FileInfo does not exist."); - } - return writableArchive.AddEntry( - key, - fileInfo.OpenRead(), - true, - fileInfo.Length, - fileInfo.LastWriteTime - ); + writableArchive.SaveTo(stream, options?? new (CompressionType.Deflate)); } } diff --git a/src/SharpCompress/Archives/IWritableAsyncArchiveExtensions.cs b/src/SharpCompress/Archives/IWritableAsyncArchiveExtensions.cs new file mode 100644 index 00000000..ec43872c --- /dev/null +++ b/src/SharpCompress/Archives/IWritableAsyncArchiveExtensions.cs @@ -0,0 +1,29 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using SharpCompress.Common; +using SharpCompress.Writers; + +namespace SharpCompress.Archives; + +public static class IWritableAsyncArchiveExtensions +{ + public static ValueTask SaveToAsync( + this IWritableAsyncArchive writableArchive, + string filePath, + WriterOptions? options = null, + CancellationToken cancellationToken = default + ) => writableArchive.SaveToAsync(new FileInfo(filePath), options ?? new (CompressionType.Deflate), cancellationToken); + + public static async ValueTask SaveToAsync( + this IWritableAsyncArchive writableArchive, + FileInfo fileInfo, + WriterOptions? options = null, + CancellationToken cancellationToken = default + ) + { + using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write); + await writableArchive.SaveToAsync(stream, options?? new (CompressionType.Deflate), cancellationToken).ConfigureAwait(false); + } + +} diff --git a/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs b/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs index bf6f0cd4..ff19275f 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.Extensions.cs @@ -1,4 +1,6 @@ using System.Linq; +using System.Threading.Tasks; +using SharpCompress.Common.Rar; namespace SharpCompress.Archives.Rar; @@ -7,12 +9,25 @@ public static class RarArchiveExtensions /// /// RarArchive is the first volume of a multi-part archive. If MultipartVolume is true and IsFirstVolume is false then the first volume file must be missing. /// - public static bool IsFirstVolume(this RarArchive archive) => - archive.Volumes.First().IsFirstVolume; + public static bool IsFirstVolume(this IRarArchive archive) => + archive.Volumes.Cast().First().IsFirstVolume; /// /// RarArchive is part of a multi-part archive. /// - public static bool IsMultipartVolume(this RarArchive archive) => - archive.Volumes.First().IsMultiVolume; + public static bool IsMultipartVolume(this IRarArchive archive) => + archive.Volumes.Cast().First().IsMultiVolume; + + + /// + /// RarArchive is the first volume of a multi-part archive. If MultipartVolume is true and IsFirstVolume is false then the first volume file must be missing. + /// + public static async ValueTask IsFirstVolumeAsync(this IRarAsyncArchive archive) => + (await archive.VolumesAsync.CastAsync().FirstAsync()).IsFirstVolume; + + /// + /// RarArchive is part of a multi-part archive. + /// + public static async ValueTask IsMultipartVolumeAsync(this IRarAsyncArchive archive) => + (await archive.VolumesAsync.CastAsync().FirstAsync()).IsMultiVolume; } diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index 1cb7b00a..77568e86 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -14,7 +14,23 @@ using SharpCompress.Readers.Rar; namespace SharpCompress.Archives.Rar; -public class RarArchive : AbstractArchive +public interface IRarArchiveCommon +{ + + + int MinVersion { get; } + int MaxVersion { get; } +} + +public interface IRarArchive : IArchive, IRarArchiveCommon +{ +} + +public interface IRarAsyncArchive : IAsyncArchive, IRarArchiveCommon +{ +} + +public class RarArchive : AbstractArchive, IRarArchive { private bool _disposed; internal Lazy UnpackV2017 { get; } = @@ -103,7 +119,7 @@ public class RarArchive : AbstractArchive /// /// /// - public static IArchive Open(string filePath, ReaderOptions? options = null) + public static IRarArchive Open(string filePath, ReaderOptions? options = null) { filePath.NotNullOrEmpty(nameof(filePath)); var fileInfo = new FileInfo(filePath); @@ -121,7 +137,7 @@ public class RarArchive : AbstractArchive /// /// /// - public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null) + public static IRarArchive Open(FileInfo fileInfo, ReaderOptions? options = null) { fileInfo.NotNull(nameof(fileInfo)); return new RarArchive( @@ -138,7 +154,7 @@ public class RarArchive : AbstractArchive /// /// /// - public static IArchive Open(Stream stream, ReaderOptions? options = null) + public static IRarArchive Open(Stream stream, ReaderOptions? options = null) { stream.NotNull(nameof(stream)); @@ -155,7 +171,7 @@ public class RarArchive : AbstractArchive /// /// /// - public static IArchive Open( + public static IRarArchive Open( IEnumerable fileInfos, ReaderOptions? readerOptions = null ) @@ -176,7 +192,7 @@ public class RarArchive : AbstractArchive /// /// /// - public static IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + public static IRarArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { streams.NotNull(nameof(streams)); var strms = streams.ToArray(); @@ -195,14 +211,14 @@ public class RarArchive : AbstractArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IRarAsyncArchive OpenAsync( Stream stream, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(stream, readerOptions); + return (IRarAsyncArchive)Open(stream, readerOptions); } /// @@ -211,14 +227,14 @@ public class RarArchive : AbstractArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IRarAsyncArchive OpenAsync( FileInfo fileInfo, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(fileInfo, readerOptions); + return (IRarAsyncArchive)Open(fileInfo, readerOptions); } /// @@ -227,14 +243,14 @@ public class RarArchive : AbstractArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IRarAsyncArchive OpenAsync( IReadOnlyList streams, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(streams, readerOptions); + return (IRarAsyncArchive)Open(streams, readerOptions); } /// @@ -243,14 +259,14 @@ public class RarArchive : AbstractArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IRarAsyncArchive OpenAsync( IReadOnlyList fileInfos, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(fileInfos, readerOptions); + return (IRarAsyncArchive)Open(fileInfos, readerOptions); } public static bool IsRarFile(string filePath) => IsRarFile(new FileInfo(filePath)); diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs index 76d47d0b..b1d19bda 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.cs @@ -22,7 +22,7 @@ public class TarArchive : AbstractWritableArchive /// /// /// - public static IArchive Open(string filePath, ReaderOptions? readerOptions = null) + public static IWritableArchive Open(string filePath, ReaderOptions? readerOptions = null) { filePath.NotNullOrEmpty(nameof(filePath)); return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); @@ -33,7 +33,7 @@ public class TarArchive : AbstractWritableArchive /// /// /// - public static IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + public static IWritableArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) { fileInfo.NotNull(nameof(fileInfo)); return new TarArchive( @@ -50,7 +50,7 @@ public class TarArchive : AbstractWritableArchive /// /// /// - public static IArchive Open( + public static IWritableArchive Open( IEnumerable fileInfos, ReaderOptions? readerOptions = null ) @@ -71,7 +71,7 @@ public class TarArchive : AbstractWritableArchive /// /// /// - public static IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + public static IWritableArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { streams.NotNull(nameof(streams)); var strms = streams.ToArray(); @@ -89,7 +89,7 @@ public class TarArchive : AbstractWritableArchive /// /// /// - public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + public static IWritableArchive Open(Stream stream, ReaderOptions? readerOptions = null) { stream.NotNull(nameof(stream)); @@ -109,14 +109,24 @@ public class TarArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( Stream stream, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(stream, readerOptions); + return (IWritableAsyncArchive)Open(stream, readerOptions); + } + + public static IWritableAsyncArchive OpenAsync( + string file, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return (IWritableAsyncArchive)Open(new FileInfo(file), readerOptions); } /// @@ -125,14 +135,14 @@ public class TarArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( FileInfo fileInfo, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(fileInfo, readerOptions); + return (IWritableAsyncArchive)Open(fileInfo, readerOptions); } /// @@ -141,14 +151,14 @@ public class TarArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( IReadOnlyList streams, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(streams, readerOptions); + return (IWritableAsyncArchive)Open(streams, readerOptions); } /// @@ -157,14 +167,14 @@ public class TarArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( IReadOnlyList fileInfos, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(fileInfos, readerOptions); + return (IWritableAsyncArchive)Open(fileInfos, readerOptions); } public static bool IsTarFile(string filePath) => IsTarFile(new FileInfo(filePath)); diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs index 48786117..d23925b5 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchive.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs @@ -43,7 +43,7 @@ public class ZipArchive : AbstractWritableArchive /// /// /// - public static IArchive Open(string filePath, ReaderOptions? readerOptions = null) + public static IWritableArchive Open(string filePath, ReaderOptions? readerOptions = null) { filePath.NotNullOrEmpty(nameof(filePath)); return Open(new FileInfo(filePath), readerOptions ?? new ReaderOptions()); @@ -54,7 +54,7 @@ public class ZipArchive : AbstractWritableArchive /// /// /// - public static IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) + public static IWritableArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) { fileInfo.NotNull(nameof(fileInfo)); return new ZipArchive( @@ -71,7 +71,7 @@ public class ZipArchive : AbstractWritableArchive /// /// /// - public static IArchive Open( + public static IWritableArchive Open( IEnumerable fileInfos, ReaderOptions? readerOptions = null ) @@ -92,7 +92,7 @@ public class ZipArchive : AbstractWritableArchive /// /// /// - public static IArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) + public static IWritableArchive Open(IEnumerable streams, ReaderOptions? readerOptions = null) { streams.NotNull(nameof(streams)); var strms = streams.ToArray(); @@ -110,7 +110,7 @@ public class ZipArchive : AbstractWritableArchive /// /// /// - public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null) + public static IWritableArchive Open(Stream stream, ReaderOptions? readerOptions = null) { stream.NotNull(nameof(stream)); @@ -124,14 +124,14 @@ public class ZipArchive : AbstractWritableArchive ); } - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( string path, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(path, readerOptions); + return (IWritableAsyncArchive)Open(path, readerOptions); } /// @@ -140,14 +140,14 @@ public class ZipArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( Stream stream, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(stream, readerOptions); + return (IWritableAsyncArchive)Open(stream, readerOptions); } /// @@ -156,14 +156,14 @@ public class ZipArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( FileInfo fileInfo, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(fileInfo, readerOptions); + return (IWritableAsyncArchive)Open(fileInfo, readerOptions); } /// @@ -172,14 +172,14 @@ public class ZipArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( IReadOnlyList streams, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(streams, readerOptions); + return (IWritableAsyncArchive)Open(streams, readerOptions); } /// @@ -188,14 +188,14 @@ public class ZipArchive : AbstractWritableArchive /// /// /// - public static IAsyncArchive OpenAsync( + public static IWritableAsyncArchive OpenAsync( IReadOnlyList fileInfos, ReaderOptions? readerOptions = null, CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); - return (IAsyncArchive)Open(fileInfos, readerOptions); + return (IWritableAsyncArchive)Open(fileInfos, readerOptions); } public static bool IsZipFile( diff --git a/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs b/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs index 7ce90ded..5e8be6ec 100644 --- a/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs +++ b/src/SharpCompress/Polyfills/AsyncEnumerableExtensions.cs @@ -116,6 +116,24 @@ public static class AsyncEnumerableExtensions } } + public static async ValueTask SingleAsync( + this IAsyncEnumerable source, + Func predicate + ) + { + var enumerator = source.WhereAsync(predicate).GetAsyncEnumerator(); + if (!await enumerator.MoveNextAsync()) + { + throw new InvalidOperationException("The source sequence is empty."); + } + var value = enumerator.Current; + if (await enumerator.MoveNextAsync()) + { + throw new InvalidOperationException("The source sequence contains more than one element."); + } + return value; + } + public static async ValueTask FirstAsync(this IAsyncEnumerable source) { await foreach (var item in source) diff --git a/src/SharpCompress/packages.lock.json b/src/SharpCompress/packages.lock.json index 032c15c4..41325333 100644 --- a/src/SharpCompress/packages.lock.json +++ b/src/SharpCompress/packages.lock.json @@ -216,9 +216,9 @@ "net10.0": { "Microsoft.NET.ILLink.Tasks": { "type": "Direct", - "requested": "[10.0.1, )", - "resolved": "10.0.1", - "contentHash": "ISahzLHsHY7vrwqr2p1YWZ+gsxoBRtH7gWRDK8fDUst9pp2He0GiesaqEfeX0V8QMCJM3eNEHGGpnIcPjFo2NQ==" + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "kICGrGYEzCNI3wPzfEXcwNHgTvlvVn9yJDhSdRK+oZQy4jvYH529u7O0xf5ocQKzOMjfS07+3z9PKRIjrFMJDA==" }, "Microsoft.NETFramework.ReferenceAssemblies": { "type": "Direct", diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index 3001b4bd..cdd98a33 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -601,7 +601,7 @@ public class ArchiveTests : ReaderTests ) ) await using ( - var archive = await archiveFactory.OpenAsync( + var archive = archiveFactory.OpenAsync( new AsyncOnlyStream(stream), readerOptions ) diff --git a/tests/SharpCompress.Test/GZip/GZipArchiveAsyncTests.cs b/tests/SharpCompress.Test/GZip/GZipArchiveAsyncTests.cs index 6b27aa50..ba74de93 100644 --- a/tests/SharpCompress.Test/GZip/GZipArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipArchiveAsyncTests.cs @@ -48,8 +48,7 @@ public class GZipArchiveAsyncTests : ArchiveTests await using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"))) #endif { - IAsyncArchive archive = await GZipArchive.OpenAsync(stream); - try + await using (var archive = GZipArchive.OpenAsync(stream)) { var entry = await archive.EntriesAsync.FirstAsync(); await entry.WriteToFileAsync(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull())); @@ -61,10 +60,6 @@ public class GZipArchiveAsyncTests : ArchiveTests Assert.Equal(size, scratch.Length); Assert.Equal(size, test.Length); } - finally - { - archive.DisposeAsync().AsTask().Wait(); - } } CompareArchivesByPath( Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"), @@ -81,18 +76,12 @@ public class GZipArchiveAsyncTests : ArchiveTests #else await using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); #endif - IAsyncArchive archive = await GZipArchive.OpenAsync(stream); - try + await using (var archive = GZipArchive.OpenAsync(stream)) { - await Assert.ThrowsAsync(() => - archive.AddEntry("jpg\\test.jpg", jpg) - ); + Assert.Throws(() => + archive.AddEntry("jpg\\test.jpg", jpg)); await archive.SaveToAsync(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz")); } - finally - { - await archive.DisposeAsync(); - } } [Fact] diff --git a/tests/SharpCompress.Test/Rar/RarReaderAsyncTests.cs b/tests/SharpCompress.Test/Rar/RarReaderAsyncTests.cs index 0de82da5..fe8d3b25 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderAsyncTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderAsyncTests.cs @@ -36,59 +36,61 @@ public class RarReaderAsyncTests : ReaderTests "Rar5.multi.part06.rar", ]); - private async ValueTask DoRar_Multi_Reader_Async(string[] archives) - { - using ( - var reader = RarReader.Open( - archives - .Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) - .Select(p => File.OpenRead(p)) - ) - ) - { - while (await reader.MoveToNextEntryAsync()) - { - await reader.WriteEntryToDirectoryAsync( - SCRATCH_FILES_PATH, - new ExtractionOptions { ExtractFullPath = true, Overwrite = true } - ); - } - } - VerifyFiles(); - } + private async ValueTask DoRar_Multi_Reader_Async(string[] archives) + { + using ( + IReader baseReader = RarReader.Open( + archives + .Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) + .Select(p => File.OpenRead(p)) + ) + ) + { + IAsyncReader reader = (IAsyncReader)baseReader; + while (await reader.MoveToNextEntryAsync()) + { + await reader.WriteEntryToDirectoryAsync( + SCRATCH_FILES_PATH, + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } + ); + } + } + VerifyFiles(); + } - [Fact] - public async ValueTask Rar_Multi_Reader_Encrypted_Async() => - await Assert.ThrowsAsync(async () => - { - string[] archives = - [ - "Rar.EncryptedParts.part01.rar", - "Rar.EncryptedParts.part02.rar", - "Rar.EncryptedParts.part03.rar", - "Rar.EncryptedParts.part04.rar", - "Rar.EncryptedParts.part05.rar", - "Rar.EncryptedParts.part06.rar", - ]; - using ( - var reader = RarReader.Open( - archives - .Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) - .Select(p => File.OpenRead(p)), - new ReaderOptions { Password = "test" } - ) - ) - { - while (await reader.MoveToNextEntryAsync()) - { - await reader.WriteEntryToDirectoryAsync( - SCRATCH_FILES_PATH, - new ExtractionOptions { ExtractFullPath = true, Overwrite = true } - ); - } - } - VerifyFiles(); - }); + [Fact] + public async ValueTask Rar_Multi_Reader_Encrypted_Async() => + await Assert.ThrowsAsync(async () => + { + string[] archives = + [ + "Rar.EncryptedParts.part01.rar", + "Rar.EncryptedParts.part02.rar", + "Rar.EncryptedParts.part03.rar", + "Rar.EncryptedParts.part04.rar", + "Rar.EncryptedParts.part05.rar", + "Rar.EncryptedParts.part06.rar", + ]; + using ( + IReader baseReader = RarReader.Open( + archives + .Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) + .Select(p => File.OpenRead(p)), + new ReaderOptions { Password = "test" } + ) + ) + { + IAsyncReader reader = (IAsyncReader)baseReader; + while (await reader.MoveToNextEntryAsync()) + { + await reader.WriteEntryToDirectoryAsync( + SCRATCH_FILES_PATH, + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } + ); + } + } + VerifyFiles(); + }); [Fact] public async ValueTask Rar_Multi_Reader_Delete_Files_Async() => @@ -121,30 +123,31 @@ public class RarReaderAsyncTests : ReaderTests Path.Combine(SCRATCH2_FILES_PATH, file) ); } - var streams = archives - .Select(s => Path.Combine(SCRATCH2_FILES_PATH, s)) - .Select(File.OpenRead) - .ToList(); - using (var reader = RarReader.Open(streams)) - { - while (await reader.MoveToNextEntryAsync()) - { - await reader.WriteEntryToDirectoryAsync( - SCRATCH_FILES_PATH, - new ExtractionOptions { ExtractFullPath = true, Overwrite = true } - ); - } - } - foreach (var stream in streams) - { - stream.Dispose(); - } - VerifyFiles(); + var streams = archives + .Select(s => Path.Combine(SCRATCH2_FILES_PATH, s)) + .Select(File.OpenRead) + .ToList(); + using (IReader baseReader = RarReader.Open(streams)) + { + IAsyncReader reader = (IAsyncReader)baseReader; + while (await reader.MoveToNextEntryAsync()) + { + await reader.WriteEntryToDirectoryAsync( + SCRATCH_FILES_PATH, + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } + ); + } + } + foreach (var stream in streams) + { + stream.Dispose(); + } + VerifyFiles(); - foreach (var file in archives.Select(s => Path.Combine(SCRATCH2_FILES_PATH, s))) - { - File.Delete(file); - } + foreach (var file in archives.Select(s => Path.Combine(SCRATCH2_FILES_PATH, s))) + { + File.Delete(file); + } } [Fact] @@ -271,23 +274,24 @@ public class RarReaderAsyncTests : ReaderTests ); } - [Fact] - public async ValueTask Rar_Jpg_Reader_Async() - { - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"))) - using (var reader = RarReader.Open(stream, new ReaderOptions { LookForHeader = true })) - { - while (await reader.MoveToNextEntryAsync()) - { - Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); - await reader.WriteEntryToDirectoryAsync( - SCRATCH_FILES_PATH, - new ExtractionOptions { ExtractFullPath = true, Overwrite = true } - ); - } - } - VerifyFiles(); - } + [Fact] + public async ValueTask Rar_Jpg_Reader_Async() + { + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"))) + using (IReader baseReader = RarReader.Open(stream, new ReaderOptions { LookForHeader = true })) + { + IAsyncReader reader = (IAsyncReader)baseReader; + while (await reader.MoveToNextEntryAsync()) + { + Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); + await reader.WriteEntryToDirectoryAsync( + SCRATCH_FILES_PATH, + new ExtractionOptions { ExtractFullPath = true, Overwrite = true } + ); + } + } + VerifyFiles(); + } [Fact] public async ValueTask Rar_Solid_Reader_Async() => diff --git a/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs index 9fae1c58..d140c788 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveAsyncTests.cs @@ -47,7 +47,7 @@ public class TarArchiveAsyncTests : ArchiveTests var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive); using (var archive2 = TarArchive.Open(unmodified)) { - Assert.Equal(1, archive2.Entries.Count); + Assert.Equal(1, archive2.Entries.Count()); Assert.Contains(filename, archive2.Entries.Select(entry => entry.Key)); foreach (var entry in archive2.Entries) @@ -91,7 +91,7 @@ public class TarArchiveAsyncTests : ArchiveTests var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive); using (var archive2 = TarArchive.Open(unmodified)) { - Assert.Equal(1, archive2.Entries.Count); + Assert.Equal(1, archive2.Entries.Count()); Assert.Contains(longFilename, archive2.Entries.Select(entry => entry.Key)); foreach (var entry in archive2.Entries) @@ -128,7 +128,7 @@ public class TarArchiveAsyncTests : ArchiveTests var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar"); var modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar"); - using (var archive = TarArchive.Open(unmodified)) + await using (var archive = TarArchive.OpenAsync(unmodified)) { archive.AddEntry("jpg\\test.jpg", jpg); await archive.SaveToAsync(scratchPath, new WriterOptions(CompressionType.None)); @@ -143,9 +143,9 @@ public class TarArchiveAsyncTests : ArchiveTests var modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar"); var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar"); - using (var archive = TarArchive.Open(unmodified)) + await using (var archive = TarArchive.OpenAsync(unmodified)) { - var entry = archive.Entries.Single(x => + var entry = await archive.EntriesAsync.SingleAsync(x => x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ); archive.RemoveEntry(entry); diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index efbde472..0f92681c 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -49,7 +49,7 @@ public class TarArchiveTests : ArchiveTests var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive); using (var archive2 = TarArchive.Open(unmodified)) { - Assert.Equal(1, archive2.Entries.Count); + Assert.Equal(1, archive2.Entries.Count()); Assert.Contains(filename, archive2.Entries.Select(entry => entry.Key)); foreach (var entry in archive2.Entries) @@ -67,7 +67,7 @@ public class TarArchiveTests : ArchiveTests { var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "very long filename.tar"); using var archive = TarArchive.Open(unmodified); - Assert.Equal(5, archive.Entries.Count); + Assert.Equal(5, archive.Entries.Count()); Assert.Contains("very long filename/", archive.Entries.Select(entry => entry.Key)); Assert.Contains( "very long filename/very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename.jpg", @@ -109,7 +109,7 @@ public class TarArchiveTests : ArchiveTests var unmodified = Path.Combine(SCRATCH2_FILES_PATH, archive); using (var archive2 = TarArchive.Open(unmodified)) { - Assert.Equal(1, archive2.Entries.Count); + Assert.Equal(1, archive2.Entries.Count()); Assert.Contains(longFilename, archive2.Entries.Select(entry => entry.Key)); foreach (var entry in archive2.Entries) @@ -127,7 +127,7 @@ public class TarArchiveTests : ArchiveTests { var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "ustar with long names.tar"); using var archive = TarArchive.Open(unmodified); - Assert.Equal(6, archive.Entries.Count); + Assert.Equal(6, archive.Entries.Count()); Assert.Contains("Directory/", archive.Entries.Select(entry => entry.Key)); Assert.Contains( "Directory/Some file with veeeeeeeeeery loooooooooong name", diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs index ffec6efc..ecc1c9c8 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveAsyncTests.cs @@ -126,10 +126,9 @@ public class ZipArchiveAsyncTests : ArchiveTests var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); var modified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod.zip"); - using (IArchive baseArchive = ZipArchive.Open(unmodified)) + await using (var archive = ZipArchive.OpenAsync(unmodified)) { - IWritableArchive archive = (IWritableArchive)baseArchive; - var entry = archive.Entries.Single(x => + var entry = await archive.EntriesAsync.SingleAsync(x => x.Key.NotNull().EndsWith("jpg", StringComparison.OrdinalIgnoreCase) ); archive.RemoveEntry(entry); @@ -150,9 +149,8 @@ public class ZipArchiveAsyncTests : ArchiveTests var unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod.zip"); var modified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.noEmptyDirs.zip"); - using (IArchive baseArchive = ZipArchive.Open(unmodified)) + await using (var archive = ZipArchive.OpenAsync(unmodified)) { - IWritableArchive archive = (IWritableArchive)baseArchive; archive.AddEntry("jpg\\test.jpg", jpg); WriterOptions writerOptions = new ZipWriterOptions(CompressionType.Deflate); diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index 067e9953..a4e4712d 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -495,7 +495,7 @@ public class ZipArchiveTests : ArchiveTests new ReaderOptions { Password = "test" } ); var isComplete = reader.IsComplete; - Assert.Equal(1, reader.Volumes.Count); + Assert.Equal(1, reader.Volumes.Count()); var expectedComment = "Encoding:utf-8 || Compression:Deflate levelDefault || Encrypt:None || ZIP64:Always\r\nCreated at 2017-Jan-23 14:10:43 || DotNetZip Tool v1.9.1.8\r\nTest zip64 archive"; @@ -708,7 +708,7 @@ public class ZipArchiveTests : ArchiveTests var zipPath = Path.Combine(TEST_ARCHIVES_PATH, "Zip.LongComment.zip"); using var za = ZipArchive.Open(zipPath); - var count = za.Entries.Count; + var count = za.Entries.Count(); Assert.Equal(1, count); } diff --git a/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs index 9d5f4da9..7c2d85aa 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderAsyncTests.cs @@ -121,8 +121,9 @@ public class ZipReaderAsyncTests : ReaderTests using ( Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.bzip2.pkware.zip")) ) - using (var reader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) + using (IReader baseReader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) { + IAsyncReader reader = (IAsyncReader)baseReader; while (await reader.MoveToNextEntryAsync()) { if (!reader.Entry.IsDirectory) @@ -191,8 +192,11 @@ public class ZipReaderAsyncTests : ReaderTests File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.WinzipAES.zip")) ) ) - using (var reader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) + using ( + IReader baseReader = ZipReader.Open(stream, new ReaderOptions { Password = "test" }) + ) { + IAsyncReader reader = (IAsyncReader)baseReader; while (await reader.MoveToNextEntryAsync()) { if (!reader.Entry.IsDirectory) @@ -216,8 +220,9 @@ public class ZipReaderAsyncTests : ReaderTests File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES.zip")) ) ) - using (var reader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) + using (IReader baseReader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) { + IAsyncReader reader = (IAsyncReader)baseReader; while (await reader.MoveToNextEntryAsync()) { if (!reader.Entry.IsDirectory) @@ -242,8 +247,9 @@ public class ZipReaderAsyncTests : ReaderTests File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "zipcrypto.zip")) ) ) - using (var reader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) + using (IReader baseReader = ZipReader.Open(stream, new ReaderOptions { Password = "test" })) { + IAsyncReader reader = (IAsyncReader)baseReader; while (await reader.MoveToNextEntryAsync()) { if (!reader.Entry.IsDirectory)