bin/projects/dbatools/dbatools/TabExpansion/TabExpansionHost.cs

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Threading;

namespace Sqlcollaborative.Dbatools.TabExpansion
{
    /// <summary>
    /// Class that handles the static fields supporting the dbatools TabExpansion implementation
    /// </summary>
    public static class TabExpansionHost
    {
        #region State information
        /// <summary>
        /// Field containing the scripts that were registered.
        /// </summary>
        public static ConcurrentDictionary<string, ScriptContainer> Scripts = new ConcurrentDictionary<string, ScriptContainer>();

        /// <summary>
        /// The cache used by scripts utilizing TabExpansionPlusPlus in dbatools
        /// </summary>
        public static Hashtable Cache = new Hashtable();

        /// <summary>
        /// List of instances and when they were last accessed
        /// </summary>
        public static ConcurrentDictionary<string, InstanceAccess> InstanceAccess = new ConcurrentDictionary<string, InstanceAccess>();

        /// <summary>
        /// Scripts that build the cache and are suitable for synchronous execution
        /// </summary>
        public static List<ScriptBlock> TeppGatherScriptsFast = new List<ScriptBlock>();

        /// <summary>
        /// Scripts that build the cache and are not suitable for synchronous execution
        /// </summary>
        public static List<ScriptBlock> TeppGatherScriptsSlow = new List<ScriptBlock>();

        /// <summary>
        /// A list of all commands imported into dbatools
        /// </summary>
        public static List<FunctionInfo> DbatoolsCommands = new List<FunctionInfo>();

        /// <summary>
        /// List of completion sets that should be processed into Tepp Assignments. Only populate this list on first import.
        /// </summary>
        public static List<TabCompletionSet> TabCompletionSets = new List<TabCompletionSet>();

        /// <summary>
        /// Maps a TEPP scriptblock to a command and parameter
        /// </summary>
        public static Dictionary<string, Dictionary<string, ScriptContainer>> TeppAssignment = new Dictionary<string, Dictionary<string, ScriptContainer>>();
        #endregion State information

        #region Utility methods
        /// <summary>
        /// Registers a new instance or updates an already existing one. Should only be called from Connect-SqlInstance and Connect-DbaSqlServer
        /// </summary>
        /// <param name="InstanceName">Name of the instance connected to</param>
        /// <param name="Connection">To connection object containing the relevant information for accessing the instance</param>
        /// <param name="IsSysAdmin">Whether the account connecting to the instnace has SA privileges</param>
        public static void SetInstance(string InstanceName, object Connection, bool IsSysAdmin)
        {
            string tempName = InstanceName.ToLower();

            if (!InstanceAccess.ContainsKey(tempName))
            {
                InstanceAccess tempAccess = new InstanceAccess();
                tempAccess.InstanceName = tempName;
                tempAccess.LastAccess = DateTime.Now;
                tempAccess.ConnectionObject = Connection;
                tempAccess.IsSysAdmin = IsSysAdmin;
                InstanceAccess[tempName] = tempAccess;
            }
            else
            {
                InstanceAccess[tempName].LastAccess = DateTime.Now;

                if (IsSysAdmin & !InstanceAccess[tempName].IsSysAdmin)
                {
                    InstanceAccess[tempName].ConnectionObject = Connection;
                    InstanceAccess[tempName].IsSysAdmin = IsSysAdmin;
                }
            }
        }

        /// <summary>
        /// Returns the assigned scriptblock for a given parameter
        /// </summary>
        /// <param name="Command">The command that should be completed</param>
        /// <param name="Parameter">The parameter completion is provided for</param>
        /// <returns>Either the relevant script container or null</returns>
        public static ScriptContainer GetTeppScript(string Command, string Parameter)
        {
            if (TeppAssignment.ContainsKey(Command) && TeppAssignment[Command].ContainsKey(Parameter))
                return TeppAssignment[Command][Parameter];
            return null;
        }

        /// <summary>
        /// Assigns a registered script to the parameter of a command
        /// </summary>
        /// <param name="Command">The command for which to complete</param>
        /// <param name="Parameter">The parameter for which to complete</param>
        /// <param name="Script">To name of the script with which to complete</param>
        public static void SetTeppScript(string Command, string Parameter, string Script)
        {
            if (!Scripts.ContainsKey(Script))
                return;

            if (!TeppAssignment.ContainsKey(Command))
                TeppAssignment[Command] = new Dictionary<string, ScriptContainer>();

            TeppAssignment[Command][Parameter] = Scripts[Script];
        }

        /// <summary>
        /// Adds a completion set to the list of items to process
        /// </summary>
        /// <param name="Command">The command to complete for (accepts wildcard matching)</param>
        /// <param name="Parameter">The parameter to complete for (accepts wildcard matching)</param>
        /// <param name="Script">The script to register</param>
        public static void AddTabCompletionSet(string Command, string Parameter, string Script)
        {
            // Only import on the first import
            if (!dbaSystem.SystemHost.ModuleImported)
                TabCompletionSets.Add(new TabCompletionSet(Command, Parameter, Script));
        }

        /// <summary>
        /// Processes the content of TabCompletionSets and Scripts into TappAssignments based on the DbatoolsCommands list.
        /// </summary>
        public static void CalculateTabExpansion()
        {
            foreach (FunctionInfo info in DbatoolsCommands)
                foreach (ParameterMetadata paramInfo in info.Parameters.Values)
                    foreach (TabCompletionSet set in TabCompletionSets)
                        if (set.Applies(info.Name, paramInfo.Name))
                            SetTeppScript(info.Name, paramInfo.Name, set.Script);
        }
        #endregion Utility methods

        #region Configuration
        /// <summary>
        /// Whether TEPP in its entirety is disabled
        /// </summary>
        public static bool TeppDisabled = false;

        /// <summary>
        /// Whether asynchronous TEPP updating should be disabled
        /// </summary>
        public static bool TeppAsyncDisabled = false;

        /// <summary>
        /// Whether synchronous TEPP updating should be disabled
        /// </summary>
        public static bool TeppSyncDisabled = true;

        /// <summary>
        /// The interval in which asynchronous TEPP cache updates are performed
        /// </summary>
        public static TimeSpan TeppUpdateInterval = new TimeSpan(0, 3, 0);

        /// <summary>
        /// After this timespan of no requests to a server, the updates to its cache are disabled.
        /// </summary>
        public static TimeSpan TeppUpdateTimeout = new TimeSpan(0, 0, 30);
        #endregion Configuration
    }
}