Skip to content

Commit

Permalink
adding env var handling
Browse files Browse the repository at this point in the history
  • Loading branch information
AC-4 committed May 19, 2024
1 parent 28ab431 commit c8d110b
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 21 deletions.
14 changes: 14 additions & 0 deletions src/Xcaciv.Command.Interface/IEnvironment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,19 @@ public interface IEnvironment
/// <param name="message"></param>
/// <returns></returns>
Task Complete(string? message);
/// <summary>
/// add a value to the environment, across commands
/// the storage mechanism should be apropriate to the running environment
/// probably a ConcurrentDictionary<TKey,TValue>
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
void SetValue(string key, string value);
/// <summary>
/// retrieve global environment value
/// </summary>
/// <param name="key"></param>
/// <returns>String.Empty if not found</returns>
string GetValue(string key);
}
}
1 change: 1 addition & 0 deletions src/Xcaciv.Command.Interface/ITextIoContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public interface ITextIoContext : IEnvironment, IInputContext, IOutputContext, I
Guid? Parent { get; }
/// <summary>
/// create a child output context
/// MUST pass down expected Environment values
/// may track the instance for later use
/// </summary>
/// <param name="childArguments">arguments to pass to child context</param>
Expand Down
28 changes: 28 additions & 0 deletions src/Xcaciv.Command.Tests/Commands/SayCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Text;
using System.Threading.Tasks;
using Xcaciv.Command.FileLoader;
using Xcaciv.Command.Commands;

namespace Xcaciv.Command.Tests.Commands
{
Expand All @@ -25,5 +26,32 @@ public async Task HandleExecutionTest()
// by looking at the output of the second output line
Assert.Equal("what is up", textio.Children.First().Output.First());
}

[Fact()]
public void ProcessEnvValuesTest()
{
var textio = new TestImpementations.TestTextIo();
textio.SetValue("direction", "up");

var actual = SayCommand.ProcessEnvValues("what is %direction%!", textio);

Assert.Equal("what is up!", actual);
}

[Fact()]
public async Task HandleExecutionWithEnvTest()
{
var commands = new CommandController(new Crawler(), "");
commands.EnableDefaultCommands();

var textio = new TestImpementations.TestTextIo();
textio.SetValue("direction", "up");
// simulate user input
await commands.Run(@"say ""what is %direction%!""", textio);

// verify the output of the first run
// by looking at the output of the second output line
Assert.Equal("what is up!", textio.Children.First().Output.First());
}
}
}
19 changes: 15 additions & 4 deletions src/Xcaciv.Command.Tests/TestImpementations/TestTextIo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,22 @@ public class TestTextIo : AbstractTextIo
/// </summary>
public List<string> Output { get; private set; } = new List<string>();

public List<string> Trace { get; private set; } = new List<string>();

public Dictionary<string, string> PromptAnswers { get; private set; } = new Dictionary<string, string>();

public TestTextIo(string[]? arguments = null) : base("TestTextIo", null)
public TestTextIo(string[]? arguments = null, Dictionary<string, string>? envVars = default) : base("TestTextIo", null)
{
this.Parameters = arguments ?? string.Empty.Split(' ');
this.Verbose = true;
if (envVars != null)
this.EnvironmentVariables = new System.Collections.Concurrent.ConcurrentDictionary<string, string>(envVars);
}

public override Task<ITextIoContext> GetChild(string[]? childArguments = null)
{
var child = new TestTextIo(childArguments)
var envVarsCopy = this.EnvironmentVariables.ToDictionary() ;
var child = new TestTextIo(childArguments, envVarsCopy)
{
Parent = Id
};
Expand Down Expand Up @@ -83,12 +88,18 @@ public override string ToString()
}
}



output += string.Join('-', Output);

return output;
}

public override Task AddTraceMessage(string message)
{
Trace.Add(message);
// if we are not verbose, send the output to DEBUG
System.Diagnostics.Debug.WriteLine(message);
return Task.CompletedTask;
}

}
}
49 changes: 45 additions & 4 deletions src/Xcaciv.Command/AbstractTextIo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.PortableExecutable;
Expand All @@ -18,7 +19,7 @@ namespace Xcaciv.Command
/// </remarks>
/// <param name="name"></param>
/// <param name="parentId"></param>
public abstract class AbstractTextIo(string name, Guid? parentId = null) : ITextIoContext
public abstract class AbstractTextIo(string name, Guid? parentId = default) : ITextIoContext
{
public bool Verbose { get; set; } = false;

Expand All @@ -34,7 +35,11 @@ public abstract class AbstractTextIo(string name, Guid? parentId = null) : IText

protected ChannelReader<string>? inputPipe;
protected ChannelWriter<string>? outputPipe;

/// <summary>
/// implementation must set the expected child's properties and pass environment values
/// </summary>
/// <param name="childArguments"></param>
/// <returns></returns>
public abstract Task<ITextIoContext> GetChild(string[]? childArguments = null);
/// <summary>
/// handles the Channel output and allows the implementation to handle
Expand Down Expand Up @@ -127,10 +132,46 @@ public virtual Task AddTraceMessage(string message)
{
if (this.Verbose)
{
return this.SetStatusMessage(message);
return this.OutputChunk("\tTRACE" + message);
}

// if we are not verbose, send the output to DEBUG
System.Diagnostics.Debug.WriteLine(message);
return Task.CompletedTask;
}
/// <summary>
/// Thread safe collection of env vars
/// MUST be set when creating a child!
/// </summary>
protected ConcurrentDictionary<string, string> EnvironmentVariables { get; set; } = new ConcurrentDictionary<string, string>();
/// <summary>
/// <see cref="Xcaciv.Command.Interface.IEnvironment"/>
/// </summary>
/// <param name="key"></param>
/// <param name="addValue"></param>
/// <returns></returns>
public void SetValue(string key, string addValue)
{
// make case insensitive var names
key = key.ToUpper();
EnvironmentVariables.AddOrUpdate(key, addValue, (key, value) =>
{
AddTraceMessage($"Environment value {key} changed from {value} to {addValue}.").Wait();
return addValue;
});
}
/// <summary>
/// <see cref="Xcaciv.Command.Interface.IEnvironment"/>
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string GetValue(string key)
{
// make case insensitive var names
key = key.ToUpper();

string? returnValue;
EnvironmentVariables.TryGetValue(key, out returnValue);
return returnValue ?? String.Empty;
}
}
}
10 changes: 5 additions & 5 deletions src/Xcaciv.Command/Commands/AbstractCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,25 @@ public virtual void Help(IOutputContext outputContext)
outputContext.OutputChunk($"[{BaseCommand}] ({FriendlyName}): {HelpString}");
}

public async IAsyncEnumerable<string> Main(IInputContext input, IStatusContext statusContext)
public async IAsyncEnumerable<string> Main(IInputContext input, IEnvironment environment)
{
if (input.HasPipedInput)
{
await foreach (var p in input.ReadInputPipeChunks())
{
if (string.IsNullOrEmpty(p)) continue;
yield return this.HandlePipedChunk(p, input.Parameters, statusContext);
yield return this.HandlePipedChunk(p, input.Parameters, environment);
}
}
else
{
yield return HandleExecution(input.Parameters, statusContext);
yield return HandleExecution(input.Parameters, environment);
}
}

public abstract string HandlePipedChunk(string pipedChunk, string[] parameters, IStatusContext status);
public abstract string HandlePipedChunk(string pipedChunk, string[] parameters, IEnvironment env);

public abstract string HandleExecution(string[] parameters, IStatusContext status);
public abstract string HandleExecution(string[] parameters, IEnvironment env);

}
}
4 changes: 2 additions & 2 deletions src/Xcaciv.Command/Commands/RegifCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class RegifCommand : AbstractCommand

protected string regex { get; set; } = string.Empty;

public override string HandleExecution(string[] parameters, IStatusContext status)
public override string HandleExecution(string[] parameters, IEnvironment status)
{
var output = new StringBuilder();
setRegexExpression(parameters);
Expand All @@ -39,7 +39,7 @@ public override string HandleExecution(string[] parameters, IStatusContext statu
return output.ToString().Trim();
}

public override string HandlePipedChunk(string stringToCheck, string[] parameters, IStatusContext status)
public override string HandlePipedChunk(string stringToCheck, string[] parameters, IEnvironment status)
{
if (parameters.Length > 0)
{
Expand Down
38 changes: 33 additions & 5 deletions src/Xcaciv.Command/Commands/SayCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Xcaciv.Command.Interface;

Expand All @@ -15,16 +16,43 @@ public class SayCommand : AbstractCommand
public override string FriendlyName { get; } = "say something";


public override string HelpString { get; } = "SAY <thing to print>";
public override string HelpString { get; } = @"SAY <thing to print> \n\tUse double quotes and %<env var name>% to embed values from the environment.";

public override string HandleExecution(string[] parameters, IStatusContext status)
public override string HandleExecution(string[] parameters, IEnvironment env)
{
return String.Join(" ", parameters);
var builder = new StringBuilder();
foreach (var parameter in parameters) // zero position contains command
{
var value = parameter.ToString();
if (value.Contains('%')) value = ProcessEnvValues(value, env);
builder.Append(value);
builder.Append(' ');
}
var result = builder.ToString();
return result[..^1];
}

public override string HandlePipedChunk(string pipedChunk, string[] parameters, IStatusContext status)
public static string ProcessEnvValues(string value, IEnvironment env)
{
return pipedChunk;
Regex regex = new Regex(@"%(.\w*?)%");
return regex.Replace(value, match =>
{
string variable = match.Groups[1].Value;
string value = env.GetValue(variable);
if (!String.IsNullOrEmpty(value))
{
return value;
}
else
{
return '%'+match.Value+'%';
}
});
}

public override string HandlePipedChunk(string pipedChunk, string[] parameters, IEnvironment env)
{
return ProcessEnvValues(pipedChunk, env);
}

}
Expand Down
55 changes: 55 additions & 0 deletions src/Xcaciv.Command/Commands/SetCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xcaciv.Command.Interface;

namespace Xcaciv.Command.Commands
{
internal class SetCommand : AbstractCommand
{
public override string BaseCommand => "SET";

public override string FriendlyName => "Set environment values";

public override string HelpString => "SET <varname> = <value>";

public override string HandleExecution(string[] parameters, IEnvironment env)
{
var varName = String.Empty;
var varValue = String.Empty;

foreach (var token in parameters)
{
// skip assignment character
if (token == "=") continue;
if (String.IsNullOrEmpty(varName))
{
// set case insenstive var name removing assignment character from ends
varName = token.Trim('=');
}
else if (String.IsNullOrEmpty(varValue))
{
// set value removing assignment character from ends
varValue = token.Trim('=');
}

// when we get both, add it to the values
if (!String.IsNullOrEmpty(varName) && !String.IsNullOrEmpty(varValue))
{
env.SetValue(varName, varValue);
varName = String.Empty;
varValue = String.Empty;
}
}
// nothing to display
return String.Empty;
}

public override string HandlePipedChunk(string pipedChunk, string[] parameters, IEnvironment status)
{
throw new NotImplementedException();
}
}
}
2 changes: 1 addition & 1 deletion src/zTestCommandPackage/EchoCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public void Help(IOutputContext outputContext)
outputContext.OutputChunk($"[{BaseCommand}] ({FriendlyName}) - test command to output each parameter as a chunk");
}

public async IAsyncEnumerable<string> Main(IInputContext input, IStatusContext statusContext)
public async IAsyncEnumerable<string> Main(IInputContext input, IEnvironment statusContext)
{
await statusContext.SetStatusMessage($"{this.BaseCommand} test start");
if (input.HasPipedInput)
Expand Down

0 comments on commit c8d110b

Please sign in to comment.