Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove redundant validation when enumerating files & folders in SystemFile/SystemFolder. #56

Merged
Merged
28 changes: 26 additions & 2 deletions src/System/IO/SystemFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,30 @@ public SystemFile(FileInfo info)
Path = _info.FullName;
}

/// <summary>
/// Creates a new instance of <see cref="SystemFile"/>
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="path">The path to the file.</param>
/// <param name="isInternal">
/// Dummy parameter to differiate from the public constructors.
/// </param>
internal SystemFile(string path, bool isInternal)
{
foreach (var c in global::System.IO.Path.GetInvalidPathChars())
{
if (path.Contains(c))
throw new FormatException($"Provided path contains invalid character '{c}'.");
}

Id = path;
Path = path;
}

/// <inheritdoc />
public string Id { get; }

Expand Down Expand Up @@ -78,13 +102,13 @@ public Task<Stream> OpenStreamAsync(FileAccess accessMode = FileAccess.Read, Can
public Task<IFolder?> GetParentAsync(CancellationToken cancellationToken = default)
{
DirectoryInfo? parent = _info != null ? _info.Directory : Directory.GetParent(Path);
return Task.FromResult<IFolder?>(parent != null ? new SystemFolder(parent) : null);
return Task.FromResult<IFolder?>(parent != null ? new SystemFolder(parent, isInternal: true) : null);
}

/// <inheritdoc />
public Task<IFolder?> GetRootAsync(CancellationToken cancellationToken = default)
{
DirectoryInfo root = _info?.Directory != null ? _info.Directory.Root : new DirectoryInfo(Path).Root;
return Task.FromResult<IFolder?>(new SystemFolder(root));
return Task.FromResult<IFolder?>(new SystemFolder(root, isInternal: true));
}
}
75 changes: 60 additions & 15 deletions src/System/IO/SystemFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,51 @@ public SystemFolder(DirectoryInfo info)
Name = global::System.IO.Path.GetFileName(Path) ?? throw new ArgumentException($"Could not determine directory name from path '{Path}'.");
}

/// <summary>
/// Creates a new instance of <see cref="SystemFolder"/>
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="path">The path to the folder.</param>
/// <param name="isInternal">
/// Dummy parameter to differiate from the public constructors.
/// </param>
internal SystemFolder(string path, bool isInternal)
{
// 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}'.");
}


/// <summary>
/// Creates a new instance of <see cref="SystemFolder"/>.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="info">The directory to use.</param>
/// <param name="isInternal">
/// Dummy parameter to differiate from the public constructors.
/// </param>
internal SystemFolder(DirectoryInfo info, bool isInternal)
{
_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);

Id = Path;
Name = global::System.IO.Path.GetFileName(Path) ?? throw new ArgumentException($"Could not determine directory name from path '{Path}'.");
}

/// <summary>
/// Gets the underlying <see cref="DirectoryInfo"/> for this folder.
/// </summary>
Expand Down Expand Up @@ -91,10 +136,10 @@ public async IAsyncEnumerable<IStorableChild> GetItemsAsync(StorableType type =
continue;

if (IsFolder(item))
yield return new SystemFolder(item);
yield return new SystemFolder(item, isInternal: true);

else if (IsFile(item))
yield return new SystemFile(item);
yield return new SystemFile(item, isInternal: true);
}

yield break;
Expand All @@ -109,7 +154,7 @@ public async IAsyncEnumerable<IStorableChild> GetItemsAsync(StorableType type =
if (file is null)
continue;

yield return new SystemFile(file);
yield return new SystemFile(file, isInternal: true);
}
}

Expand All @@ -122,7 +167,7 @@ public async IAsyncEnumerable<IStorableChild> GetItemsAsync(StorableType type =
if (folder is null)
continue;

yield return new SystemFolder(folder);
yield return new SystemFolder(folder, isInternal: true);
}
}
}
Expand All @@ -135,10 +180,10 @@ public Task<IStorableChild> 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<IStorableChild>(new SystemFile(id));
return Task.FromResult<IStorableChild>(new SystemFile(id, isInternal: true));

if (IsFolder(id))
return Task.FromResult<IStorableChild>(new SystemFolder(id));
return Task.FromResult<IStorableChild>(new SystemFolder(id, isInternal: true));

throw new ArgumentException($"Could not determine if the provided path is a file or folder. Path '{id}'.");
}
Expand All @@ -159,7 +204,7 @@ public Task<IStorableChild> 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<IStorableChild>(new SystemFile(fullPath));
return Task.FromResult<IStorableChild>(new SystemFile(fullPath, isInternal: true));
}

if (IsFolder(id))
Expand All @@ -168,16 +213,16 @@ public Task<IStorableChild> 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<IStorableChild>(new SystemFolder(id));
return Task.FromResult<IStorableChild>(new SystemFolder(id, isInternal: true));
}

throw new FileNotFoundException($"Could not determine if the provided path exists, or whether it's a file or folder. Id '{id}'.");
}

/// <inheritdoc/>
public async Task<IStorableChild> GetFirstByNameAsync(string name, CancellationToken cancellationToken = default)
public Task<IStorableChild> 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);
}

/// <inheritdoc />
Expand Down Expand Up @@ -219,14 +264,14 @@ public async Task<IChildFile> CreateCopyOfAsync(IFile fileToCopy, bool overwrite
if (File.Exists(newPath))
{
if (!overwrite)
return new SystemFile(newPath);
return new SystemFile(newPath, isInternal: true);

File.Delete(newPath);
}

File.Copy(systemFile.Path, newPath, overwrite);

return new SystemFile(newPath);
return new SystemFile(newPath, isInternal: true);
}

/// <inheritdoc />
Expand All @@ -239,14 +284,14 @@ public async Task<IChildFile> 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, isInternal: true);

if (overwrite)
File.Delete(newPath);

File.Move(systemFile.Path, newPath);

return new SystemFile(newPath);
return new SystemFile(newPath, isInternal: true);
}

/// <inheritdoc />
Expand Down Expand Up @@ -276,7 +321,7 @@ public Task<IChildFile> CreateFileAsync(string name, bool overwrite = false, Can
if (overwrite || !File.Exists(newPath))
File.Create(newPath).Dispose();

return Task.FromResult<IChildFile>(new SystemFile(newPath));
return Task.FromResult<IChildFile>(new SystemFile(newPath, isInternal: true));
}

/// <inheritdoc />
Expand Down
Loading