feat: initial cli implementation

This commit is contained in:
Stevan Freeborn
2026-04-17 15:28:59 -05:00
parent ab57ac5a13
commit ba927c0e18
41 changed files with 1124 additions and 17 deletions
@@ -0,0 +1,51 @@
using System.CommandLine;
using StevanFreeborn.Extensions.Configuration.Secure.Configuration;
namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands;
internal sealed class DeleteCommandFactory(
ISecureConfig secureConfig,
ITerminal terminal
) : ICommandFactory
{
private const string CommandName = "delete";
private const string CommandDescription = "Delete the value for the given key";
private readonly Argument<string> _keyArg = new("key")
{
Description = "The key whose value you want to remove."
};
private readonly ISecureConfig _secureConfig = secureConfig;
private readonly ITerminal _terminal = terminal;
public Command Create()
{
var command = new Command(CommandName, CommandDescription)
{
_keyArg,
};
command.SetAction(HandleAsync);
return command;
}
private async Task<int> HandleAsync(ParseResult result, CancellationToken ct)
{
var key = result.GetRequiredValue(_keyArg);
var isDeleted = await _secureConfig.DeleteAsync(key, ct);
if (isDeleted)
{
_terminal.WriteLine($"Successfully removed value for {key}");
return ExitCodes.Success;
}
else
{
_terminal.WriteLine($"Unable to remove value for {key}");
return ExitCodes.KeyNotRemoved;
}
}
}
@@ -0,0 +1,8 @@
namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands;
internal static class ExitCodes
{
public const int Success = 0;
public const int KeyNotFound = 1;
public const int KeyNotRemoved = 2;
}
@@ -0,0 +1,80 @@
using System.CommandLine;
using System.Text.Json;
using StevanFreeborn.Extensions.Configuration.Secure.Cli.Json;
using StevanFreeborn.Extensions.Configuration.Secure.Configuration;
namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands;
internal sealed class GetCommandFactory(
ISecureConfig secureConfig,
ITerminal terminal
) : ICommandFactory
{
private const string CommandName = "get";
private const string CommandDescription = "Retrieves the value for the given key";
private readonly Argument<string> _keyArg = new("key")
{
Description = "The key whose value you want to retrieve."
};
private readonly Option<bool> _prettyOption = new("--pretty", "-p")
{
Description = "Indicates whether the value - if JSON - should be pretty printed or not"
};
private readonly ISecureConfig _secureConfig = secureConfig;
private readonly ITerminal _terminal = terminal;
public Command Create()
{
var command = new Command(CommandName, CommandDescription)
{
_keyArg,
_prettyOption
};
command.SetAction(HandleAsync);
return command;
}
private async Task<int> HandleAsync(ParseResult result, CancellationToken ct)
{
var key = result.GetRequiredValue(_keyArg);
var shouldPrintPretty = result.GetValue(_prettyOption);
var value = await _secureConfig.GetAsync<string>(key, ct);
if (value is null)
{
_terminal.WriteLine($"Unable to retrieve value for {key}");
return ExitCodes.KeyNotFound;
}
try
{
using var json = JsonDocument.Parse(value);
if (shouldPrintPretty)
{
var options = new JsonSerializerOptions()
{
WriteIndented = true,
};
var context = new CliJsonContext(options);
value = JsonSerializer.Serialize(json.RootElement, context.JsonElement);
}
else
{
value = JsonSerializer.Serialize(json.RootElement, CliJsonContext.Default.JsonElement);
}
}
catch (JsonException)
{
}
_terminal.WriteLine(value);
return ExitCodes.Success;
}
}
@@ -0,0 +1,8 @@
using System.CommandLine;
namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands;
internal interface ICommandFactory
{
Command Create();
}
@@ -0,0 +1,6 @@
namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands;
internal interface ITerminal
{
public void WriteLine(string value);
}
@@ -0,0 +1,63 @@
using System.CommandLine;
using System.Text.Json;
using StevanFreeborn.Extensions.Configuration.Secure.Cli.Json;
using StevanFreeborn.Extensions.Configuration.Secure.Configuration;
namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands;
internal sealed class SetCommandFactory(
ISecureConfig secureConfig,
ITerminal terminal
) : ICommandFactory
{
private const string CommandName = "set";
private const string CommandDescription = "Sets the value for the given key";
private readonly Argument<string> _keyArg = new("key")
{
Description = "The key whose value you want to set."
};
private readonly Argument<string> _valueArg = new("value")
{
Description = "The value that you want to set for the key."
};
private readonly ISecureConfig _secureConfig = secureConfig;
private readonly ITerminal _terminal = terminal;
public Command Create()
{
var command = new Command(CommandName, CommandDescription)
{
_keyArg,
_valueArg,
};
command.SetAction(HandleAsync);
return command;
}
private async Task<int> HandleAsync(ParseResult result, CancellationToken ct)
{
var key = result.GetRequiredValue(_keyArg);
var value = result.GetRequiredValue(_valueArg);
try
{
using var json = JsonDocument.Parse(value);
value = JsonSerializer.Serialize(json.RootElement, CliJsonContext.Default.JsonElement);
}
catch (JsonException)
{
}
await _secureConfig.SetAsync(key, value, ct);
_terminal.WriteLine($"Successfully set value of {key}");
return ExitCodes.Success;
}
}
@@ -0,0 +1,9 @@
namespace StevanFreeborn.Extensions.Configuration.Secure.Cli.Commands;
internal sealed class Terminal : ITerminal
{
public void WriteLine(string value)
{
Console.WriteLine(value);
}
}