diff --git a/BasicNodes/File/DeleteSourceDirectory.cs b/BasicNodes/File/DeleteSourceDirectory.cs index 6be7fef9..9fc7f464 100644 --- a/BasicNodes/File/DeleteSourceDirectory.cs +++ b/BasicNodes/File/DeleteSourceDirectory.cs @@ -40,6 +40,11 @@ public class DeleteSourceDirectory : Node /// eg if [mkv, mp4, mov, etc] is the list, if any video file is found the directory will not be deleted. /// [StringArray(2)] public string[] IncludePatterns { get; set; } + + /// + /// Gets or sets if only the top most directory should be deleted, and all parent directories should be left alone + /// + [Boolean(3)] public bool TopMostOnly { get; set; } /// /// Executes the flow element @@ -47,8 +52,71 @@ public class DeleteSourceDirectory : Node /// the node parameters /// the output to call next, -1 to abort flow, 0 to end flow public override int Execute(NodeParameters args) + => TopMostOnly ? DeleteTopMostOnly(args) : DeleteFull(args); + + /// + /// Deletes only the top most directory in the library + /// + /// the node parameters + /// the output to call next, -1 to abort flow, 0 to end flow + private int DeleteTopMostOnly(NodeParameters args) { - string libraryPath = args.LibraryFileName.Substring(0, args.LibraryFileName.Length - args.RelativeFile.Length) + string libraryPath = args.LibraryFileName[..^args.RelativeFile.Length] + .TrimEnd('/') + .TrimEnd('\\'); + string dir = args.OriginalIsDirectory ? args.LibraryFileName : FileHelper.GetDirectory(args.LibraryFileName); + args.Logger?.ILog("Deleting top level directory only: " + dir); + var existsResult = args.FileService.DirectoryExists(dir); + if (existsResult.Failed(out var error)) + { + args.Logger?.WLog("Failed determining if directory exists: " + error); + return 2; + } + + if (existsResult.Value == false) + { + args.Logger?.ILog($"Directory '{dir}' no longer exists."); + return 2; + } + + if (IfEmpty) + { + args.Logger?.ILog($"Checking if directory is empty: {dir}"); + var emptyResult = args.FileService.DirectoryEmpty(dir, IncludePatterns); + if (emptyResult.Failed(out error)) + { + args.Logger?.WLog(error); + return 2; + } + + if (emptyResult.Value == false) + { + args.Logger?.ILog("Directory is not empty"); + return 2; + } + args.Logger?.ILog("Directory is considered empty"); + } + + args.Logger?.ILog("About to delete directory: " + dir); + var deleteResult = args.FileService.DirectoryDelete(dir, true); + if (deleteResult.Failed(out error)) + { + args.Logger?.WLog("Failed to deleted directory: " + error); + return 2; + } + + args.Logger?.ILog("Directory deleted: " + dir); + return 1; + } + + /// + /// Deletes the full library folder + /// + /// the node parameters + /// the output to call next, -1 to abort flow, 0 to end flow + private int DeleteFull(NodeParameters args) + { + string libraryPath = args.LibraryFileName[..^args.RelativeFile.Length] .TrimEnd('/') .TrimEnd('\\'); diff --git a/ComicNodes/ComicNodes.csproj b/ComicNodes/ComicNodes.csproj index 1e2790ba..e95e0901 100644 --- a/ComicNodes/ComicNodes.csproj +++ b/ComicNodes/ComicNodes.csproj @@ -21,6 +21,7 @@ + diff --git a/ComicNodes/Tests/ComicInfoTests.cs b/ComicNodes/Tests/ComicInfoTests.cs index 9592c5d5..062081bb 100644 --- a/ComicNodes/Tests/ComicInfoTests.cs +++ b/ComicNodes/Tests/ComicInfoTests.cs @@ -2,6 +2,8 @@ using FileFlows.ComicNodes.Comics; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting.Logging; +using PluginTestLibrary; namespace FileFlows.Comic.Tests; @@ -15,8 +17,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics/DC/Batman (1939)/Batman - #42 - Batman vs. Joker [old] [great] [amazing].cbr", "/home/john/Comics",true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -46,8 +46,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics/actual", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -73,8 +71,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -100,8 +96,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -128,8 +122,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -154,8 +146,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -181,8 +171,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -208,8 +196,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -235,8 +221,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -262,8 +246,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -289,8 +271,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -316,8 +296,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -344,8 +322,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -371,8 +347,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -397,8 +371,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -423,9 +395,7 @@ public class ComicInfoTests : TestBase "/home/john/Comics/Marvel/X-Men/X-Man/Specials/X-Man Annual 2004.cbz", "/home/john/Comics", true); - - TestContext.WriteLine(Logger.ToString()); - + Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -442,6 +412,7 @@ public class ComicInfoTests : TestBase var name = CreateComicInfo.GetNewName(info, "cbz", 3); Assert.AreEqual("X-Man - Annual 2004.cbz", name.Value); } + [TestMethod] public void AnnualNoAnnual2() { @@ -450,8 +421,6 @@ public class ComicInfoTests : TestBase "/home/john/Comics", true); - TestContext.WriteLine(Logger.ToString()); - Assert.IsFalse(result.IsFailed); var info = result.Value; Assert.IsNotNull(info); @@ -467,18 +436,14 @@ public class ComicInfoTests : TestBase var name = CreateComicInfo.GetNewName(info, "cbz", 3); Assert.AreEqual("Grimm Fairy Tales Specials - Annual 2016.cbz", name.Value); } + // [TestMethod] public void PhysicalFileTest() { const string FILE = "/home/john/Comics/DC/Batman (1939)/Batman - #1 - Batman vs. Joker [old] [great] [amazing].cbz"; - var logger = new TestLogger(); - var args = new NodeParameters(FILE, logger, false, string.Empty, new LocalFileService()) - { - LibraryFileName = FILE, - LibraryPath = "/home/john/Comics" - }; - args.TempPath = TempPath; + var args = GetNodeParameters(FILE); + args.LibraryPath = "/home/john/Comics"; var ele = new CreateComicInfo(); var result = ele.Execute(args); diff --git a/ComicNodes/Tests/TestLogger.cs b/ComicNodes/Tests/TestLogger.cs deleted file mode 100644 index 7e73e7c6..00000000 --- a/ComicNodes/Tests/TestLogger.cs +++ /dev/null @@ -1,59 +0,0 @@ -#if(DEBUG) - -namespace FileFlows.Comic.Tests; - -using FileFlows.Plugin; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -internal class TestLogger : ILogger -{ - private List Messages = new List(); - - public void DLog(params object[] args) => Log("DBUG", args); - - public void ELog(params object[] args) => Log("ERRR", args); - - public void ILog(params object[] args) => Log("INFO", args); - - public void WLog(params object[] args) => Log("WARN", args); - private void Log(string type, object[] args) - { - if (args == null || args.Length == 0) - return; - string message = type + " -> " + - string.Join(", ", args.Select(x => - x == null ? "null" : - x.GetType().IsPrimitive || x is string ? x.ToString() : - System.Text.Json.JsonSerializer.Serialize(x))); - Messages.Add(message); - } - - public bool Contains(string message) - { - if (string.IsNullOrWhiteSpace(message)) - return false; - - string log = string.Join(Environment.NewLine, Messages); - return log.Contains(message); - } - - public override string ToString() - { - return String.Join(Environment.NewLine, this.Messages.ToArray()); - } - - public string GetTail(int length = 50) - { - if (length <= 0) - length = 50; - if (Messages.Count <= length) - return string.Join(Environment.NewLine, Messages); - return string.Join(Environment.NewLine, Messages.TakeLast(length)); - } -} - -#endif \ No newline at end of file diff --git a/ComicNodes/Tests/_LocalFileService.cs b/ComicNodes/Tests/_LocalFileService.cs deleted file mode 100644 index 95bfab74..00000000 --- a/ComicNodes/Tests/_LocalFileService.cs +++ /dev/null @@ -1,479 +0,0 @@ -#if(DEBUG) -using FileFlows.Plugin.Helpers; -using FileFlows.Plugin.Models; -using FileFlows.Plugin.Services; - -namespace FileFlows.Comic.Tests; - - -public class LocalFileService : IFileService -{ - /// - /// Gets or sets the path separator for the file system - /// - public char PathSeparator { get; init; } = Path.DirectorySeparatorChar; - - /// - /// Gets or sets the allowed paths the file service can access - /// - public string[]? AllowedPaths { get; init; } - - /// - /// Gets or sets a function for replacing variables in a string. - /// - /// - /// The function takes a string input, a boolean indicating whether to strip missing variables, - /// and a boolean indicating whether to clean special characters. - /// - public ReplaceVariablesDelegate? ReplaceVariables { get; set; } - - /// - /// Gets or sets the permissions to use for files - /// - public int? Permissions { get; set; } - - /// - /// Gets or sets the owner:group to use for files - /// - public string? OwnerGroup { get; set; } - - /// - /// Gets or sets the logger used for logging - /// - public ILogger? Logger { get; set; } - - public Result GetFiles(string path, string searchPattern = "", bool recursive = false) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - return Directory.GetFiles(path, searchPattern ?? string.Empty, - recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); - } - catch (Exception) - { - return new string[] { }; - } - } - - public Result GetDirectories(string path) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - return Directory.GetDirectories(path); - } - catch (Exception) - { - return new string[] { }; - } - } - - public Result DirectoryExists(string path) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - return Directory.Exists(path); - } - catch (Exception) - { - return false; - } - } - - public Result DirectoryDelete(string path, bool recursive = false) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - Directory.Delete(path, recursive); - return true; - } - catch (Exception ex) - { - return Result.Fail(ex.Message); - } - } - - public Result DirectoryMove(string path, string destination) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - if (IsProtectedPath(ref destination)) - return Result.Fail("Cannot access protected path: " + destination); - try - { - Directory.Move(path, destination); - SetPermissions(destination); - return true; - } - catch (Exception ex) - { - return Result.Fail(ex.Message); - } - } - - public Result DirectoryCreate(string path) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - var dirInfo = new DirectoryInfo(path); - if (dirInfo.Exists == false) - dirInfo.Create(); - SetPermissions(path); - return true; - } - catch (Exception ex) - { - return Result.Fail(ex.Message); - } - } - - public Result DirectoryCreationTimeUtc(string path) - { - throw new NotImplementedException(); - } - - public Result DirectoryLastWriteTimeUtc(string path) - { - throw new NotImplementedException(); - } - - public Result FileExists(string path) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - return File.Exists(path); - } - catch (Exception) - { - return false; - } - } - - public Result FileInfo(string path) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - FileInfo fileInfo = new FileInfo(path); - - return new FileInformation - { - CreationTime = fileInfo.CreationTime, - CreationTimeUtc = fileInfo.CreationTimeUtc, - LastWriteTime = fileInfo.LastWriteTime, - LastWriteTimeUtc = fileInfo.LastWriteTimeUtc, - Extension = fileInfo.Extension.TrimStart('.'), - Name = fileInfo.Name, - FullName = fileInfo.FullName, - Length = fileInfo.Length, - Directory = fileInfo.DirectoryName! - }; - } - catch (Exception ex) - { - return Result.Fail(ex.Message); - } - } - - public Result FileDelete(string path) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - var fileInfo = new FileInfo(path); - if(fileInfo.Exists) - fileInfo.Delete(); - return true; - } - catch (Exception) - { - return false; - } - } - - public Result FileSize(string path) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - var fileInfo = new FileInfo(path); - if (fileInfo.Exists == false) - return Result.Fail("File does not exist"); - return fileInfo.Length; - } - catch (Exception ex) - { - return Result.Fail(ex.Message); - } - } - - public Result FileCreationTimeUtc(string path) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - var fileInfo = new FileInfo(path); - if (fileInfo.Exists == false) - return Result.Fail("File does not exist"); - return fileInfo.CreationTimeUtc; - } - catch (Exception ex) - { - return Result.Fail(ex.Message); - } - } - - public Result FileLastWriteTimeUtc(string path) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - var fileInfo = new FileInfo(path); - if (fileInfo.Exists == false) - return Result.Fail("File does not exist"); - return fileInfo.LastWriteTimeUtc; - } - catch (Exception ex) - { - return Result.Fail(ex.Message); - } - } - - public Result FileMove(string path, string destination, bool overwrite = true) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - if (IsProtectedPath(ref destination)) - return Result.Fail("Cannot access protected path: " + destination); - try - { - var fileInfo = new FileInfo(path); - if (fileInfo.Exists == false) - return Result.Fail("File does not exist"); - var destDir = new FileInfo(destination).Directory; - if (destDir!.Exists == false) - { - destDir.Create(); - SetPermissions(destDir.FullName); - } - - fileInfo.MoveTo(destination, overwrite); - SetPermissions(destination); - return true; - } - catch (Exception ex) - { - return Result.Fail(ex.Message); - } - } - - public Result FileCopy(string path, string destination, bool overwrite = true) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - if (IsProtectedPath(ref destination)) - return Result.Fail("Cannot access protected path: " + destination); - try - { - var fileInfo = new FileInfo(path); - if (fileInfo.Exists == false) - return Result.Fail("File does not exist"); - - var destDir = new FileInfo(destination).Directory; - if (destDir!.Exists == false) - { - destDir.Create(); - SetPermissions(destDir.FullName); - } - - fileInfo.CopyTo(destination, overwrite); - SetPermissions(destination); - return true; - } - catch (Exception ex) - { - return Result.Fail(ex.Message); - } - } - - public Result FileAppendAllText(string path, string text) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - File.AppendAllText(path, text); - SetPermissions(path); - return true; - } - catch (Exception ex) - { - return Result.Fail(ex.Message); - } - } - - public bool FileIsLocal(string path) => true; - - /// - /// Gets the local path - /// - /// the path - /// the local path to the file - public Result GetLocalPath(string path) - => Result.Success(path); - - public Result Touch(string path) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - - if (DirectoryExists(path).Is(true)) - { - try - { - Directory.SetLastWriteTimeUtc(path, DateTime.UtcNow); - return true; - } - catch (Exception ex) - { - return Result.Fail("Failed to touch directory: " + ex.Message); - } - } - - try - { - if (File.Exists(path)) - File.SetLastWriteTimeUtc(path, DateTime.UtcNow); - else - { - File.Create(path); - SetPermissions(path); - } - - return true; - } - catch (Exception ex) - { - return Result.Fail($"Failed to touch file: '{path}' => {ex.Message}"); - } - } - - public Result DirectorySize(string path) - { - throw new NotImplementedException(); - } - - public Result SetCreationTimeUtc(string path, DateTime date) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - if (!File.Exists(path)) - return Result.Fail("File not found."); - - File.SetCreationTimeUtc(path, date); - return Result.Success(true); - } - catch (Exception ex) - { - return Result.Fail($"Error setting creation time: {ex.Message}"); - } - } - - public Result SetLastWriteTimeUtc(string path, DateTime date) - { - if (IsProtectedPath(ref path)) - return Result.Fail("Cannot access protected path: " + path); - try - { - if (!File.Exists(path)) - return Result.Fail("File not found."); - - File.SetLastWriteTimeUtc(path, date); - return Result.Success(true); - } - catch (Exception ex) - { - return Result.Fail($"Error setting last write time: {ex.Message}"); - } - } - - /// - /// Checks if a path is accessible by the file server - /// - /// the path to check - /// true if accessible, otherwise false - private bool IsProtectedPath(ref string path) - { - if (OperatingSystem.IsWindows()) - path = path.Replace("/", "\\"); - else - path = path.Replace("\\", "/"); - - if(ReplaceVariables != null) - path = ReplaceVariables(path, true); - - if (FileHelper.IsSystemDirectory(path)) - return true; // a system directory, no access - - if (AllowedPaths?.Any() != true) - return false; // no allowed paths configured, allow all - - if (OperatingSystem.IsWindows()) - path = path.ToLowerInvariant(); - - for(int i=0;i? logMethod = null) - { - logMethod ??= (string message) => Logger?.ILog(message); - - permissions = permissions != null && permissions > 0 ? permissions : Permissions; - if (permissions == null || permissions < 1) - permissions = 777; - - - if ((File.Exists(path) == false && Directory.Exists(path) == false)) - { - logMethod("SetPermissions: File doesnt existing, skipping"); - return; - } - - //StringLogger stringLogger = new StringLogger(); - var logger = new TestLogger(); - - bool isFile = new FileInfo(path).Exists; - - FileHelper.SetPermissions(logger, path, file: isFile, permissions: permissions); - - FileHelper.ChangeOwner(logger, path, file: isFile, ownerGroup: OwnerGroup!); - - logMethod(logger.ToString()); - } -} -#endif \ No newline at end of file diff --git a/ComicNodes/Tests/_TestBase.cs b/ComicNodes/Tests/_TestBase.cs deleted file mode 100644 index 7d0740e8..00000000 --- a/ComicNodes/Tests/_TestBase.cs +++ /dev/null @@ -1,40 +0,0 @@ -#if(DEBUG) - -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace FileFlows.Comic.Tests; - - -[TestClass] -public abstract class TestBase -{ - /// - /// Gets the test logger - /// - internal TestLogger Logger = new TestLogger(); - - /// - /// The test context instance - /// - private TestContext? testContextInstance; - - /// - /// Gets or sets the test context - /// - public TestContext TestContext - { - get => testContextInstance!; - set => testContextInstance = value; - } - - public string TempPath = "/home/john/Comics/temp"; - - [TestInitialize] - public void TestInitialize() - { - if (Directory.Exists(this.TempPath) == false) - Directory.CreateDirectory(this.TempPath); - } -} - -#endif \ No newline at end of file diff --git a/FileFlows.Plugin.dll b/FileFlows.Plugin.dll index 9038a1a0..8f627406 100644 Binary files a/FileFlows.Plugin.dll and b/FileFlows.Plugin.dll differ diff --git a/FileFlows.Plugin.pdb b/FileFlows.Plugin.pdb index cdadc864..4219bd67 100644 Binary files a/FileFlows.Plugin.pdb and b/FileFlows.Plugin.pdb differ diff --git a/PluginTestLibrary/_LocalFileService.cs b/PluginTestLibrary/_LocalFileService.cs index a0a5eb74..ba54ff5d 100644 --- a/PluginTestLibrary/_LocalFileService.cs +++ b/PluginTestLibrary/_LocalFileService.cs @@ -82,6 +82,67 @@ public class LocalFileService : IFileService } } + /// + public Result DirectoryEmpty(string path, string[]? includePatterns = null) + { + if (IsProtectedPath(ref path)) + return Result.Fail("Cannot access protected path: " + path); + + try + { + if (!Directory.Exists(path)) + return Result.Success(true); // Path doesn't exist, considered empty + + // Get all files in the directory + var files = Directory.GetFiles(path); + + // If there are patterns, only count matching files + if (includePatterns != null && includePatterns.Length > 0) + { + foreach (var file in files) + { + foreach (var pattern in includePatterns) + { + try + { + if (System.Text.RegularExpressions.Regex.IsMatch(file, pattern.Trim(), + System.Text.RegularExpressions.RegexOptions.IgnoreCase)) + return Result.Success(false); // File matches, directory is not empty + } + catch (Exception) + { + // Handle regex exceptions silently, as per your example + } + } + } + } + else if (files.Length > 0) + { + // No patterns provided, directory is not empty if any file exists + return Result.Success(false); + } + + // Check for directories (subdirectories are not affected by includePatterns) + var dirs = Directory.GetDirectories(path); + if (dirs.Length > 0) + return Result.Success(false); // Directory contains subdirectories, not empty + + return Result.Success(true); // Directory is empty + } + catch (UnauthorizedAccessException ex) + { + return Result.Fail("Unauthorized access to path: " + path + " - " + ex.Message); + } + catch (IOException ex) + { + return Result.Fail("IO error while accessing path: " + path + " - " + ex.Message); + } + catch (Exception ex) + { + return Result.Fail("Error while accessing path: " + path + " - " + ex.Message); + } + } + public Result DirectoryDelete(string path, bool recursive = false) { if (IsProtectedPath(ref path))