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

Advanced filter in FromDirectory #166

Merged
merged 7 commits into from Mar 13, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
111 changes: 110 additions & 1 deletion src/Yarhl.UnitTests/FileSystem/NodeFactoryTests.cs
Expand Up @@ -22,6 +22,7 @@ namespace Yarhl.UnitTests.FileSystem
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using NUnit.Framework;
using Yarhl.FileSystem;
using Yarhl.IO;
Expand Down Expand Up @@ -310,7 +311,7 @@ public void CreateFromDirectoryAndNullName()
Directory.CreateDirectory(tempDir);

Assert.Throws<ArgumentNullException>(() =>
NodeFactory.FromDirectory(tempDir, null));
NodeFactory.FromDirectory(tempDir, "*", null));

Directory.Delete(tempDir, true);
}
Expand Down Expand Up @@ -635,5 +636,113 @@ public void ReadFromReadonlyFileUsingWriteModeThrowsException()
File.SetAttributes(tempFile, FileAttributes.Normal);
File.Delete(tempFile);
}

// Advanced filter
[Test]
public void CreateFromDirectoryAdvancedFilterAndEmptyPath()
{
Assert.That(
() => NodeFactory.FromDirectory(null, _ => true),
Throws.ArgumentNullException);
Assert.That(
() => NodeFactory.FromDirectory(string.Empty, _ => true),
Throws.ArgumentNullException);
Assert.That(
() => NodeFactory.FromDirectory(null, _ => true, "name"),
Throws.ArgumentNullException);
Assert.That(
() => NodeFactory.FromDirectory(string.Empty, _ => true, "name"),
Throws.ArgumentNullException);
}

[Test]
public void CreateFromDirectoryAdvancedFilterAndEmptyName()
{
Assert.That(
() => NodeFactory.FromDirectory("dir", _ => true, null),
Throws.ArgumentNullException);
Assert.That(
() => NodeFactory.FromDirectory("dir", _ => true, string.Empty),
Throws.ArgumentNullException);
}

[Test]
public void CreateFromDirectoryAdvancedFilterWithFinalSlash()
{
string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(tempDir);

string tempFile1 = Path.Combine(tempDir, Path.GetRandomFileName());
File.Create(tempFile1).Dispose();

Node node = NodeFactory.FromDirectory(tempDir + Path.DirectorySeparatorChar, _ => true);
Assert.AreEqual(Path.GetFileName(tempDir), node.Name);
Assert.IsTrue(node.IsContainer);
Assert.AreEqual(1, node.Children.Count);
Assert.IsTrue(node.Children.Any(n => n.Name == Path.GetFileName(tempFile1)));

node.Dispose();
Directory.Delete(tempDir, true);
}

[Test]
public void CreateFromDirectoryWithFilesAndAdvancedFilter()
{
string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(tempDir);

string tempFile1 = Path.Combine(tempDir, "file1.bin");
File.Create(tempFile1).Dispose();
string tempFile2 = Path.Combine(tempDir, "arch2.bin");
File.Create(tempFile2).Dispose();
string tempFile3 = Path.Combine(tempDir, "file3.txt");
File.Create(tempFile3).Dispose();

Node node = NodeFactory.FromDirectory(tempDir, x => Regex.IsMatch(x, @"file\d\.(txt|bin)$"));
Assert.AreEqual(Path.GetFileName(tempDir), node.Name);
Assert.AreEqual(2, node.Children.Count);
Assert.IsTrue(node.Children.Any(n => n.Name == Path.GetFileName(tempFile1)));
Assert.IsTrue(node.Children.Any(n => n.Name == Path.GetFileName(tempFile3)));
node.Dispose();

node = NodeFactory.FromDirectory(tempDir, x => x.EndsWith(".bin"));
Assert.AreEqual(Path.GetFileName(tempDir), node.Name);
Assert.AreEqual(2, node.Children.Count);
Assert.IsTrue(node.Children.Any(n => n.Name == Path.GetFileName(tempFile1)));
Assert.IsTrue(node.Children.Any(n => n.Name == Path.GetFileName(tempFile2)));

node.Dispose();
Directory.Delete(tempDir, true);
}

[Test]
public void CreateFromDirectoryWithFilesAndNameAndAdvancedFilter()
{
string tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
Directory.CreateDirectory(tempDir);

string tempFile1 = Path.Combine(tempDir, "file1.bin");
File.Create(tempFile1).Dispose();
string tempFile2 = Path.Combine(tempDir, "arch2.bin");
File.Create(tempFile2).Dispose();
string tempFile3 = Path.Combine(tempDir, "file3.txt");
File.Create(tempFile3).Dispose();

Node node = NodeFactory.FromDirectory(tempDir, x => Regex.IsMatch(x, @"file\d\.(txt|bin)$"), "MyDir");
Assert.AreEqual("MyDir", node.Name);
Assert.AreEqual(2, node.Children.Count);
Assert.IsTrue(node.Children.Any(n => n.Name == Path.GetFileName(tempFile1)));
Assert.IsTrue(node.Children.Any(n => n.Name == Path.GetFileName(tempFile3)));
node.Dispose();

node = NodeFactory.FromDirectory(tempDir, x => x.EndsWith(".bin"), "MyDir");
Assert.AreEqual("MyDir", node.Name);
Assert.AreEqual(2, node.Children.Count);
Assert.IsTrue(node.Children.Any(n => n.Name == Path.GetFileName(tempFile1)));
Assert.IsTrue(node.Children.Any(n => n.Name == Path.GetFileName(tempFile2)));

node.Dispose();
Directory.Delete(tempDir, true);
}
}
}
78 changes: 76 additions & 2 deletions src/Yarhl/FileSystem/NodeFactory.cs
Expand Up @@ -20,6 +20,7 @@
namespace Yarhl.FileSystem
{
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Yarhl.IO;
Expand Down Expand Up @@ -215,16 +216,89 @@ public static Node FromDirectory(string dirPath, string filter = "*", FileOpenMo
bool subDirectories = false,
FileOpenMode mode = FileOpenMode.ReadWrite)
{
var options = subDirectories ?
if (string.IsNullOrEmpty(dirPath))
throw new ArgumentNullException(nameof(dirPath));

if (string.IsNullOrEmpty(filter))
throw new ArgumentNullException(nameof(filter));

if (string.IsNullOrEmpty(nodeName))
throw new ArgumentNullException(nameof(nodeName));

SearchOption options = subDirectories ?
SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;

// This sanitizes the path and remove double slashes
dirPath = Path.GetFullPath(dirPath);

string[] fileList = Directory.GetFiles(dirPath, filter, options);
return FromFileList(dirPath, nodeName, fileList, mode);
}

/// <summary>
/// Creates a Node containing all the files from the directory.
/// </summary>
/// <returns>The container node.</returns>
/// <param name="dirPath">Directory path.</param>
/// <param name="filter">Filter for files in directory.</param>
/// <param name="mode">The mode to open the files.</param>
public static Node FromDirectory(string dirPath, Func<string, bool> filter, FileOpenMode mode = FileOpenMode.ReadWrite)
pleonex marked this conversation as resolved.
Show resolved Hide resolved
{
if (string.IsNullOrEmpty(dirPath))
throw new ArgumentNullException(nameof(dirPath));

if (dirPath[dirPath.Length - 1] == Path.DirectorySeparatorChar)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The one-line conditions not related to guards should have braces to prevent future bugs (the previous function doesn't follow this rule this rule but I think it's old code before this rule was introduced).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

dirPath = dirPath.Remove(dirPath.Length - 1);
pleonex marked this conversation as resolved.
Show resolved Hide resolved

string dirName = Path.GetFileName(dirPath);
return FromDirectory(dirPath, filter, dirName, false, mode);
}

/// <summary>
/// Creates a Node containing all the files from the directory.
/// </summary>
/// <returns>The container node.</returns>
/// <param name="dirPath">Directory path.</param>
/// <param name="filter">Filter for files in directory.</param>
/// <param name="nodeName">Node name.</param>
/// <param name="subDirectories">
/// If <see langword="true" /> it searchs recursively in subdirectories.
/// </param>
/// <param name="mode">The mode to open the files.</param>
[SuppressMessage(
"Reliability",
"CA2000:Dispose objects before losing scope",
Justification = "Ownserhip dispose transferred")]
public static Node FromDirectory(
string dirPath,
Func<string, bool> filter,
string nodeName,
bool subDirectories = false,
FileOpenMode mode = FileOpenMode.ReadWrite)
{
if (string.IsNullOrEmpty(dirPath))
throw new ArgumentNullException(nameof(dirPath));

if (string.IsNullOrEmpty(nodeName))
throw new ArgumentNullException(nameof(nodeName));

SearchOption options = subDirectories ?
SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;

// This sanitizes the path and remove double slashes
dirPath = Path.GetFullPath(dirPath);

string[] allFiles = Directory.GetFiles(dirPath, "*", options);
string[] fileList = Array.FindAll(allFiles, x => filter(x));
return FromFileList(dirPath, nodeName, fileList, mode);
}

private static Node FromFileList(string dirPath, string nodeName, IEnumerable<string> fileList, FileOpenMode mode = FileOpenMode.ReadWrite)
{
Node folder = CreateContainer(nodeName);
folder.Tags["DirectoryInfo"] = new DirectoryInfo(dirPath);

foreach (string filePath in Directory.GetFiles(dirPath, filter, options)) {
foreach (string filePath in fileList) {
string relParent = Path.GetDirectoryName(filePath)
.Replace(dirPath, string.Empty);
CreateContainersForChild(folder, relParent, FromFile(filePath, mode));
Expand Down