diff --git a/AzureLiquid.Preview/PreviewProcess.cs b/AzureLiquid.Preview/PreviewProcess.cs index 8cbd8f3..432e82d 100644 --- a/AzureLiquid.Preview/PreviewProcess.cs +++ b/AzureLiquid.Preview/PreviewProcess.cs @@ -12,6 +12,11 @@ namespace AzureLiquid.Preview; /// public class PreviewProcess { + /// + /// The argument parser. + /// + private readonly PreviewProcessArguments _args; + /// /// Handles writing console output to private persisted log. /// @@ -32,6 +37,7 @@ public class PreviewProcess /// public PreviewProcess() { + _args = new PreviewProcessArguments(); Template = string.Empty; Content = string.Empty; Output = "./preview.txt"; @@ -68,7 +74,7 @@ public PreviewProcess() public string Output { get; set; } /// - /// Gets a value indicating whether the process should watch for changes to template or content files. + /// Gets or sets a value indicating whether the process should watch for changes to template or content files. /// /// /// true if should watch; otherwise, false. @@ -87,16 +93,19 @@ public PreviewProcess() /// /// Start a new instance of the class using the incoming arguments. /// - /// The process arguments + /// The process arguments. /// A new instance of the class. [ExcludeFromCodeCoverage] public static PreviewProcess Create(string[] args) { var preview = new PreviewProcess(); - // deepcode ignore XmlInjection: XML is not used by this application, it is passed back to the user, deepcode ignore XXE: - preview.ParseArguments(args); + preview.Template = preview._args.ParsePath(args, "template"); + preview.Content = preview._args.ParsePath(args, "content"); + preview.Output = preview._args.ParsePath(args, "output"); + HandleNoArgumentsPassed(args, preview); + if (preview.CanRender) { RenderAndWatch(preview); @@ -109,107 +118,6 @@ public static PreviewProcess Create(string[] args) return preview; } - /// Parses the arguments and sets process options. - /// - /// The arguments. Values are expected to be "--template", "--help", "--content", "--output" or - /// "--watch". - /// - private void ParseArguments(string[] args) - { - for (var index = 0; index < args.Length; index++) - { - var arg = args[index]; - var path = Directory.GetCurrentDirectory(); - ParseTemplate(args, index, arg, path); - ParseContent(args, index, arg, path); - ParseOutputResults(args, index, arg, path); - - // Switch watch param if needed - if (IsArgMatch(arg, "watch")) - { - ShouldWatch = true; - } - - // Show help info - if (IsArgMatch(arg, "help")) - { - WriteHelpOutput(); - } - } - } - - /// - /// Parses the output results file path if specified. - /// - /// The passed command arguments. - /// The parameter index. - /// The current argument. - /// The target path. - private void ParseOutputResults(string[] args, int index, string arg, string path) - { - if (!IsArgMatch(arg, "output") || index - 1 >= args.Length) - { - return; - } - - try - { - Output = Path.GetFullPath(args[index + 1], path); - } - catch - { - WriteErrorLine($"Invalid output path: {args[index + 1]}"); - } - } - - /// - /// Parses the incoming content file path if specified. - /// - /// The passed command arguments. - /// The parameter index. - /// The current argument. - /// The target path. - private void ParseContent(string[] args, int index, string arg, string path) - { - if (!IsArgMatch(arg, "content") || index - 1 >= args.Length) - { - return; - } - - try - { - Content = Path.GetFullPath(args[index + 1], path); - } - catch - { - WriteErrorLine($"Invalid content path: {args[index + 1]}"); - } - } - - /// - /// Parses the incoming template file path if specified. - /// - /// The passed command arguments. - /// The parameter index. - /// The current argument. - /// The target path. - private void ParseTemplate(string[] args, int index, string arg, string path) - { - if (!IsArgMatch(arg, "template") || index - 1 >= args.Length) - { - return; - } - - try - { - Template = Path.GetFullPath(args[index + 1], path); - } - catch - { - WriteErrorLine($"Invalid template path: {args[index + 1]}"); - } - } - /// /// Renders the output and watches for changes if specified. /// @@ -308,67 +216,102 @@ public string Render() { if (!CanRender) { - WriteErrorLine("Unable to render as inputs our outputs not found or not specified"); + WriteErrorLine("Unable to render as inputs or outputs not found or not specified"); return string.Empty; } - string content; - try + var content = ReadFileContent(Content); + if (string.IsNullOrEmpty(content)) { - content = File.ReadAllText(Content); + return string.Empty; } - catch (IOException) + + var template = ReadFileContent(Template); + if (string.IsNullOrEmpty(template)) { - // Lock issue, wait and retry - Thread.Sleep(TimeSpan.FromSeconds(1)); - return Render(); + return string.Empty; } - string template; + var parser = new LiquidParser(); + if (!SetParserContent(parser, content)) + { + return string.Empty; + } + + return RenderTemplate(parser, template); + } + + /// + /// Reads the file content. + /// + /// The file path. + /// The file content. + private string ReadFileContent(string filePath) + { try { - template = File.ReadAllText(Template); + return File.ReadAllText(filePath); } catch (IOException) { // Lock issue, wait and retry Thread.Sleep(TimeSpan.FromSeconds(1)); - return Render(); + return ReadFileContent(filePath); } + catch (Exception e) + { + LogWarning($"Unable to read file: {filePath}", e); + return string.Empty; + } + } - var parser = new LiquidParser(); - - if (Content.ToLowerInvariant().EndsWith(".json")) + /// + /// Sets the parser content. + /// + /// The parser. + /// The content. + /// + /// true if the content was set; otherwise, false. + /// + private bool SetParserContent(LiquidParser parser, string content) + { + try { - try + if (Content.ToLowerInvariant().EndsWith(".json")) { parser.SetContentJson(content); } - catch (Exception e) - { - LogWarning(" Unable to read input JSON file", e); - return string.Empty; - } - } - - if (Content.ToLowerInvariant().EndsWith(".xml")) - { - try + else if (Content.ToLowerInvariant().EndsWith(".xml")) { parser.SetContentXml(content); } - catch (Exception ex) + else { - LogWarning(" Unable to read input XML file", ex); - return string.Empty; + WriteErrorLine("Unsupported content type"); + return false; } } + catch (Exception e) + { + LogWarning("Unable to set parser content", e); + return false; + } + + return true; + } + /// + /// Renders the template. + /// + /// The parser. + /// The template. + /// The output from the template. + private string RenderTemplate(LiquidParser parser, string template) + { try { var output = parser.Parse(template).Render(); File.WriteAllText(Output, output); - return output; } catch (Exception e) @@ -376,8 +319,6 @@ public string Render() WriteErrorLine($"Error: {e.Message}"); return string.Empty; } - - // TODO: Refactor this method } /// diff --git a/AzureLiquid.Preview/PreviewProcessArguments.cs b/AzureLiquid.Preview/PreviewProcessArguments.cs new file mode 100644 index 0000000..91e6495 --- /dev/null +++ b/AzureLiquid.Preview/PreviewProcessArguments.cs @@ -0,0 +1,63 @@ +namespace AzureLiquid.Preview; + +/// +/// Handles the arguments passed to the preview process. +/// +public class PreviewProcessArguments +{ + /// + /// The current path of the process. + /// + private readonly string _path; + + /// + /// Initializes a new instance of the class. + /// + public PreviewProcessArguments() => _path = Directory.GetCurrentDirectory(); + + /// + /// Gets the index of the argument, if it exists. + /// + /// The key. + /// The index of the argument. + private static int GetArgumentIndex(string[] args, string key) + { + for (int i = 0; i < args?.Length; i++) + { + if (IsArgMatch(args[i], key)) + { + return i; + } + } + + return -1; + } + + /// + /// Determines whether the argument matches the partial argument key name. + /// + /// The argument. + /// The key. + /// + /// true if argument found; otherwise, false. + /// + private static bool IsArgMatch(string arg, string key) + { + return string.CompareOrdinal(arg, "--" + key) == 0; + } + + /// + /// Parses the argument value. + /// + /// The arguments. + /// The key. + /// The argument value. + public string ParsePath(string[] args, string key) + { + var index = GetArgumentIndex(args, key); + return + index == -1 || index - 1 >= args?.Length || args == null ? string.Empty : // No match, or no arguments passed + Path.GetFullPath(args[index + 1], _path) // Argument found, parsing path + ?? string.Empty; // Argument found, but path is invalid + } +} \ No newline at end of file diff --git a/AzureLiquid.Tests/PreviewProcessTests.cs b/AzureLiquid.Tests/PreviewProcessTests.cs index 0a3e993..9356741 100644 --- a/AzureLiquid.Tests/PreviewProcessTests.cs +++ b/AzureLiquid.Tests/PreviewProcessTests.cs @@ -72,7 +72,6 @@ public void EnsurePreviewParsingCSharpArguments() /// /// Ensure the preview process can be created from a set of arguments. /// - /// Determine if a log should be produced. /// First argument. /// Second argument. /// Third argument. @@ -82,16 +81,13 @@ public void EnsurePreviewParsingCSharpArguments() /// Seventh argument. /// Eighth argument. [Theory] - [InlineData(false, "", "", "", "", "", "", "", "")] - [InlineData(false, "--template", "./Resources/event.liquid", "--content", "./Resources/event.json", "--output", - "./Resources/preview.txt", "", "")] - [InlineData(false, "--template", "./Resources/event.liquid", "", "", "", "", "", "")] - [InlineData(false, "--watch", "", "", "", "", "", "", "")] - [InlineData(true, "--help", "", "", "", "", "", "", "")] - [InlineData(true, "--template", "./Resources/event_not_found.liquid", "--content", "./Resources/event.xml", - "--output", "./Resources/preview.txt", "", "")] - public void EnsureArgumentParsing(bool shouldLog, string arg1, string arg2, string arg3, string arg4, string arg5, - string arg6, string arg7, string arg8) + [InlineData("", "", "", "", "", "", "", "")] + [InlineData("--template", "./Resources/event.liquid", "--content", "./Resources/event.json", "--output", "./Resources/preview.txt", "", "")] + [InlineData("--template", "./Resources/event.liquid", "", "", "", "", "", "")] + [InlineData("--watch", "", "", "", "", "", "", "")] + [InlineData("--help", "", "", "", "", "", "", "")] + [InlineData("--template", "./Resources/event_not_found.liquid", "--content", "./Resources/event.xml", "--output", "./Resources/preview.txt", "", "")] + public void EnsureArgumentParsing(string arg1, string arg2, string arg3, string arg4, string arg5, string arg6, string arg7, string arg8) { // Arrange var args = new[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }; @@ -101,17 +97,11 @@ public void EnsureArgumentParsing(bool shouldLog, string arg1, string arg2, stri // Assert preview.Should().NotBeNull("A preview process should have been created"); - - if (shouldLog) - { - preview.Log.Should().NotBeEmpty("A log should have been created"); - } - else - { - preview.Log.Should().BeEmpty("No log should have been created"); - } } + /// + /// Ensure the preview process can be created from a set of arguments. + /// [Fact] public void EnsureObjectCreation() { @@ -153,8 +143,6 @@ public void EnsureWatcher() instance.Log.Should().NotBeEmpty("A log should have been created"); } - #region Nested type: Arrangement - /// /// Contains arranged values used for testing, containing mock instances and expected return values. /// @@ -222,6 +210,4 @@ public static string GetPath(string path) return Path.GetFullPath(path, basePath); } } - - #endregion } \ No newline at end of file