diff --git a/XUnitTester/LifecycleHook.cs b/XUnitTester/LifecycleHook.cs new file mode 100644 index 0000000..69009ed --- /dev/null +++ b/XUnitTester/LifecycleHook.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using GeminiLab.Core2.CommandLineParser; +using GeminiLab.Core2.CommandLineParser.Default; +using Xunit; + +namespace XUnitTester { + public static class LifecycleHook { + class HookedOption { + public List Queue { get; set; } = new List(); + + [PreParsing] + public void PreParsing() { + Queue.Add("pre"); + } + + [PostParsing] + public void PostParsing() { + Queue.Add("post"); + } + + [ShortOption('z', OptionParameter.Required)] + public void Zulu(string z) { + Queue.Add($"z:{z}"); + } + } + + [Fact] + public static void LifecycleHookTest() { + var options = CommandLineParser.DoParse("-zzulu"); + + Assert.Equal(new string[] { "pre", "z:zulu", "post" }, options.Queue); + } + } +} diff --git a/src/GeminiLab.Core2.CommandLineParser/CommandLineParser.cs b/src/GeminiLab.Core2.CommandLineParser/CommandLineParser.cs index c05fc6c..b266e3e 100644 --- a/src/GeminiLab.Core2.CommandLineParser/CommandLineParser.cs +++ b/src/GeminiLab.Core2.CommandLineParser/CommandLineParser.cs @@ -21,6 +21,7 @@ public static T DoParse(params string[] args) { private List _optionCategories = null!; private List<(Type ExceptionType, object Handler)> _exceptionHandlers = null!; + private List _hooks = null!; [Obsolete("Use method 'Parse' instead")] public T ParseFromSpan(ReadOnlySpan args) { @@ -172,6 +173,7 @@ private void EvaluateMetaInfo() { _optionCategories = new List(); _exceptionHandlers = new List<(Type ExceptionType, object Handler)>(); + _hooks = new List(); var attributes = GetAttributes(); @@ -211,6 +213,10 @@ private void EvaluateMetaInfo() { if (ifType == typeof(IOptionCategory)) { _optionCategories.Add((IOptionCategory) instance); } + + if (ifType == typeof(IParsingHook)) { + _hooks.Add((IParsingHook) instance); + } } } } @@ -225,6 +231,8 @@ public T Parse(params string[] args) { int ptr = 0; var rv = new T(); + _hooks.ForEach(h => h.OnParsingEvent(ParsingEvent.PreParsing, rv)); + while (ptr < len) { var current = workplace[ptr..]; int consumed = 0; @@ -267,6 +275,8 @@ public T Parse(params string[] args) { ptr += consumed; } + _hooks.ForEach(h => h.OnParsingEvent(ParsingEvent.PostParsing, rv)); + return rv; } @@ -277,6 +287,7 @@ private void LoadDefaultConfigs() { Use(new TailArgumentsConfig()); Use(); + Use(); Use(); } diff --git a/src/GeminiLab.Core2.CommandLineParser/Custom/IParsingHook.cs b/src/GeminiLab.Core2.CommandLineParser/Custom/IParsingHook.cs new file mode 100644 index 0000000..07f9929 --- /dev/null +++ b/src/GeminiLab.Core2.CommandLineParser/Custom/IParsingHook.cs @@ -0,0 +1,10 @@ +namespace GeminiLab.Core2.CommandLineParser.Custom { + public enum ParsingEvent { + PreParsing, + PostParsing, + } + + public interface IParsingHook { + void OnParsingEvent(ParsingEvent parsingEvent, object target); + } +} diff --git a/src/GeminiLab.Core2.CommandLineParser/Default/LifecycleHookAttributes.cs b/src/GeminiLab.Core2.CommandLineParser/Default/LifecycleHookAttributes.cs new file mode 100644 index 0000000..9690f05 --- /dev/null +++ b/src/GeminiLab.Core2.CommandLineParser/Default/LifecycleHookAttributes.cs @@ -0,0 +1,11 @@ +using System; +using GeminiLab.Core2.CommandLineParser.Custom; + +namespace GeminiLab.Core2.CommandLineParser.Default { + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class PreParsingAttribute : ParsingAttribute { } + + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class PostParsingAttribute : ParsingAttribute { } +} diff --git a/src/GeminiLab.Core2.CommandLineParser/Default/LifecycleHookComponent.cs b/src/GeminiLab.Core2.CommandLineParser/Default/LifecycleHookComponent.cs new file mode 100644 index 0000000..99d81c3 --- /dev/null +++ b/src/GeminiLab.Core2.CommandLineParser/Default/LifecycleHookComponent.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using GeminiLab.Core2.CommandLineParser.Custom; + +namespace GeminiLab.Core2.CommandLineParser.Default { + public class LifecycleHookComponent : IParsingHook, IAttributeCategory, IAttributeCategory { + private MethodInfo? _pre, _post; + + public void OnParsingEvent(ParsingEvent parsingEvent, object target) { + (parsingEvent switch { + ParsingEvent.PreParsing => _pre, + ParsingEvent.PostParsing => _post, + _ => null, + })?.Invoke(target, Array.Empty()); + } + + IEnumerable.MemberWithAttribute> IAttributeCategory.Options { + set { _pre = value.Select(mwa => mwa.Target).OfType().FirstOrDefault(); } + } + + IEnumerable.MemberWithAttribute> IAttributeCategory.Options { + set { _post = value.Select(mwa => mwa.Target).OfType().FirstOrDefault(); } + } + } +} diff --git a/src/GeminiLab.Core2.CommandLineParser/Default/UnknownOptionHandlerAttribute.cs b/src/GeminiLab.Core2.CommandLineParser/Default/UnknownOptionHandlerAttribute.cs index 6365002..e044b55 100644 --- a/src/GeminiLab.Core2.CommandLineParser/Default/UnknownOptionHandlerAttribute.cs +++ b/src/GeminiLab.Core2.CommandLineParser/Default/UnknownOptionHandlerAttribute.cs @@ -2,6 +2,6 @@ using GeminiLab.Core2.CommandLineParser.Custom; namespace GeminiLab.Core2.CommandLineParser.Default { - [AttributeUsage(SupportedTargets, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class UnknownOptionHandlerAttribute : ParsingAttribute { } }