using System; using System.IO; using System.Runtime.InteropServices; using JetBrains.Annotations; using UnityEngine; namespace NugetForUnity.Helper { /// /// Helper class for file system operations. /// internal static class FileSystemHelper { /// /// Recursively moves all files and sub-directories from one directory to another. /// If a file or directory already exists inside the target directory, it will be overwritten. /// /// The path to the folder to move from. /// The path to the folder to move to. internal static void DirectoryMove([NotNull] string sourceDirectoryPath, [NotNull] string destDirectoryPath) { if (!Directory.Exists(destDirectoryPath)) { // target doesn't exist so we can simply move the folder NugetLogger.LogVerbose("Moving {0} to {1}", sourceDirectoryPath, destDirectoryPath); Directory.Move(sourceDirectoryPath, destDirectoryPath); return; } // move the files var files = Directory.EnumerateFiles(sourceDirectoryPath); foreach (var file in files) { var newFilePath = Path.Combine(destDirectoryPath, Path.GetFileName(file)); try { NugetLogger.LogVerbose("Moving {0} to {1}", file, newFilePath); DeleteFile(newFilePath); File.Move(file, newFilePath); } catch (Exception e) { Debug.LogWarningFormat("'{0}' couldn't be moved to '{1}'. \n{2}", file, newFilePath, e); } } // move the directories var directories = Directory.EnumerateDirectories(sourceDirectoryPath); foreach (var directory in directories) { var newDirectoryPath = Path.Combine(destDirectoryPath, Path.GetFileName(directory)); try { NugetLogger.LogVerbose("Moving {0} to {1}", directory, newDirectoryPath); if (Directory.Exists(newDirectoryPath)) { DeleteDirectory(newDirectoryPath, false); } Directory.Move(directory, newDirectoryPath); } catch (Exception e) { Debug.LogWarningFormat("'{0}' couldn't be moved to '{1}'. \n{2}", directory, newDirectoryPath, e); } } } /// /// Recursively deletes the folder at the given path. /// NOTE: Directory.Delete() doesn't delete Read-Only files, whereas this does. /// /// The path of the folder to delete. /// Whether to log the deletion. internal static void DeleteDirectory([NotNull] string directoryPath, bool log) { if (!Directory.Exists(directoryPath)) { return; } if (log) { NugetLogger.LogVerbose("Deleting directory: {0}", directoryPath); } var directoryInfo = new DirectoryInfo(directoryPath); // delete any sub-folders first foreach (var childInfo in directoryInfo.EnumerateDirectories()) { DeleteDirectory(childInfo.FullName, false); } // remove the read-only flag on all files var files = directoryInfo.EnumerateFiles(); foreach (var file in files) { file.Attributes = FileAttributes.Normal; } // remove the read-only flag on the directory directoryInfo.Attributes = FileAttributes.Normal; // delete the directory try { directoryInfo.Delete(true); } catch (UnauthorizedAccessException e) when (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && e.Message.Contains(".dll")) { Debug.LogError( "Windows was unable to delete all the files, if you are using Native files this is because the file is in use by Unity. Please restart Unity to remove all the files."); } } /// /// Deletes a file at the given file-path. /// /// The file-path to the file to delete. internal static void DeleteFile([NotNull] string filePath) { if (!File.Exists(filePath)) { return; } File.SetAttributes(filePath, FileAttributes.Normal); try { File.Delete(filePath); } catch (UnauthorizedAccessException e) when (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && e.Message.Contains(".dll")) { Debug.LogError( "Windows was unable to delete all the files, if you are using Native files this is because the file is in use by Unity. Please restart Unity to remove all the files."); } } /// /// Moves a file from one location to another. If the destination file already exists, it is overwritten. /// /// The path of the file that should be moved. /// The target path where the file should be moved to. /// Whether we should check if there is a file located at . internal static void MoveFile([NotNull] string sourceFilePath, [NotNull] string destFilePath, bool checkSourceExists) { if (checkSourceExists && !File.Exists(sourceFilePath)) { throw new FileNotFoundException($"Source file does not exist or could not be found: {sourceFilePath}"); } NugetLogger.LogVerbose("Moving {0} to {1}", sourceFilePath, destFilePath); DeleteFile(destFilePath); File.Move(sourceFilePath, destFilePath); } /// /// Deletes all files in the given directory and in any sub-directory, with the given filter. /// /// The path to the directory to delete all files of the given extension from. /// The filter of the files to delete, in the form "*.ext". internal static void DeleteAllFiles([NotNull] string directoryPath, [NotNull] string filter) { var files = Directory.EnumerateFiles(directoryPath, filter, SearchOption.AllDirectories); foreach (var file in files) { DeleteFile(file); } } /// /// Replace all %20 encodings with a normal space. /// /// The path to the directory. internal static void FixSpaces([NotNull] string directoryPath) { if (directoryPath.Contains("%20")) { NugetLogger.LogVerbose("Removing %20 from {0}", directoryPath); Directory.Move(directoryPath, directoryPath.Replace("%20", " ")); directoryPath = directoryPath.Replace("%20", " "); } var subdirectories = Directory.EnumerateDirectories(directoryPath); foreach (var subDir in subdirectories) { FixSpaces(subDir); } var files = Directory.EnumerateFiles(directoryPath); foreach (var file in files) { if (file.Contains("%20")) { NugetLogger.LogVerbose("Removing %20 from {0}", file); File.Move(file, file.Replace("%20", " ")); } } } } }