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 all 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
153 changes: 152 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 @@ -301,6 +302,29 @@ public void CreateFromDirectoryDoesNotExist()
public void CreateFromNullDirectory()
{
Assert.Throws<ArgumentNullException>(() => NodeFactory.FromDirectory(null));

Assert.Throws<ArgumentNullException>(() => NodeFactory.FromDirectory(string.Empty));

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

Assert.Throws<ArgumentNullException>(() => NodeFactory.FromDirectory(string.Empty, "*", "name"));
}

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

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

Assert.Throws<ArgumentNullException>(() => NodeFactory.FromDirectory(tempDir, string.Empty));

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

Assert.Throws<ArgumentNullException>(() => NodeFactory.FromDirectory(tempDir, string.Empty, "name"));

Directory.Delete(tempDir, true);
}

[Test]
Expand All @@ -310,7 +334,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 +659,132 @@ 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 CreateFromDirectoryAdvancedFilterAndEmptyFilter()
{
Assert.That(
() => NodeFactory.FromDirectory("dir", (Func<string, bool>)null),
Throws.ArgumentNullException);

Assert.That(
() => NodeFactory.FromDirectory("dir", (Func<string, bool>)null, "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();

node = NodeFactory.FromDirectory(tempDir + Path.DirectorySeparatorChar, _ => true, "name");
Assert.AreEqual("name", 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);
}
}
}
101 changes: 97 additions & 4 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 @@ -186,8 +187,12 @@ public static Node FromDirectory(string dirPath, string filter = "*", FileOpenMo
if (string.IsNullOrEmpty(dirPath))
throw new ArgumentNullException(nameof(dirPath));

if (dirPath[dirPath.Length - 1] == Path.DirectorySeparatorChar)
// This sanitizes the path and remove double slashes
dirPath = Path.GetFullPath(dirPath);

if (dirPath[dirPath.Length - 1] == Path.DirectorySeparatorChar) {
dirPath = dirPath.Remove(dirPath.Length - 1);
}

string dirName = Path.GetFileName(dirPath);
return FromDirectory(dirPath, filter, dirName, false, mode);
Expand Down Expand Up @@ -215,24 +220,112 @@ 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 (filter == null)
throw new ArgumentNullException(nameof(filter));

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

if (dirPath[dirPath.Length - 1] == Path.DirectorySeparatorChar) {
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 (filter == null)
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);

if (dirPath[dirPath.Length - 1] == Path.DirectorySeparatorChar) {
dirPath = dirPath.Remove(dirPath.Length - 1);
}

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));
}

foreach (Node node in Navigator.IterateNodes(folder)) {
if (!node.IsContainer || node.Tags.ContainsKey("DirectoryInfo"))
if (!node.IsContainer || node.Tags.ContainsKey("DirectoryInfo")) {
continue;
}

int rootPathLength = $"{NodeSystem.PathSeparator}{nodeName}".Length;
string nodePath = Path.GetFullPath(string.Concat(dirPath, node.Path.Substring(rootPathLength)));
Expand Down