using DiscUtils.Iso9660; using Microsoft.Extensions.Logging; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO.Compression; using System.Text.Json; using System.Text.Json.Serialization; namespace File_Folder_Helper.ADO2024.PI4; internal static partial class Helper20241217 { private record SecureShell( ); private record ServerMessageBlock(string Path, bool Required); private record Target(SecureShell? SecureShell, ServerMessageBlock? ServerMessageBlock); private record File(long LastWriteTicks, long Length, string RelativePath); private record Record(string DestinationDirectory, string DirectoryName, Job Job, string Path, string Snap2HyperTextMarkupLanguage, string SourceDirectory); private record Job(string? AlternatePath, string Directory, string Extension, File[] Files, int FilesCount, double FilesTotalLength, int Keep, Target[] Targets); [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(Job))] private partial class JobSourceGenerationContext : JsonSerializerContext { } [JsonSourceGenerationOptions(WriteIndented = true)] [JsonSerializable(typeof(File[]))] private partial class FilesSourceGenerationContext : JsonSerializerContext { } private static ReadOnlyCollection GetFiles(string searchPattern, string[] ignoreFileNames, Record record) => GetFiles(record.SourceDirectory, searchPattern, ignoreFileNames); internal static void Backup(ILogger logger, List args) { Job jobNew; string path; string? json; string fileName; bool areTheyTheSame; IEnumerable records; logger.LogInformation(args[0]); logger.LogInformation(args[1]); logger.LogInformation(args[2]); logger.LogInformation(args[3]); logger.LogInformation(args[4]); ReadOnlyCollection files; string searchPattern = args[2]; IEnumerable searchPatternFiles; string[] ignoreFileNames = args[3].Split('~'); string destination = Path.GetFullPath(args[4]); string sourceDirectory = Path.GetFullPath(args[0]); string? snap2HyperTextMarkupLanguage = args.Count < 6 ? null : Path.GetFullPath(args[5]); logger.LogInformation("Searching <{sourceDirectory}> with search pattern {searchPattern}", args[0], searchPattern); if (Debugger.IsAttached) Verify(searchPattern, ignoreFileNames); for (int i = 1; i < 3; i++) { if (i == 1) { searchPatternFiles = Directory.EnumerateFiles(sourceDirectory, searchPattern, new EnumerationOptions { IgnoreInaccessible = true, RecurseSubdirectories = true }); } else if (i == 2) { searchPatternFiles = Directory.GetFiles(sourceDirectory, searchPattern, SearchOption.AllDirectories); } else throw new NotImplementedException(); records = GetRecords(sourceDirectory, destination, searchPatternFiles); foreach (Record record in records) { if (record.Job is null || string.IsNullOrEmpty(record.Job.Extension)) continue; logger.LogInformation("Searching <{directory}>", record.SourceDirectory); if (snap2HyperTextMarkupLanguage is not null && System.IO.File.Exists(snap2HyperTextMarkupLanguage)) WriteSnap2HyperTextMarkupLanguage(logger, snap2HyperTextMarkupLanguage, record); files = GetFiles(searchPattern, ignoreFileNames, record); jobNew = GetJob(searchPattern, ignoreFileNames, record, files); json = JsonSerializer.Serialize(jobNew, JobSourceGenerationContext.Default.Job); areTheyTheSame = GetAreTheyTheSame(logger, searchPattern, ignoreFileNames, record, jobNew); if (areTheyTheSame) { WriteAllText(record.Path, json); continue; } fileName = $"{record.DirectoryName}-{DateTime.Now:yyyy-MM-dd-HH-mm-ss-fff}{record.Job.Extension}"; path = Path.Combine(record.DestinationDirectory, fileName); logger.LogInformation("Writing <{path}> extension", path); WritePassedExtension(record, files, record.DirectoryName, path); logger.LogInformation("Wrote <{path}> extension", path); logger.LogInformation("Moved <{path}> extension", path); WriteAllText(record, json, path); } } } private static void Verify(string searchPattern, string[] ignoreFileNames) { List targets = [ new(new SecureShell(), null), new(null, new ServerMessageBlock("\\\\mesfs.infineon.com\\EC_APC\\DEV", true)) ]; string directory = Path.Combine(Environment.CurrentDirectory, ".vscode", "helper"); if (!Directory.Exists(directory)) _ = Directory.CreateDirectory(directory); string path = Path.Combine(directory, "verify.json"); ReadOnlyCollection files = GetFiles(directory, searchPattern, ignoreFileNames); ReadOnlyCollection collection = GetFilteredFiles(searchPattern, ignoreFileNames, files); double filesTotalLength = collection.Select(l => l.Length).Sum(); Job job = new(AlternatePath: "C:/Users/phares", Directory: directory, Extension: ".iso", Files: collection.ToArray(), FilesCount: files.Count, FilesTotalLength: filesTotalLength, Keep: 3, Targets: targets.ToArray()); string json = JsonSerializer.Serialize(job, JobSourceGenerationContext.Default.Job); WriteAllText(path, json); } private static ReadOnlyCollection GetFilteredFiles(string searchPattern, string[] ignoreFileNames, ReadOnlyCollection files) { List results = []; string fileName; foreach (File file in files) { if (file.RelativePath == searchPattern) continue; fileName = Path.GetFileName(file.RelativePath); if (fileName == searchPattern) throw new Exception("Found nested file!"); if (ignoreFileNames.Any(l => l == fileName)) continue; if (file.Length == 0) continue; results.Add(file); } return results.AsReadOnly(); } private static IEnumerable GetRecords(string directory, string destination, IEnumerable files) { Job? job; string json; Record record; string fileName; string directoryName; string sourceDirectory; string destinationDirectory; string snap2HyperTextMarkupLanguage; foreach (string file in files) { fileName = Path.GetFileName(file); sourceDirectory = Path.GetDirectoryName(file) ?? throw new Exception(); directoryName = Path.GetFileName(sourceDirectory); if (!fileName.StartsWith('.')) { System.IO.File.Move(file, Path.Combine(sourceDirectory, $".{fileName}")); continue; } json = System.IO.File.ReadAllText(file); snap2HyperTextMarkupLanguage = Path.Combine(sourceDirectory, ".html"); if (string.IsNullOrEmpty(json) || json is "{}" or "[]") job = null; else job = JsonSerializer.Deserialize(json, JobSourceGenerationContext.Default.Job); job ??= new(AlternatePath: null, Directory: directory, Extension: ".iso", Files: [], FilesCount: 0, FilesTotalLength: 0, Keep: 3, Targets: []); destinationDirectory = $"{destination}{sourceDirectory[2..]}"; if (!Directory.Exists(destinationDirectory)) _ = Directory.CreateDirectory(destinationDirectory); record = new(DestinationDirectory: destinationDirectory, DirectoryName: directoryName, Job: job, Path: file, Snap2HyperTextMarkupLanguage: snap2HyperTextMarkupLanguage, SourceDirectory: sourceDirectory); yield return record; } } private static void WriteSnap2HyperTextMarkupLanguage(ILogger logger, string snap2HyperTextMarkupLanguage, Record record) { string title = Path.GetFileName(record.SourceDirectory); ProcessStartInfo processStartInfo = new() { Arguments = $"-path:\"{record.SourceDirectory}\" -outfile:\"{record.Snap2HyperTextMarkupLanguage}\" -title:\"{title}\" -hidden -silent", FileName = snap2HyperTextMarkupLanguage, RedirectStandardError = true, RedirectStandardInput = true, RedirectStandardOutput = true, WorkingDirectory = record.SourceDirectory }; Process? process = Process.Start(processStartInfo) ?? throw new Exception("Process should not be null."); process.WaitForExit(); logger.LogInformation($"Snap2HyperTextMarkupLanguage: {process.StandardOutput.ReadToEnd()}{Environment.NewLine}{process.StandardError.ReadToEnd()}{Environment.NewLine}{process.ExitCode}"); } private static ReadOnlyCollection GetFiles(string directory, string searchPattern, string[] ignoreFileNames) { List results = []; File file; string relativePath; string[] files = Directory.GetFiles(directory, "*", SearchOption.AllDirectories); FileInfo[] fileInfoCollection = files.Select(l => new FileInfo(l)).ToArray(); foreach (FileInfo fileInfo in fileInfoCollection) { if (fileInfo.Name == searchPattern) continue; if (ignoreFileNames.Any(l => l == fileInfo.Name)) continue; if (!string.IsNullOrEmpty(fileInfo.LinkTarget)) continue; relativePath = Path.GetRelativePath(directory, fileInfo.FullName).Replace(';', '_'); if (relativePath.StartsWith("..")) relativePath = relativePath[3..]; file = new(LastWriteTicks: fileInfo.LastWriteTime.Ticks, Length: fileInfo.Length, RelativePath: relativePath); results.Add(file); } return results.AsReadOnly(); } private static Job GetJob(string searchPattern, string[] ignoreFileNames, Record record, ReadOnlyCollection files) { Job result; ReadOnlyCollection collection = GetFilteredFiles(searchPattern, ignoreFileNames, files); double filesTotalLengthNew = collection.Select(l => l.Length).Sum(); result = new(AlternatePath: record.Job.AlternatePath, Directory: record.SourceDirectory, Extension: record.Job.Extension, Files: collection.ToArray(), FilesCount: collection.Count, FilesTotalLength: filesTotalLengthNew, Keep: record.Job.Keep, Targets: record.Job.Targets); return result; } private static bool GetAreTheyTheSame(ILogger logger, string searchPattern, string[] ignoreFileNames, Record record, Job jobNew) { bool result; ReadOnlyCollection collection = GetFilteredFiles(searchPattern, ignoreFileNames, record.Job.Files.AsReadOnly()); int filesCountOld = collection.Count; int filesCountNew = jobNew.Files.Length; if (filesCountNew != filesCountOld) { result = false; logger.LogInformation("<{directory}> file count has changed {filesCountNew} != {filesCountOld}", record.SourceDirectory, filesCountNew, filesCountOld); } else { double filesTotalLengthOld = collection.Select(l => l.Length).Sum(); double filesTotalLengthNew = jobNew.Files.Select(l => l.Length).Sum(); if (filesTotalLengthNew != filesTotalLengthOld) { result = false; logger.LogInformation("<{directory}> file length has changed {filesTotalLengthNew} != {filesTotalLengthOld}", record.SourceDirectory, filesTotalLengthNew, filesTotalLengthOld); } else { string jsonNew = JsonSerializer.Serialize(jobNew.Files, FilesSourceGenerationContext.Default.FileArray); string jsonOld = JsonSerializer.Serialize(collection.ToArray(), FilesSourceGenerationContext.Default.FileArray); if (jsonNew == jsonOld) result = true; else { result = false; if (Debugger.IsAttached) { WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", "old.json"), jsonOld); WriteAllText(Path.Combine(Environment.CurrentDirectory, ".vscode", "helper", "new.json"), jsonNew); } logger.LogInformation("<{directory}> file serialized are different {filesTotalLengthNew} != {filesTotalLengthOld}", record.SourceDirectory, filesTotalLengthNew, filesTotalLengthOld); } } } return result; } private static void WriteAllText(string path, string text) { string check = !System.IO.File.Exists(path) ? string.Empty : System.IO.File.ReadAllText(path); if (check != text) System.IO.File.WriteAllText(path, text); } private static void WritePassedExtension(Record record, ReadOnlyCollection files, string directoryName, string path) { if (record.Job.Extension.Equals(".iso", StringComparison.OrdinalIgnoreCase)) WriteISO(record, files, path, directoryName); else if (record.Job.Extension.Equals(".zip", StringComparison.OrdinalIgnoreCase)) WriteZIP(record, files, path); else throw new NotImplementedException(); } private static void WriteISO(Record record, ReadOnlyCollection files, string path, string directoryName) { CDBuilder builder = new() { UseJoliet = true, VolumeIdentifier = directoryName.Length < 25 ? directoryName : directoryName[..25] }; foreach (File file in files) _ = builder.AddFile(file.RelativePath, Path.Combine(record.SourceDirectory, file.RelativePath)); builder.Build(path); } private static void WriteZIP(Record record, ReadOnlyCollection files, string path) { using ZipArchive zip = ZipFile.Open(path, ZipArchiveMode.Create); string directoryEntry; List directoryEntries = []; foreach (File file in files) { directoryEntry = Path.GetDirectoryName(file.RelativePath) ?? throw new Exception(); if (!directoryEntries.Contains(directoryEntry)) continue; directoryEntries.Add(directoryEntry); _ = zip.CreateEntry(file.RelativePath); } foreach (File file in files) _ = zip.CreateEntryFromFile(Path.Combine(record.SourceDirectory, file.RelativePath), file.RelativePath); } private static void WriteAllText(Record record, string text, string path) { WriteAllText(record.Path, text); System.IO.File.Copy(record.Path, $"{path}.json"); string checkFile = Path.Combine(record.SourceDirectory, ".html"); if (System.IO.File.Exists(checkFile)) System.IO.File.Copy(checkFile, $"{path}.html"); } }