diff --git a/src/System/IO/SystemFile.cs b/src/System/IO/SystemFile.cs index 124bbf7..6f754b4 100644 --- a/src/System/IO/SystemFile.cs +++ b/src/System/IO/SystemFile.cs @@ -29,7 +29,6 @@ public SystemFile(string path) if (!File.Exists(path)) throw new FileNotFoundException($"File not found at path {path}."); - Id = path; Path = path; } @@ -45,12 +44,48 @@ public SystemFile(FileInfo info) _info = info; _name = _info.Name; - Id = _info.FullName; + Path = _info.FullName; + } + + /// + /// Creates a new instance of + /// + /// + /// NOTE: This constructor does not verify whether the file + /// actually exists beforehand. Do not use outside of enumeration + /// or when it's known that the file exists. + /// + /// The path to the file. + /// + /// A required value for this overload. No functional difference between provided values. + /// + internal SystemFile(string path, bool noValidation) + { + Path = path; + } + + /// + /// Creates a new instance of + /// + /// + /// NOTE: This constructor does not verify whether the file + /// actually exists beforehand. Do not use outside of enumeration + /// or when it's known that the file exists. + /// + /// The file info. + /// + /// A required value for this overload. No functional difference between provided values. + /// + internal SystemFile(FileInfo info, bool noValidation) + { + _info = info; + + _name = _info.Name; Path = _info.FullName; } /// - public string Id { get; } + public string Id => Path; /// public string Name => _name ??= global::System.IO.Path.GetFileName(Path); @@ -78,13 +113,13 @@ public Task OpenStreamAsync(FileAccess accessMode = FileAccess.Read, Can public Task GetParentAsync(CancellationToken cancellationToken = default) { DirectoryInfo? parent = _info != null ? _info.Directory : Directory.GetParent(Path); - return Task.FromResult(parent != null ? new SystemFolder(parent) : null); + return Task.FromResult(parent != null ? new SystemFolder(parent, noValidation: true) : null); } /// public Task GetRootAsync(CancellationToken cancellationToken = default) { DirectoryInfo root = _info?.Directory != null ? _info.Directory.Root : new DirectoryInfo(Path).Root; - return Task.FromResult(new SystemFolder(root)); + return Task.FromResult(new SystemFolder(root, noValidation: true)); } } diff --git a/src/System/IO/SystemFolder.cs b/src/System/IO/SystemFolder.cs index 083bb19..41a2b2a 100644 --- a/src/System/IO/SystemFolder.cs +++ b/src/System/IO/SystemFolder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -15,6 +15,7 @@ namespace OwlCore.Storage.System.IO; /// public class SystemFolder : IModifiableFolder, IChildFolder, ICreateCopyOf, IMoveFrom, IGetItem, IGetItemRecursive, IGetFirstByName, IGetRoot { + private string? _name; private DirectoryInfo? _info; /// @@ -29,14 +30,11 @@ public SystemFolder(string path) throw new FormatException($"Provided path contains invalid character '{c}'."); } - // For consistency, always remove the trailing directory separator. - Path = path.TrimEnd(global::System.IO.Path.PathSeparator, global::System.IO.Path.DirectorySeparatorChar, global::System.IO.Path.AltDirectorySeparatorChar); - if (!Directory.Exists(path)) throw new FileNotFoundException($"Directory not found at path '{Path}'."); - Id = Path; - Name = global::System.IO.Path.GetFileName(Path) ?? throw new ArgumentException($"Could not determine directory name from path '{Path}'."); + // For consistency, always remove the trailing directory separator. + Path = path.TrimEnd(global::System.IO.Path.PathSeparator, global::System.IO.Path.DirectorySeparatorChar, global::System.IO.Path.AltDirectorySeparatorChar); } /// @@ -45,16 +43,54 @@ public SystemFolder(string path) /// The directory to use. public SystemFolder(DirectoryInfo info) { + if (!info.Exists) + throw new FileNotFoundException($"Directory not found at path '{Path}'."); + _info = info; // For consistency, always remove the trailing directory separator. Path = info.FullName.TrimEnd(global::System.IO.Path.PathSeparator, global::System.IO.Path.DirectorySeparatorChar, global::System.IO.Path.AltDirectorySeparatorChar); + _name = info.Name; + } - if (!info.Exists) - throw new FileNotFoundException($"Directory not found at path '{Path}'."); + /// + /// Creates a new instance of + /// + /// + /// NOTE: This constructor does not verify whether the directory + /// actually exists beforehand. Do not use outside of enumeration + /// or when it's known that the folder exists. + /// + /// The path to the folder. + /// + /// A required value for this overload. No functional difference between provided values. + /// + internal SystemFolder(string path, bool noValidation) + { + // For consistency, always remove the trailing directory separator. + Path = path.TrimEnd(global::System.IO.Path.PathSeparator, global::System.IO.Path.DirectorySeparatorChar, global::System.IO.Path.AltDirectorySeparatorChar); + } - Id = Path; - Name = global::System.IO.Path.GetFileName(Path) ?? throw new ArgumentException($"Could not determine directory name from path '{Path}'."); + + /// + /// Creates a new instance of . + /// + /// + /// NOTE: This constructor does not verify whether the directory + /// actually exists beforehand. Do not use outside of enumeration + /// or when it's known that the folder exists. + /// + /// The directory to use. + /// + /// A required value for this overload. No functional difference between provided values. + /// + internal SystemFolder(DirectoryInfo info, bool noValidation) + { + _info = info; + + // For consistency, always remove the trailing directory separator. + Path = info.FullName.TrimEnd(global::System.IO.Path.PathSeparator, global::System.IO.Path.DirectorySeparatorChar, global::System.IO.Path.AltDirectorySeparatorChar); + _name = info.Name; } /// @@ -63,10 +99,10 @@ public SystemFolder(DirectoryInfo info) public DirectoryInfo Info => _info ??= new DirectoryInfo(Path); /// - public string Id { get; } + public string Id => Path; /// - public string Name { get; } + public string Name => _name ??= global::System.IO.Path.GetFileName(Path) ?? throw new ArgumentException($"Could not determine directory name from path '{Path}'."); /// /// Gets the path of the folder on disk. @@ -83,18 +119,17 @@ public async IAsyncEnumerable GetItemsAsync(StorableType type = if (type.HasFlag(StorableType.All)) { - foreach (var item in Directory.EnumerateFileSystemEntries(Path)) + foreach (var item in Info.EnumerateFileSystemInfos()) { cancellationToken.ThrowIfCancellationRequested(); if (item is null) continue; - if (IsFolder(item)) - yield return new SystemFolder(item); - - else if (IsFile(item)) - yield return new SystemFile(item); + if (item.Attributes.HasFlag(FileAttributes.Directory)) + yield return new SystemFolder((DirectoryInfo)item, noValidation: true); + else + yield return new SystemFile((FileInfo)item, noValidation: true); } yield break; @@ -109,7 +144,7 @@ public async IAsyncEnumerable GetItemsAsync(StorableType type = if (file is null) continue; - yield return new SystemFile(file); + yield return new SystemFile(file, noValidation: true); } } @@ -122,7 +157,7 @@ public async IAsyncEnumerable GetItemsAsync(StorableType type = if (folder is null) continue; - yield return new SystemFolder(folder); + yield return new SystemFolder(folder, noValidation: true); } } } @@ -135,10 +170,10 @@ public Task GetItemRecursiveAsync(string id, CancellationToken c // Since the path is used as the id, we can provide a fast method of getting a single item, without iterating. if (IsFile(id)) - return Task.FromResult(new SystemFile(id)); + return Task.FromResult(new SystemFile(id, noValidation: true)); if (IsFolder(id)) - return Task.FromResult(new SystemFolder(id)); + return Task.FromResult(new SystemFolder(id, noValidation: true)); throw new ArgumentException($"Could not determine if the provided path is a file or folder. Path '{id}'."); } @@ -159,7 +194,7 @@ public Task GetItemAsync(string id, CancellationToken cancellati if (!File.Exists(fullPath)) throw new FileNotFoundException($"The provided Id does not belong to an item in this folder."); - return Task.FromResult(new SystemFile(fullPath)); + return Task.FromResult(new SystemFile(fullPath, noValidation: true)); } if (IsFolder(id)) @@ -168,16 +203,16 @@ public Task GetItemAsync(string id, CancellationToken cancellati if (global::System.IO.Path.GetDirectoryName(id) != Path || !Directory.Exists(id)) throw new FileNotFoundException($"The provided Id does not belong to an item in this folder."); - return Task.FromResult(new SystemFolder(id)); + return Task.FromResult(new SystemFolder(id, noValidation: true)); } throw new FileNotFoundException($"Could not determine if the provided path exists, or whether it's a file or folder. Id '{id}'."); } /// - public async Task GetFirstByNameAsync(string name, CancellationToken cancellationToken = default) + public Task GetFirstByNameAsync(string name, CancellationToken cancellationToken = default) { - return await GetItemAsync(global::System.IO.Path.Combine(Path, name), cancellationToken); + return GetItemAsync(global::System.IO.Path.Combine(Path, name), cancellationToken); } /// @@ -195,8 +230,7 @@ public Task DeleteAsync(IStorableChild item, CancellationToken cancellationToken if (IsFolder(item.Id)) Directory.Delete(item.Id, recursive: true); - - if (IsFile(item.Id)) + else if (IsFile(item.Id)) File.Delete(item.Id); return Task.CompletedTask; @@ -219,14 +253,14 @@ public async Task CreateCopyOfAsync(IFile fileToCopy, bool overwrite if (File.Exists(newPath)) { if (!overwrite) - return new SystemFile(newPath); + return new SystemFile(newPath, noValidation: true); File.Delete(newPath); } File.Copy(systemFile.Path, newPath, overwrite); - return new SystemFile(newPath); + return new SystemFile(newPath, noValidation: true); } /// @@ -239,14 +273,14 @@ public async Task MoveFromAsync(IChildFile fileToMove, IModifiableFo // Handle using System.IO var newPath = global::System.IO.Path.Combine(Path, systemFile.Name); if (File.Exists(newPath) && !overwrite) - return new SystemFile(newPath); + return new SystemFile(newPath, noValidation: true); if (overwrite) File.Delete(newPath); File.Move(systemFile.Path, newPath); - return new SystemFile(newPath); + return new SystemFile(newPath, noValidation: true); } /// @@ -276,13 +310,13 @@ public Task CreateFileAsync(string name, bool overwrite = false, Can if (overwrite || !File.Exists(newPath)) File.Create(newPath).Dispose(); - return Task.FromResult(new SystemFile(newPath)); + return Task.FromResult(new SystemFile(newPath, noValidation: true)); } /// public Task GetParentAsync(CancellationToken cancellationToken = default) { - return Task.FromResult(Directory.GetParent(Path) is { } di ? new SystemFolder(di) : null); + return Task.FromResult(Directory.GetParent(Path) is { } di ? new SystemFolder(di, noValidation: true) : null); } /// @@ -311,4 +345,4 @@ string GetParentDirectoryName(string relativePath) return parentPath.Replace(parentParentPath, "").TrimEnd(global::System.IO.Path.DirectorySeparatorChar); } -} \ No newline at end of file +} diff --git a/src/System/IO/SystemFolderWatcher.cs b/src/System/IO/SystemFolderWatcher.cs index 1c4b03e..214a14d 100644 --- a/src/System/IO/SystemFolderWatcher.cs +++ b/src/System/IO/SystemFolderWatcher.cs @@ -108,7 +108,7 @@ private static IStorable CreateStorableFromPath(string path, bool minimalImpleme if (minimalImplementation) return new SimpleStorableItem(id: path, name: Path.GetDirectoryName(path) ?? throw new ArgumentException($"Could not determine directory name from path '{path}'.")); - return new SystemFolder(path); + return new SystemFolder(path, noValidation: true); } if (IsFile(path)) @@ -116,7 +116,7 @@ private static IStorable CreateStorableFromPath(string path, bool minimalImpleme if (minimalImplementation) return new SimpleStorableItem(id: path, name: Path.GetFileName(path)); - return new SystemFile(path); + return new SystemFile(path, noValidation: true); } // The item is most likely deleted. Return all available information through SimpleStorableItem