2using System.Collections.Generic;
4using System.IO.Abstractions;
5using System.IO.Compression;
8using System.Threading.Tasks;
62 cancellationToken.ThrowIfCancellationRequested();
65 if (!dir.Attributes.HasFlag(FileAttributes.Directory) || dir.Attributes.HasFlag(FileAttributes.ReparsePoint))
71 List<Exception>? exceptions =
null;
72 foreach (var subDir
in dir.EnumerateDirectories())
77 catch (AggregateException ex)
79 exceptions ??=
new List<Exception>();
80 exceptions.AddRange(ex.InnerExceptions);
83 foreach (var file
in dir.EnumerateFiles())
85 cancellationToken.ThrowIfCancellationRequested();
88 file.Attributes = FileAttributes.Normal;
93 exceptions ??=
new List<Exception>();
98 cancellationToken.ThrowIfCancellationRequested();
105 exceptions ??=
new List<Exception>();
109 if (exceptions !=
null)
110 throw new AggregateException(exceptions);
115 IEnumerable<string>? ignore,
116 Func<string, string, ValueTask>? postCopyCallback,
120 CancellationToken cancellationToken)
122 ArgumentNullException.ThrowIfNull(src);
123 ArgumentNullException.ThrowIfNull(src);
125 if (taskThrottle.HasValue && taskThrottle < 1)
126 throw new ArgumentOutOfRangeException(nameof(taskThrottle), taskThrottle,
"taskThrottle must be at least 1!");
131 using var semaphore = taskThrottle.HasValue ?
new SemaphoreSlim(taskThrottle.Value) :
null;
132 await Task.WhenAll(
CopyDirectoryImpl(src, dest, ignore, postCopyCallback, semaphore, cancellationToken));
139 public async ValueTask
CopyFile(
string src,
string dest, CancellationToken cancellationToken)
141 ArgumentNullException.ThrowIfNull(src);
142 ArgumentNullException.ThrowIfNull(dest);
145 await
using var srcStream =
fileSystem.FileStream.New(
149 FileShare.Read | FileShare.Delete,
151 FileOptions.Asynchronous | FileOptions.SequentialScan);
155 await srcStream.CopyToAsync(destStream, 81920, cancellationToken);
163 => Task.Factory.StartNew(
173 TaskScheduler.Current);
186 ??
throw new InvalidOperationException($
"Null was returned. Path ({path}) must be rooted. This is not supported!");
189 public string GetFileName(
string path) =>
fileSystem.Path.GetFileName(path ??
throw new ArgumentNullException(nameof(path)));
195 public Task<List<string>>
GetFilesWithExtension(
string path,
string extension,
bool recursive, CancellationToken cancellationToken) => Task.Factory.StartNew(
199 ArgumentNullException.ThrowIfNull(extension);
200 var results =
new List<string>();
201 foreach (var fileName
in fileSystem.Directory.EnumerateFiles(
204 recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
206 cancellationToken.ThrowIfCancellationRequested();
207 results.Add(fileName);
214 TaskScheduler.Current);
217 public Task
MoveFile(
string source,
string destination, CancellationToken cancellationToken) => Task.Factory.StartNew(
220 ArgumentNullException.ThrowIfNull(destination);
221 source =
ResolvePath(source ??
throw new ArgumentNullException(nameof(source)));
227 TaskScheduler.Current);
230 public Task
MoveDirectory(
string source,
string destination, CancellationToken cancellationToken) => Task.Factory.StartNew(
233 ArgumentNullException.ThrowIfNull(destination);
234 source =
ResolvePath(source ??
throw new ArgumentNullException(nameof(source)));
236 fileSystem.Directory.Move(source, destination);
240 TaskScheduler.Current);
243 public async ValueTask<byte[]>
ReadAllBytes(
string path, CancellationToken cancellationToken)
247 buf =
new byte[file.Length];
248 await file.ReadAsync(buf, cancellationToken);
259 if (
fileSystem.Path.IsPathRooted(path ??
throw new ArgumentNullException(nameof(path))))
271 public async ValueTask
WriteAllBytes(
string path,
byte[] contents, CancellationToken cancellationToken)
274 await file.WriteAsync(contents, cancellationToken);
285 FileShare.Read | FileShare.Delete,
287 FileOptions.Asynchronous | FileOptions.SequentialScan);
298 FileShare.ReadWrite | FileShare.Delete | (shareWrite ? FileShare.Write : FileShare.None),
301 ? FileOptions.Asynchronous | FileOptions.SequentialScan
302 : FileOptions.Asynchronous);
306 public Task<IReadOnlyList<string>>
GetDirectories(
string path, CancellationToken cancellationToken) => Task.Factory.StartNew(
310 var results =
new List<string>();
311 cancellationToken.ThrowIfCancellationRequested();
312 foreach (var directoryName
in fileSystem.Directory.EnumerateDirectories(path))
314 results.Add(directoryName);
315 cancellationToken.ThrowIfCancellationRequested();
318 return (IReadOnlyList<string>)results;
322 TaskScheduler.Current);
325 public Task<IReadOnlyList<string>>
GetFiles(
string path, CancellationToken cancellationToken) => Task.Factory.StartNew(
329 var results =
new List<string>();
330 cancellationToken.ThrowIfCancellationRequested();
331 foreach (var fileName
in fileSystem.Directory.EnumerateFiles(path))
333 results.Add(fileName);
334 cancellationToken.ThrowIfCancellationRequested();
337 return (IReadOnlyList<string>)results;
341 TaskScheduler.Current);
347 ArgumentNullException.ThrowIfNull(zipFile);
350#error Check if zip file seeking has been addressesed. See https:
354 if (!zipFile.CanSeek)
355 throw new ArgumentException(
"Stream does not support seeking!", nameof(zipFile));
357 using var archive =
new ZipArchive(zipFile, ZipArchiveMode.Read,
true);
361 foreach (var entry
in archive.Entries)
363 var entryPath =
fileSystem.Path.Combine(path, entry.FullName);
365 if (
string.IsNullOrEmpty(entry.Name))
367 fileSystem.Directory.CreateDirectory(entryPath);
371 var directoryPath =
fileSystem.Path.GetDirectoryName(entryPath);
372 if (directoryPath ==
null)
374 throw new JobException(
"Zip archive concatenation resulted in a null directory path!");
377 fileSystem.Directory.CreateDirectory(directoryPath);
379 using var entryStream = entry.Open();
380 using var outputStream =
fileSystem.File.Create(entryPath);
381 await entryStream.CopyToAsync(outputStream, cancellationToken);
393 ??
throw new ArgumentNullException(nameof(path));
396 public Task<DateTimeOffset>
GetLastModified(
string path, CancellationToken cancellationToken) => Task.Factory.StartNew(
399 path =
ResolvePath(path ??
throw new ArgumentNullException(nameof(path)));
401 return new DateTimeOffset(fileInfo.LastWriteTimeUtc);
405 TaskScheduler.Current);
408 public Task<bool>
PathIsChildOf(
string parentPath,
string childPath, CancellationToken cancellationToken) => Task.Factory.StartNew(
414 if (parentPath == childPath)
418 var di1 =
fileSystem.DirectoryInfo.New(parentPath);
419 var di2 =
fileSystem.DirectoryInfo.New(childPath);
420 while (di2.Parent !=
null)
422 if (di2.Parent.FullName == di1.FullName)
432 TaskScheduler.Current);
435 public Task<IDirectoryInfo>
DirectoryInfo(
string path, CancellationToken cancellationToken)
436 => Task.Factory.StartNew(
440 TaskScheduler.Current);
449 ArgumentNullException.ThrowIfNull(subdirectoryPath);
451 if (!
fileSystem.Path.IsPathRooted(subdirectoryPath))
467 =>
fileSystem.Path.GetFullPath(path ??
throw new ArgumentNullException(nameof(path)));
482 IEnumerable<string>? ignore,
483 Func<string, string, ValueTask>? postCopyCallback,
484 SemaphoreSlim? semaphore,
485 CancellationToken cancellationToken)
488 Task? subdirCreationTask =
null;
489 foreach (var subDirectory
in dir.EnumerateDirectories())
491 if (ignore !=
null && ignore.Contains(subDirectory.Name))
494 var checkingSubdirCreationTask =
true;
495 foreach (var copyTask
in CopyDirectoryImpl(subDirectory.FullName,
fileSystem.Path.Combine(dest, subDirectory.Name),
null, postCopyCallback, semaphore, cancellationToken))
497 if (subdirCreationTask ==
null)
499 subdirCreationTask = copyTask;
500 yield
return subdirCreationTask;
502 else if (!checkingSubdirCreationTask)
503 yield
return copyTask;
505 checkingSubdirCreationTask =
false;
509 foreach (var fileInfo
in dir.EnumerateFiles())
511 if (subdirCreationTask ==
null)
514 yield
return subdirCreationTask;
517 if (ignore !=
null && ignore.Contains(fileInfo.Name))
520 var sourceFile = fileInfo.FullName;
521 var destFile =
ConcatPath(dest, fileInfo.Name);
523 async Task CopyThisFile()
525 await subdirCreationTask.WaitAsync(cancellationToken);
526 using var lockContext = semaphore !=
null
529 await
CopyFile(sourceFile, destFile, cancellationToken);
530 if (postCopyCallback !=
null)
531 await postCopyCallback(sourceFile, destFile);
534 yield
return CopyThisFile();
IIOManager that resolves paths to Environment.CurrentDirectory.
IEnumerable< Task > CopyDirectoryImpl(string src, string dest, IEnumerable< string >? ignore, Func< string, string, ValueTask >? postCopyCallback, SemaphoreSlim? semaphore, CancellationToken cancellationToken)
Copies a directory from src to dest .
async ValueTask< byte[]> ReadAllBytes(string path, CancellationToken cancellationToken)
Returns all the contents of a file at path as a byte array.A ValueTask that results in the contents ...
Task DeleteDirectory(string path, CancellationToken cancellationToken)
Recursively delete a directory, removes and does not enter any symlinks encounterd....
Task MoveFile(string source, string destination, CancellationToken cancellationToken)
Moves a file at source to destination .A Task representing the running operation.
string ResolvePath()
Retrieve the full path of the current working directory.The full path of the current working director...
virtual string ResolvePathCore(string path)
Resolve a given, non-rooted, path .
Task CreateDirectory(string path, CancellationToken cancellationToken)
Create a directory at path .A Task representing the running operation.
Task< IReadOnlyList< string > > GetDirectories(string path, CancellationToken cancellationToken)
Returns full directory names in a given path .A Task<TResult> resulting in the directories in path .
DefaultIOManager(IFileSystem fileSystem)
Initializes a new instance of the DefaultIOManager class.
Task< List< string > > GetFilesWithExtension(string path, string extension, bool recursive, CancellationToken cancellationToken)
Gets a list of files in path with the given extension .A Task resulting in a list of paths to files ...
async ValueTask CopyFile(string src, string dest, CancellationToken cancellationToken)
Copy a file from src to dest .A ValueTask representing the running operation.
Task< IReadOnlyList< string > > GetFiles(string path, CancellationToken cancellationToken)
Returns full file names in a given path .A Task<TResult> resulting in the files in path .
string ResolvePath(string path)
Retrieve the full path of some path given a relative path. Must be used before passing relative path...
Task< bool > FileExists(string path, CancellationToken cancellationToken)
Check that the file at path exists.A Task<TResult> resulting in true if the file at path exists,...
Task MoveDirectory(string source, string destination, CancellationToken cancellationToken)
Moves a directory at source to destination .A Task representing the running operation.
string GetFileNameWithoutExtension(string path)
Gets the file name portion of a path with.The file name portion of path .
IIOManager CreateResolverForSubdirectory(string subdirectoryPath)
Create a new IIOManager that resolves paths to the specified subdirectoryPath .A new IIOManager.
char AltDirectorySeparatorChar
Gets the alternative directory separator character.
async ValueTask WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken)
Writes some contents to a file at path overwriting previous content.A ValueTask representing the ru...
Task< bool > PathIsChildOf(string parentPath, string childPath, CancellationToken cancellationToken)
Check if a given parentPath is a parent of a given parentPath .A Task<TResult> resulting in true if ...
Task< DateTimeOffset > GetLastModified(string path, CancellationToken cancellationToken)
Get the DateTimeOffset of when a given path was last modified.A Task<TResult> resulting in the DateT...
async ValueTask CopyDirectory(IEnumerable< string >? ignore, Func< string, string, ValueTask >? postCopyCallback, string src, string dest, int? taskThrottle, CancellationToken cancellationToken)
Copies a directory from src to dest .A ValueTask representing the running operation.
const int DefaultBufferSize
Default FileStream buffer size used by .NET.
Stream CreateAsyncReadStream(string path, bool sequental, bool shareWrite)
Creates an asynchronous FileStream for sequential reading.The open Stream.
const string CurrentDirectory
Path to the current working directory for the IIOManager.
string GetDirectoryName(string path)
Gets the directory portion of a given path .The directory portion of the given path .
bool IsPathRooted(string path)
Check if a given path is at the root level of the filesystem.true if the path is rooted,...
bool PathContainsParentAccess(string path)
Check if a path contains the '..' parent directory accessor.true if path contains a '....
string ConcatPath(params string[] paths)
Combines an array of strings into a path.The combined path.
const TaskCreationOptions BlockingTaskCreationOptions
The TaskCreationOptions used to spawn Tasks for potentially long running, blocking operations.
char DirectorySeparatorChar
Gets the primary directory separator character.
static void NormalizeAndDelete(IDirectoryInfo dir, CancellationToken cancellationToken)
Recursively empty a directory.
Task DeleteFile(string path, CancellationToken cancellationToken)
Deletes a file at path .A Task representing the running operation.
string GetFileName(string path)
Gets the file name portion of a path .The file name portion of path .
async ValueTask ZipToDirectory(string path, Stream zipFile, CancellationToken cancellationToken)
Extract a set of zipFile to a given path .A ValueTask representing the running operation.
Stream CreateAsyncSequentialWriteStream(string path)
Creates an asynchronous FileStream for sequential writing.The open Stream.
readonly IFileSystem fileSystem
The backing IFileSystem.
Task< IDirectoryInfo > DirectoryInfo(string path, CancellationToken cancellationToken)
Gets a IDirectoryInfo for the given path .A Task<TResult> resulting in the IDirectoryInfo of the path...
Task< bool > DirectoryExists(string path, CancellationToken cancellationToken)
Check that the directory at path exists.A Task resulting in true if the directory at path exists,...
An IIOManager that resolve relative paths from another IIOManager to a subdirectory of that.
Operation exceptions thrown from the context of a Models.Job.
Async lock context helper.
static async ValueTask< SemaphoreSlimContext > Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken, ILogger? logger=null)
Asyncronously locks a semaphore .
Interface for using filesystems.
char DirectorySeparatorChar
Gets the primary directory separator character.