Skip to content

Commit

Permalink
feat: Implement PreviewProcessArguments class for argument handling a…
Browse files Browse the repository at this point in the history
…nd parsing
  • Loading branch information
github-actions[bot] committed Dec 2, 2024
1 parent 9b83c14 commit a05f503
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 160 deletions.
213 changes: 77 additions & 136 deletions AzureLiquid.Preview/PreviewProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ namespace AzureLiquid.Preview;
/// </summary>
public class PreviewProcess
{
/// <summary>
/// The argument parser.
/// </summary>
private readonly PreviewProcessArguments _args;

/// <summary>
/// Handles writing console output to private persisted log.
/// </summary>
Expand All @@ -32,6 +37,7 @@ public class PreviewProcess
/// </summary>
public PreviewProcess()
{
_args = new PreviewProcessArguments();
Template = string.Empty;
Content = string.Empty;
Output = "./preview.txt";
Expand Down Expand Up @@ -68,7 +74,7 @@ public PreviewProcess()
public string Output { get; set; }

/// <summary>
/// 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.
/// </summary>
/// <value>
/// <c>true</c> if should watch; otherwise, <c>false</c>.
Expand All @@ -87,16 +93,19 @@ public PreviewProcess()
/// <summary>
/// Start a new instance of the <see cref="PreviewProcess" /> class using the incoming arguments.
/// </summary>
/// <param name="args">The process arguments</param>
/// <param name="args">The process arguments.</param>
/// <returns>A new instance of the <see cref="PreviewProcess" /> class.</returns>
[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: <please specify a reason of ignoring this>
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);
Expand All @@ -109,107 +118,6 @@ public static PreviewProcess Create(string[] args)
return preview;
}

/// <summary>Parses the arguments and sets process options.</summary>
/// <param name="args">
/// The arguments. Values are expected to be "--template", "--help", "--content", "--output" or
/// "--watch".
/// </param>
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();
}
}
}

/// <summary>
/// Parses the output results file path if specified.
/// </summary>
/// <param name="args">The passed command arguments.</param>
/// <param name="index">The parameter index.</param>
/// <param name="arg">The current argument.</param>
/// <param name="path">The target path.</param>
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]}");
}
}

/// <summary>
/// Parses the incoming content file path if specified.
/// </summary>
/// <param name="args">The passed command arguments.</param>
/// <param name="index">The parameter index.</param>
/// <param name="arg">The current argument.</param>
/// <param name="path">The target path.</param>
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]}");
}
}

/// <summary>
/// Parses the incoming template file path if specified.
/// </summary>
/// <param name="args">The passed command arguments.</param>
/// <param name="index">The parameter index.</param>
/// <param name="arg">The current argument.</param>
/// <param name="path">The target path.</param>
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]}");
}
}

/// <summary>
/// Renders the output and watches for changes if specified.
/// </summary>
Expand Down Expand Up @@ -308,76 +216,109 @@ 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);
}

/// <summary>
/// Reads the file content.
/// </summary>
/// <param name="filePath">The file path.</param>
/// <returns>The file content.</returns>
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"))
/// <summary>
/// Sets the parser content.
/// </summary>
/// <param name="parser">The parser.</param>
/// <param name="content">The content.</param>
/// <returns>
/// <c>true</c> if the content was set; otherwise, <c>false</c>.
/// </returns>
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;
}

/// <summary>
/// Renders the template.
/// </summary>
/// <param name="parser">The parser.</param>
/// <param name="template">The template.</param>
/// <returns>The output from the template.</returns>
private string RenderTemplate(LiquidParser parser, string template)
{
try
{
var output = parser.Parse(template).Render();
File.WriteAllText(Output, output);

return output;
}
catch (Exception e)
{
WriteErrorLine($"Error: {e.Message}");
return string.Empty;
}

// TODO: Refactor this method
}

/// <summary>
Expand Down
63 changes: 63 additions & 0 deletions AzureLiquid.Preview/PreviewProcessArguments.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
namespace AzureLiquid.Preview;

/// <summary>
/// Handles the arguments passed to the preview process.
/// </summary>
public class PreviewProcessArguments
{
/// <summary>
/// The current path of the process.
/// </summary>
private readonly string _path;

/// <summary>
/// Initializes a new instance of the <see cref="PreviewProcessArguments" /> class.
/// </summary>
public PreviewProcessArguments() => _path = Directory.GetCurrentDirectory();

/// <summary>
/// Gets the index of the argument, if it exists.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>The index of the argument.</returns>
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;
}

/// <summary>
/// Determines whether the argument matches the partial argument key name.
/// </summary>
/// <param name="arg">The argument.</param>
/// <param name="key">The key.</param>
/// <returns>
/// <c>true</c> if argument found; otherwise, <c>false</c>.
/// </returns>
private static bool IsArgMatch(string arg, string key)
{
return string.CompareOrdinal(arg, "--" + key) == 0;
}

/// <summary>
/// Parses the argument value.
/// </summary>
/// <param name="args">The arguments.</param>
/// <param name="key">The key.</param>
/// <returns>The argument value.</returns>
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
}
}
Loading

0 comments on commit a05f503

Please sign in to comment.