ParseMethods(AppConfig appConfig)
        {
            MethodInfo?       interceptorMethodInfo    = null;
            MethodInfo?       defaultCommandMethodInfo = null;
            List <MethodInfo> localCommandMethodInfos  = new();

            foreach (var method in CommandHostClassType.GetCommandMethods(appConfig.AppSettings.Commands.InheritCommandsFromBaseClasses))
            {
                if (MethodDef.IsInterceptorMethod(method))
                {
                    if (interceptorMethodInfo != null)
                    {
                        throw new InvalidConfigurationException($"`{CommandHostClassType}` defines more than one middleware method with a parameter of type " +
                                                                $"{MethodDef.MiddlewareNextParameterType} or {MethodDef.InterceptorNextParameterType}. " +
                                                                "There can be only one.");
                    }
                    if (method.HasAttribute <DefaultCommandAttribute>())
                    {
                        throw new InvalidConfigurationException($"`{CommandHostClassType}.{method.Name}` default method cannot contain parameter of type " +
                                                                $"{MethodDef.MiddlewareNextParameterType} or {MethodDef.InterceptorNextParameterType}.");
                    }

                    var emDelegate = new ExecutionMiddleware((_, _) => ExitCodes.Error).Method;
                    if (method.ReturnType != emDelegate.ReturnType)
                    {
                        throw new InvalidConfigurationException($"`{CommandHostClassType}.{method.Name}` must return type of {emDelegate.ReturnType}.");
                    }

                    interceptorMethodInfo = method;
                }
                else if (method.HasAttribute <DefaultCommandAttribute>())
                {
                    if (defaultCommandMethodInfo != null)
                    {
                        throw new InvalidConfigurationException($"`{CommandHostClassType}` defines more than one method with {nameof(DefaultCommandAttribute)}.  There can be only one.");
                    }
                    defaultCommandMethodInfo = method;
                }
                else
                {
                    localCommandMethodInfos.Add(method);
                }
            }

            var interceptorMethod = interceptorMethodInfo == null
                ? null
                : new MethodDef(interceptorMethodInfo, appConfig, true);

            var defaultCommand = defaultCommandMethodInfo == null
                ? null
                : new MethodCommandDef(defaultCommandMethodInfo, CommandHostClassType, appConfig);

            return(
                interceptorMethod,
                defaultCommand,
                localCommandMethodInfos
                .Select(m => new MethodCommandDef(m, CommandHostClassType, appConfig))
                .Cast <ICommandDef>()
                .ToList());
        }
        private (IMethodDef interceptorMethod, ICommandDef defaultCommand, List <ICommandDef> localCommands) ParseMethods(AppConfig appConfig)
        {
            MethodInfo        interceptorMethodInfo    = null;
            MethodInfo        defaultCommandMethodInfo = null;
            List <MethodInfo> localCommandMethodInfos  = new List <MethodInfo>();

            foreach (var method in CommandHostClassType.GetDeclaredMethods())
            {
                if (MethodDef.IsInterceptorMethod(method))
                {
                    if (interceptorMethodInfo != null)
                    {
                        throw new InvalidConfigurationException($"`{CommandHostClassType}` defines more than one middleware method with a parameter of type " +
                                                                $"{MethodDef.MiddlewareNextParameterType} or {MethodDef.InterceptorNextParameterType}. " +
                                                                "There can be only one.");
                    }
                    if (method.HasAttribute <DefaultMethodAttribute>())
                    {
                        throw new InvalidConfigurationException($"`{CommandHostClassType}.{method.Name}` default method cannot contain parameter of type " +
                                                                $"{MethodDef.MiddlewareNextParameterType} or {MethodDef.InterceptorNextParameterType}.");
                    }

                    var emDelegate = new ExecutionMiddleware((context, next) => Task.FromResult(1)).Method;
                    if (method.ReturnType != emDelegate.ReturnType)
                    {
                        throw new InvalidConfigurationException($"`{CommandHostClassType}.{method.Name}` must return type of {emDelegate.ReturnType}.");
                    }

                    interceptorMethodInfo = method;
                }
                else if (method.HasAttribute <DefaultMethodAttribute>())
                {
                    if (defaultCommandMethodInfo != null)
                    {
                        throw new InvalidConfigurationException($"`{CommandHostClassType}` defines more than one method with {nameof(DefaultMethodAttribute)}.  There can be only one.");
                    }
                    defaultCommandMethodInfo = method;
                }
                else
                {
                    localCommandMethodInfos.Add(method);
                }
            }

            var interceptorMethod = interceptorMethodInfo == null
                ? NullMethodDef.Instance
                : new MethodDef(interceptorMethodInfo, appConfig);

            var defaultCommand = defaultCommandMethodInfo == null
                ? (ICommandDef) new NullCommandDef(Name)
                : new MethodCommandDef(defaultCommandMethodInfo, CommandHostClassType, appConfig);

            return(
                interceptorMethod,
                defaultCommand,
                localCommandMethodInfos
                .Select(m => new MethodCommandDef(m, CommandHostClassType, appConfig))
                .Cast <ICommandDef>()
                .ToList());
        }
        private AppRunnerResult RunInMem(int carNumber, string ownerName,
                                         ExecutionMiddleware postBindValues = null,
                                         ExecutionMiddleware preBindValues  = null)
        {
            var appRunner = new AppRunner <App>();

            if (postBindValues != null)
            {
                appRunner.Configure(c => c.UseMiddleware(postBindValues, MiddlewareStages.PostBindValuesPreInvoke, int.MaxValue));
            }
            if (preBindValues != null)
            {
                appRunner.Configure(c => c.UseMiddleware(preBindValues, MiddlewareStages.PostParseInputPreBindValues, int.MaxValue));
            }

            var args = $"NotifyOwner --Number {carNumber} --owner {ownerName}".SplitArgs();

            return(appRunner.RunInMem(args));
        }