bin/projects/dbatools/dbatools/Parameter/DbaSelectParameter.cs

using System;
using System.Collections;
using System.Linq;
using System.Management.Automation;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// Class that automatically parses input chosen for the -Property parameter of Select-PSUObject
    /// </summary>
    public class DbaSelectParameter
    {
        /// <summary>
        /// The original input object
        /// </summary>
        public object InputObject;

        /// <summary>
        /// The value as Select-Object wants it
        /// </summary>
        public object Value;

        /// <summary>
        /// Builds a property parameter from string
        /// </summary>
        /// <param name="Value">The string to interpret</param>
        public DbaSelectParameter(string Value)
        {
            InputObject = Value;

            if (!Value.Contains(" "))
            {
                this.Value = Value;
                return;
            }

            #region Process Input
            // Runtime properties
            string valueName = "";
            string propertyName = "";
            string castType = "";
            string fromName = "_";
            string wherePropInput = "";
            string wherePropOutput = "";
            string sizeName = "";
            uint sizeDecimals = 0;
            bool sizeShow = false;

            string tempValue = Value.Trim();
            propertyName = tempValue.Split(' ')[0];
            valueName = propertyName;
            tempValue = tempValue.Substring(propertyName.Length);

            if (Regex.IsMatch(tempValue, @" as \w+", RegexOptions.IgnoreCase))
            {
                propertyName = Regex.Match(tempValue, @" as (\w+)", RegexOptions.IgnoreCase).Groups[1].Value;
                tempValue = Regex.Replace(tempValue, @" as \w+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" from [\w_]+", RegexOptions.IgnoreCase))
            {
                fromName = Regex.Match(tempValue, @" from ([\w_]+)", RegexOptions.IgnoreCase).Groups[1].Value;
                tempValue = Regex.Replace(tempValue, @" from [\w_]+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" where [\w_]+ = [\w_]+", RegexOptions.IgnoreCase))
            {
                Match match = Regex.Match(tempValue, @" where ([\w_]+) = ([\w_]+)", RegexOptions.IgnoreCase);
                wherePropOutput = match.Groups[1].Value;
                wherePropInput = match.Groups[2].Value;
                tempValue = Regex.Replace(tempValue, @" where [\w_]+ = [\w_]+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" to [\w\.]+", RegexOptions.IgnoreCase))
            {
                castType = Regex.Match(tempValue, @" to ([\w\.]+)", RegexOptions.IgnoreCase).Groups[1].Value;
                tempValue = Regex.Replace(tempValue, @" to [\w\.]+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" size \w+(:\d){1,2}", RegexOptions.IgnoreCase))
            {
                Match match = Regex.Match(tempValue, @" size (\w+)(:\d){1,2}", RegexOptions.IgnoreCase);
                sizeName = match.Groups[1].Value;
                sizeDecimals = UInt32.Parse(match.Groups[2].Captures[0].Value.Trim(':'));
                if (match.Groups[2].Captures.Count > 1)
                    sizeShow = match.Groups[2].Captures[1].Value == ":1";
                tempValue = Regex.Replace(tempValue, @" size \w+(:\d){1,2}", "", RegexOptions.IgnoreCase);
            }

            if (!String.IsNullOrEmpty(tempValue))
                throw new ArgumentException(String.Format("Failed to parse input! Original input: {0} | Unprocessed leftovers: {1}", Value, tempValue));
            #endregion Process Input

            #region Build Hashtable
            Hashtable table = new Hashtable();
            table["Name"] = propertyName;

            // Process cast strings
            string stringCast = "";
            if (!String.IsNullOrEmpty(castType))
                stringCast = String.Format("[{0}]", castType);

            // Process size strings
            string stringSizeStart = "";
            string stringSizeEnd = "";
            if (sizeName != "")
            {
                stringSizeStart = "[System.Math]::Round((";
                stringSizeEnd = String.Format(" / 1{0}), {1})", sizeName, sizeDecimals);
                if (sizeShow)
                {
                    stringSizeStart = String.Format("\"$({0}", stringSizeStart);
                    stringSizeEnd = String.Format("{0}) {1}\"", stringSizeEnd, sizeName);
                }
            }

            // Process value strings
            string stringGuidVar = "${" + Guid.NewGuid().ToString() + "}";
            string preLine = String.Format("{0} = $_\n", stringGuidVar);
            string stringValue = String.Format("${0}", fromName);
            if (fromName != "_" && wherePropInput != "")
            {
                stringValue = String.Format("({1} | Where-Object {2} -eq {0}.{3})", stringGuidVar, stringValue, wherePropOutput, wherePropInput);
            }

            if (propertyName != ".")
                stringValue = String.Format("{0}.{1}", stringValue, valueName);

            // <guid> = $_
            // "(<size>(<cast>(<value>))<sizeName>)"
            string script = String.Format("{0}({1}({2}({3})){4})", preLine, stringSizeStart, stringCast, stringValue, stringSizeEnd);

            table["Expression"] = ScriptBlock.Create(script);
            this.Value = table;
            #endregion Build Hashtable
        }

        /// <summary>
        /// Builds a select parameter from a hashtable (pretty straightforward)
        /// </summary>
        /// <param name="Hash">The hashtable to accept</param>
        public DbaSelectParameter(Hashtable Hash)
        {
            InputObject = Hash;
            Value = Hash;
        }
    }
}