|
@@ -1,393 +0,0 @@
|
|
|
-using System;
|
|
|
|
|
-using System.Collections.Generic;
|
|
|
|
|
-using System.Linq;
|
|
|
|
|
-using System.Text;
|
|
|
|
|
-using Quadarax.Foundation.Core.Console;
|
|
|
|
|
-using Quadarax.Foundation.Core.QConsole.Argument;
|
|
|
|
|
-using Quadarax.Foundation.Core.QConsole.Context;
|
|
|
|
|
-using Quadarax.Foundation.Core.QConsole.Extensions;
|
|
|
|
|
-using Quadarax.Foundation.Core.Value;
|
|
|
|
|
-
|
|
|
|
|
-namespace Quadarax.Foundation.Core.QConsole.Command.Base
|
|
|
|
|
-{
|
|
|
|
|
- public abstract class AbstractCommand
|
|
|
|
|
- {
|
|
|
|
|
- #region *** Enumerations ***
|
|
|
|
|
-
|
|
|
|
|
- public enum WriteTextSeverity
|
|
|
|
|
- {
|
|
|
|
|
- Info,
|
|
|
|
|
- Caption,
|
|
|
|
|
- Warning,
|
|
|
|
|
- Error,
|
|
|
|
|
- }
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Private Fields ***
|
|
|
|
|
- private Engine _engine;
|
|
|
|
|
- private IDictionary<string, AbstractArgument> _arguments;
|
|
|
|
|
- protected ICommandContext _context;
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Properties ***
|
|
|
|
|
- public abstract string Name { get; }
|
|
|
|
|
- public abstract string Description { get; }
|
|
|
|
|
- public virtual string Notes { get; set; }
|
|
|
|
|
- public virtual string Examples { get; set; }
|
|
|
|
|
- public virtual string CustomUsage { get; set; }
|
|
|
|
|
- public AbstractArgument[] Arguments => _arguments.Values.ToArray();
|
|
|
|
|
-
|
|
|
|
|
- protected Engine Engine => _engine;
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Constructor ***
|
|
|
|
|
-
|
|
|
|
|
- public AbstractCommand(Engine engine)
|
|
|
|
|
- {
|
|
|
|
|
- _engine = engine ?? throw new ArgumentNullException(nameof(engine));
|
|
|
|
|
- _arguments = new Dictionary<string, AbstractArgument>();
|
|
|
|
|
- _context = _engine.Context.CreateCommandContext();
|
|
|
|
|
- WriteDebugInfo($"Context for command '{Name}' created: {_context}");
|
|
|
|
|
- var args = SetupArguments();
|
|
|
|
|
- foreach (var arg in args)
|
|
|
|
|
- _arguments.Add(arg.Code, arg);
|
|
|
|
|
-
|
|
|
|
|
- Init();
|
|
|
|
|
-
|
|
|
|
|
- WriteDebugInfo($"Command '{Name}' has {_arguments.Count} arguments. Initialized.");
|
|
|
|
|
- }
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Public operations ***
|
|
|
|
|
- public Result Execute(string input)
|
|
|
|
|
- {
|
|
|
|
|
- ConsoleWriter writer = null;
|
|
|
|
|
- try
|
|
|
|
|
- {
|
|
|
|
|
- writer = new ConsoleWriter(ConsoleColor.Green);
|
|
|
|
|
- foreach (var arg in Arguments)
|
|
|
|
|
- arg.SetValueAsDefault();
|
|
|
|
|
- ParseInput(input);
|
|
|
|
|
- OnInitialize();
|
|
|
|
|
- if (_engine.Configuration.ShowStatusOnEveryCommand)
|
|
|
|
|
- ShowContextStatus(writer);
|
|
|
|
|
- OnValidateArguments();
|
|
|
|
|
- BeginExecute();
|
|
|
|
|
- var result = OnExecute();
|
|
|
|
|
- result = EndExecute(result);
|
|
|
|
|
- return result;
|
|
|
|
|
- }
|
|
|
|
|
- catch (Exception e)
|
|
|
|
|
- {
|
|
|
|
|
- return new Result(e);
|
|
|
|
|
- }
|
|
|
|
|
- finally
|
|
|
|
|
- {
|
|
|
|
|
- writer?.Dispose();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- public override string ToString()
|
|
|
|
|
- {
|
|
|
|
|
- return "Command: " + Name;
|
|
|
|
|
- }
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Private operations ***
|
|
|
|
|
-
|
|
|
|
|
- private void Init()
|
|
|
|
|
- {
|
|
|
|
|
- OnCommandInit();
|
|
|
|
|
- }
|
|
|
|
|
- private void ParseInput(string input)
|
|
|
|
|
- {
|
|
|
|
|
- //var args = input.Split(new[] {Engine.Configuration.CharacterNamedArgumentSeparator}, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
- var args = Split(input, Engine.Configuration.CharacterTextValueBraceSeparator, new[]
|
|
|
|
|
- {
|
|
|
|
|
- Engine.Configuration.CharacterNamedArgumentSeparator,
|
|
|
|
|
- Engine.Configuration.CharacterOrdinalArgumentSeparator
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- for (int i = 1; i < args.Length; i++)
|
|
|
|
|
- {
|
|
|
|
|
- var arg = args[i];
|
|
|
|
|
- var index = i - 1;
|
|
|
|
|
- WriteDebugInfo($"Parsing argument [index={index}] by value string '{arg}' in progress..");
|
|
|
|
|
- AbstractArgument argument = null;
|
|
|
|
|
- if (arg.StartsWith(Engine.Configuration.CharacterNamedArgumentSeparator))
|
|
|
|
|
- {
|
|
|
|
|
- //Named
|
|
|
|
|
- WriteDebugInfo("Detects argument as named.");
|
|
|
|
|
- var parsed = ParseNamedArgument(arg);
|
|
|
|
|
- if (!ContainsArgument(parsed.Item1))
|
|
|
|
|
- throw new Exception(
|
|
|
|
|
- $"Invalid argument '{Engine.Configuration.CharacterNamedArgumentSeparator}{parsed.Item1}' for this command.");
|
|
|
|
|
- argument = GetArgument(parsed.Item1);
|
|
|
|
|
- if (argument.IsPositional())
|
|
|
|
|
- if (!((OrdinalArgument)argument).IsCodeAllowed)
|
|
|
|
|
- throw new Exception($"Usage of ordinal argument '{argument.Code}' as named argument is not allowed!");
|
|
|
|
|
- if (argument.IsFlag())
|
|
|
|
|
- ((FlagArgument)argument).Set();
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- argument.Value.Set(parsed.Item2);
|
|
|
|
|
- argument.Hit();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- {
|
|
|
|
|
- //Ordinal
|
|
|
|
|
- WriteDebugInfo("Detects argument as ordinal.");
|
|
|
|
|
- argument = GetArgument(index);
|
|
|
|
|
- if (argument == null)
|
|
|
|
|
- throw new Exception(
|
|
|
|
|
- $"Invalid ordinal argument '{arg}' at position {index} for this command.");
|
|
|
|
|
-
|
|
|
|
|
- argument.Value.Set(arg);
|
|
|
|
|
- argument.Hit();
|
|
|
|
|
- WriteDebugInfo($"Ordinal argument '{argument.Code}' is set to value '{argument.Value.AsString()}'.");
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private Tuple<string, string> ParseNamedArgument(string argument)
|
|
|
|
|
- {
|
|
|
|
|
- try
|
|
|
|
|
- {
|
|
|
|
|
- var argParts = argument.Split(new[] {Engine.Configuration.CharacterNamedArgumentValueSeparator}, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
- WriteDebugInfo($"Splitting arg-parts '{argument}' to parts (count={argParts.Length}): {string.Join(",",argParts)}");
|
|
|
|
|
- var argName = argParts[0].Replace(Engine.Configuration.CharacterNamedArgumentSeparator,string.Empty);
|
|
|
|
|
- var argValue = argParts.Length > 1 ? string.Join(Engine.Configuration.CharacterNamedArgumentValueSeparator,argParts.Skip(1).Take(argParts.Length - 1)).Replace(_engine.Configuration.CharacterTextValueBraceSeparator,string.Empty) : string.Empty;
|
|
|
|
|
- WriteDebugInfo($"Named argument '{argument}' parsed as name='{argName}'; value='{argValue}'.");
|
|
|
|
|
- return new Tuple<string, string>(argName, argValue);
|
|
|
|
|
- }
|
|
|
|
|
- catch (Exception e)
|
|
|
|
|
- {
|
|
|
|
|
- WriteDebugInfo($"Error during parsing named argument '{argument}'.");
|
|
|
|
|
- throw new Exception($"Argument '{argument}' parsing error.", e);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
- private string[] Split(string input, string valueBracket, string[] separators)
|
|
|
|
|
- {
|
|
|
|
|
- var result = new List<string>();
|
|
|
|
|
- var trimedInput = input.Trim();
|
|
|
|
|
- if (string.IsNullOrEmpty(trimedInput))
|
|
|
|
|
- return result.ToArray();
|
|
|
|
|
-
|
|
|
|
|
- var cValueBracked = valueBracket.ToCharArray().First();
|
|
|
|
|
- var cSeparators = separators.Select(x => x.ToCharArray().First());
|
|
|
|
|
-
|
|
|
|
|
- var nIndexStart = 0;
|
|
|
|
|
- var nIndexEnd = 0;
|
|
|
|
|
- var isInsideBracked = false;
|
|
|
|
|
- int offset = 0;
|
|
|
|
|
-
|
|
|
|
|
- for (offset = 0; offset < trimedInput.Length; offset++)
|
|
|
|
|
- {
|
|
|
|
|
- var current = trimedInput[offset];
|
|
|
|
|
- // current | isInside | Result
|
|
|
|
|
- // 1 1 0
|
|
|
|
|
- // 1 0 1
|
|
|
|
|
- // 0 1 1
|
|
|
|
|
- // 0 0 0
|
|
|
|
|
- isInsideBracked = current == cValueBracked ^ isInsideBracked;
|
|
|
|
|
- if (isInsideBracked)
|
|
|
|
|
- continue;
|
|
|
|
|
- if (cSeparators.Contains(current))
|
|
|
|
|
- {
|
|
|
|
|
- nIndexEnd = offset;
|
|
|
|
|
- result.Add(trimedInput.Substring(nIndexStart,nIndexEnd - nIndexStart));
|
|
|
|
|
- nIndexStart = nIndexEnd;
|
|
|
|
|
- isInsideBracked = false;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (offset>nIndexStart)
|
|
|
|
|
- result.Add(trimedInput.Substring(nIndexStart,offset - nIndexStart));
|
|
|
|
|
-
|
|
|
|
|
- return result.Where(x => !string.IsNullOrEmpty(x.Trim())).Select(x=>x.Trim()).ToArray();
|
|
|
|
|
- }
|
|
|
|
|
- private IEnumerable<AbstractArgument> SetupArguments()
|
|
|
|
|
- {
|
|
|
|
|
- return OnSetupArguments();
|
|
|
|
|
- }
|
|
|
|
|
- protected void WriteDebugInfo(string text)
|
|
|
|
|
- {
|
|
|
|
|
- using (var debug = new DebugConsoleWriter())
|
|
|
|
|
- {
|
|
|
|
|
- debug.WriteDebugLine(text);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected void WriteInfo(string text)
|
|
|
|
|
- {
|
|
|
|
|
- using (var writer = new ConsoleWriter(ConsoleColor.Green))
|
|
|
|
|
- WriteLine(writer,WriteTextSeverity.Info, text);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected void WriteCaption(string text)
|
|
|
|
|
- {
|
|
|
|
|
- using (var writer = new ConsoleWriter(ConsoleColor.White))
|
|
|
|
|
- WriteLine(writer,WriteTextSeverity.Caption, text);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected void WriteWarning(string text)
|
|
|
|
|
- {
|
|
|
|
|
- using (var writer = new ConsoleWriter(ConsoleColor.Yellow))
|
|
|
|
|
- WriteLine(writer,WriteTextSeverity.Warning, text);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected void WriteError(string text)
|
|
|
|
|
- {
|
|
|
|
|
- using (var writer = new ConsoleWriter(ConsoleColor.Red))
|
|
|
|
|
- WriteLine(writer, WriteTextSeverity.Error, text);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected void WriteError(Exception e)
|
|
|
|
|
- {
|
|
|
|
|
- if (e is AggregateException aggr)
|
|
|
|
|
- {
|
|
|
|
|
- foreach (var ex in aggr.InnerExceptions)
|
|
|
|
|
- DumpException(ex, "- ");
|
|
|
|
|
- }
|
|
|
|
|
- else
|
|
|
|
|
- DumpException(e, string.Empty);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private void DumpException(Exception e, string prefix)
|
|
|
|
|
- {
|
|
|
|
|
- while (e != null)
|
|
|
|
|
- {
|
|
|
|
|
- WriteError(prefix + e.Message);
|
|
|
|
|
- e = e.InnerException;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected virtual void WriteLine(ConsoleWriter writer, WriteTextSeverity severity, string text)
|
|
|
|
|
- {
|
|
|
|
|
- writer.WriteLine(text);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private void ShowContextStatus(ConsoleWriter writer)
|
|
|
|
|
- {
|
|
|
|
|
- using (var writerBlue = new ConsoleWriter(ConsoleColor.Blue))
|
|
|
|
|
- {
|
|
|
|
|
- using (var writerYellow = new ConsoleWriter(ConsoleColor.Magenta))
|
|
|
|
|
- {
|
|
|
|
|
- writer.WriteLine("Engine context:");
|
|
|
|
|
- writerBlue.Write(_engine.Context.ToString()).Write(" : ");
|
|
|
|
|
- writerYellow.WriteLine(_engine.Context.Status);
|
|
|
|
|
- writer.WriteLine("Command context:");
|
|
|
|
|
- writerBlue.Write(_context.ToString()).Write(" : ");
|
|
|
|
|
- writerYellow.WriteLine(_context.Status);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Protected virtuals ***
|
|
|
|
|
- protected virtual void OnCommandInit()
|
|
|
|
|
- {
|
|
|
|
|
- WriteDebugInfo("Command called OnCommandInit.");
|
|
|
|
|
- }
|
|
|
|
|
- protected virtual IEnumerable<AbstractArgument> OnSetupArguments()
|
|
|
|
|
- {
|
|
|
|
|
- return new AbstractArgument[0];
|
|
|
|
|
- //{
|
|
|
|
|
- // new FlagArgument(Constants.Arguments.General.DebugMode.Code, Constants.Arguments.General.DebugMode.Description, Constants.Arguments.General.DebugMode.Hint, true),
|
|
|
|
|
- //};
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- protected abstract Result OnExecute();
|
|
|
|
|
-
|
|
|
|
|
- protected virtual void OnValidateArguments()
|
|
|
|
|
- {
|
|
|
|
|
- var ex = new List<Exception>();
|
|
|
|
|
- foreach (var arg in _arguments.Where(x=>x.Value.IsMandatory).Select(x=>x.Value))
|
|
|
|
|
- {
|
|
|
|
|
- if (!arg.Value.HasValue)
|
|
|
|
|
- ex.Add(new Exception($"Argument '{arg.Code}' is mandatory!"));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (ex.Count > 0)
|
|
|
|
|
- throw new AggregateException(ex);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected virtual void OnInitialize()
|
|
|
|
|
- {
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected virtual void BeginExecute()
|
|
|
|
|
- {
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected virtual Result EndExecute(Result incommingResult)
|
|
|
|
|
- {
|
|
|
|
|
- return incommingResult;
|
|
|
|
|
- }
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- #region *** Protected operations ***
|
|
|
|
|
- protected AbstractArgument GetArgument(string code)
|
|
|
|
|
- {
|
|
|
|
|
- if (!_arguments.ContainsKey(code))
|
|
|
|
|
- throw new Exception($"Argument '{code}' is not defined.");
|
|
|
|
|
- return _arguments[code];
|
|
|
|
|
- }
|
|
|
|
|
- protected AbstractArgument GetArgument(int ordinalPosition)
|
|
|
|
|
- {
|
|
|
|
|
- return _arguments.Values.Where(x => x.IsPositional()).Skip(ordinalPosition).FirstOrDefault();
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected TValue GetArgumentValueOrDefault<TValue>(string code, TValue defalutValue)
|
|
|
|
|
- {
|
|
|
|
|
- if (!ContainsArgument(code)) return defalutValue;
|
|
|
|
|
- return (TValue) GetArgumentValueOrDefault<TValue>(code);
|
|
|
|
|
- }
|
|
|
|
|
- protected TValue GetArgumentValueOrDefault<TValue>(string code)
|
|
|
|
|
- {
|
|
|
|
|
- if (!ContainsArgument(code)) throw new ArgumentOutOfRangeException(code, $"Argument '{code}' not defined.");
|
|
|
|
|
- var arg = GetArgument(code);
|
|
|
|
|
- var value = arg.Value.Get() ?? arg.DefaultValue.Get();
|
|
|
|
|
- if (typeof(TValue).IsEnum)
|
|
|
|
|
- return (TValue)Enum.Parse(typeof(TValue), value?.ToString(),true);
|
|
|
|
|
- return (TValue) value;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected bool WasArgumentSpecified(string code)
|
|
|
|
|
- {
|
|
|
|
|
- if (!ContainsArgument(code)) throw new ArgumentOutOfRangeException(code, $"Argument '{code}' not defined.");
|
|
|
|
|
- var arg = GetArgument(code);
|
|
|
|
|
- return arg.Value.HasValue && arg.WasUsed;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected void ValidateArgumentValueEnum(string argumentName, Type enumType)
|
|
|
|
|
- {
|
|
|
|
|
- var argVal = GetArgumentValueOrDefault<string>(argumentName);
|
|
|
|
|
- if (!Enum.GetNames(enumType).Select(x => x.ToLower()).Contains(argVal.ToLower()))
|
|
|
|
|
- throw new ArgumentOutOfRangeException(
|
|
|
|
|
- $"Argument '{argumentName}' value must be set as following: {string.Join(",", Enum.GetNames(enumType))}");
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
- protected void ValidateArgumentValueEnum<TEnum>(string argumentName) where TEnum : Enum
|
|
|
|
|
- {
|
|
|
|
|
- ValidateArgumentValueEnum(argumentName, typeof(TEnum));
|
|
|
|
|
- }
|
|
|
|
|
- protected bool ContainsArgument(string code)
|
|
|
|
|
- {
|
|
|
|
|
- return _arguments.ContainsKey(code);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- protected TContext GetContext<TContext>() where TContext : class, ICommandContext
|
|
|
|
|
- {
|
|
|
|
|
- return (TContext)_context;
|
|
|
|
|
- }
|
|
|
|
|
- #endregion
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|