using Serilog; using Spectre.Console; using System.Diagnostics; namespace csSiteGen; public static class Conversions{ public delegate bool ConvertFunc(FileInfo file, RuntimeSettings settings); /// /// A Mapping of filetype to ConvertFunc. /// public static readonly Dictionary Mappings = new(){ {".md", Pandoc}, }; /// /// TEST FUNCTION. /// public static bool NoOp(FileInfo file, RuntimeSettings settings){ Log.Information("Performing NoOp Conversion"); string newName = GetNewName(file,settings,"NoOp"); Log.Debug("{FullName} -> {newName}",file.FullName,newName); Thread.Sleep(1500); return true; } /// /// Copy the file verbatim /// public static bool RawCpy(FileInfo file, RuntimeSettings settings){ FileInfo newPath = new FileInfo(GetNewName(file,settings,null)); Log.Information("RawCpy: Copying {file} to {dest}",file.FullName, newPath.FullName); if (!newPath.Directory!.Exists) { newPath.Directory.Create(); } try { file.CopyTo(newPath.FullName, overwrite: true); } catch (Exception e){ Log.Fatal(e,"Copy Failed"); return false; } return true; } /// /// Execute pandoc on the file, automatically detecting the template to use. /// public static bool Pandoc(FileInfo file, RuntimeSettings settings){ Log.Information("Attempting to convert {file} using pandoc",file.Name); // Look for pandoc string pandoc = Utils.PathSearch("pandoc"); if (string.IsNullOrEmpty(pandoc)) { Console.WriteLine("Conversion failed due to dependency being unavailable."); return false; } Log.Information("Located pandoc binary."); // Look for template file. FileInfo? template = null; DirectoryInfo? searchDir = file.Directory; do { // This loop starts searching at the directory of the file, // and if a template is not found works up to the InputDirectory. // If no template is found at any level we simply run pandoc with no template. if (searchDir is null) // While it is unlikely this could happen the check is here to please the compiler. { break; } var result = searchDir.GetFiles(".template"); if (result.Length > 0) { template = result.First(); Log.Information("Found template {template}", template); break; } // Go up the tree. searchDir = searchDir.Parent; } while (searchDir != settings.InputDirectory); // Check last as we want to search the InputDirectory string pandocArgs = $"{file.FullName} -o {GetNewName(file,settings,".html")}"; if (template is not null) { pandocArgs += $" --template={template.FullName}"; } else { AnsiConsole.MarkupLine("[bold][[[orange1] Warning [/]]][/] Pandoc Template was not located."); Log.Warning("Pandoc template for {file} not found",file.Name); } return RunExternalProgram(pandoc,pandocArgs); } private static bool RunExternalProgram(string program, string args) { Log.Information("Executing {program}", program); Log.Debug("Full arguments {args}",args); using (Process RunProgram = new()) { // Reading stderr and stdout needs to be done carefully. string? stdout = null; string? stderr = null; RunProgram.StartInfo.UseShellExecute = false; RunProgram.StartInfo.FileName = program; RunProgram.StartInfo.CreateNoWindow = true; RunProgram.StartInfo.Arguments = args; RunProgram.StartInfo.RedirectStandardOutput = true; RunProgram.StartInfo.RedirectStandardError = true; // Add a handler to append stderr to the stderr string RunProgram.ErrorDataReceived += new DataReceivedEventHandler((sender, e) => {stderr += e.Data;}); RunProgram.OutputDataReceived += new DataReceivedEventHandler((sender, o) => {stdout += o.Data;}); RunProgram.Start(); RunProgram.BeginErrorReadLine(); RunProgram.BeginOutputReadLine(); RunProgram.WaitForExit(); Log.Debug("{program} stdout {stdout}", program, stdout); Log.Debug("{program} stderr {stderr}", program, stderr); if (RunProgram.ExitCode != 0) { Log.Error("{program} execution Failed. Check debug data for more information", program); return false; } } return true; } private static string GetNewName(FileInfo file, RuntimeSettings settings, string? newExtension){ return file.FullName .Replace(settings.InputDirectory.FullName, settings.OutputDirectory.FullName) .Replace(file.Extension,newExtension ?? file.Extension); } }