winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

ConfigurationUnitInformation.cs (12228B)


      1 // -----------------------------------------------------------------------------
      2 // <copyright file="ConfigurationUnitInformation.cs" company="Microsoft Corporation">
      3 //     Copyright (c) Microsoft Corporation. Licensed under the MIT License.
      4 // </copyright>
      5 // -----------------------------------------------------------------------------
      6 
      7 namespace Microsoft.WinGet.Configuration.Engine.Helpers
      8 {
      9     using System;
     10     using System.Collections.Generic;
     11     using System.Linq;
     12     using System.Management.Automation;
     13     using System.Text;
     14     using Microsoft.Management.Configuration;
     15     using Microsoft.WinGet.Configuration.Engine.Extensions;
     16     using Microsoft.WinGet.Resources;
     17     using Windows.Foundation.Collections;
     18 
     19     /// <summary>
     20     /// Helper class to construct the information messages for a unit.
     21     /// This must match or be as close as possible to winget's OutputConfigurationUnitInformation.
     22     /// </summary>
     23     internal class ConfigurationUnitInformation
     24     {
     25         private const string Description = "description";
     26         private const string Module = "module";
     27         private const string TreatAsArray = "treatAsArray";
     28 
     29         private readonly string header;
     30         private readonly string information;
     31 
     32         /// <summary>
     33         /// Initializes a new instance of the <see cref="ConfigurationUnitInformation"/> class.
     34         /// </summary>
     35         /// <param name="unit">Configuration unit.</param>
     36         public ConfigurationUnitInformation(ConfigurationUnit unit)
     37         {
     38             this.header = this.CreateHeader(unit, unit.Details != null ? unit.Details.UnitType : unit.Type);
     39             this.information = this.CreateInformation(unit);
     40         }
     41 
     42         /// <summary>
     43         /// Gets the header information message.
     44         /// </summary>
     45         /// <returns>Header information message.</returns>
     46         public HostInformationMessage GetHeader()
     47         {
     48             return Utilities.CreateInformationMessage(this.header, foregroundColor: ConsoleColor.Cyan);
     49         }
     50 
     51         /// <summary>
     52         /// Gets the information message.
     53         /// </summary>
     54         /// <returns>Information message.</returns>
     55         public HostInformationMessage GetInformation()
     56         {
     57             return Utilities.CreateInformationMessage(this.information);
     58         }
     59 
     60         private string CreateHeader(ConfigurationUnit unit, string name)
     61         {
     62             var sb = new StringBuilder();
     63             sb.Append($"{this.IntentToString(unit.Intent)} :: {name}");
     64 
     65             string identifier = unit.Identifier;
     66             if (!string.IsNullOrEmpty(identifier))
     67             {
     68                 sb.Append($" [{identifier}]");
     69             }
     70 
     71             return sb.ToString();
     72         }
     73 
     74         private string CreateInformation(ConfigurationUnit unit)
     75         {
     76             IConfigurationUnitProcessorDetails details = unit.Details;
     77             ValueSet metadata = unit.Metadata;
     78 
     79             var sb = new StringBuilder();
     80             if (details != null)
     81             {
     82                 this.CreateInformationWithDetails(ref sb, details, metadata);
     83             }
     84             else
     85             {
     86                 this.CreateInformationWithoutDetails(ref sb, metadata);
     87             }
     88 
     89             // -- Sample output footer --
     90             //   Dependencies: dep1, dep2, ...
     91             //   Settings:
     92             //     <... settings splat>
     93             var dependencies = unit.Dependencies;
     94             if (dependencies.Count > 0)
     95             {
     96                 var dependencySb = new StringBuilder();
     97                 foreach (var dependency in dependencies)
     98                 {
     99                     dependencySb.Append($" {dependency}");
    100                 }
    101 
    102                 sb.AppendLine($"  {string.Format(Resources.ConfigurationDependencies, dependencySb.ToString())}");
    103             }
    104 
    105             var settings = unit.Settings;
    106             if (settings.Count > 0)
    107             {
    108                 sb.AppendLine($"  {Resources.ConfigurationSettings}");
    109                 this.AppendValueSet(ref sb, settings, 4);
    110             }
    111 
    112             return sb.ToString();
    113         }
    114 
    115         // -- Sample output when IConfigurationUnitProcessorDetails present --
    116         // Intent :: UnitName <from details> [Identifier]
    117         //   UnitDocumentationUri <if present>
    118         //   Description <from details first, directives second>
    119         //   "Module": ModuleName "by" Author / Publisher (IsLocal / ModuleSource)
    120         //     "Signed by": SigningCertificateChain (leaf subject CN)
    121         //     PublishedModuleUri / ModuleDocumentationUri <if present>
    122         //     ModuleDescription
    123         private void CreateInformationWithDetails(ref StringBuilder sb, IConfigurationUnitProcessorDetails details, ValueSet directives)
    124         {
    125             var unitDocumentationUri = details.UnitDocumentationUri;
    126             if (unitDocumentationUri != null)
    127             {
    128                 sb.AppendLine($"  {unitDocumentationUri.AbsoluteUri}");
    129             }
    130 
    131             var unitDescriptionFromDetails = details.UnitDescription;
    132             if (!string.IsNullOrEmpty(unitDescriptionFromDetails))
    133             {
    134                 sb.AppendLine($"  {unitDescriptionFromDetails}");
    135             }
    136             else
    137             {
    138                 var unitDescriptionFromDirectives = directives.TryGetStringValue(Description);
    139                 if (!string.IsNullOrEmpty(unitDescriptionFromDirectives))
    140                 {
    141                     sb.AppendLine($"  {unitDescriptionFromDirectives}");
    142                 }
    143             }
    144 
    145             var author = details.Author;
    146             if (string.IsNullOrEmpty(author))
    147             {
    148                 author = details.Publisher;
    149             }
    150 
    151             if (details.IsLocal)
    152             {
    153                 sb.AppendLine($"  {string.Format(Resources.ConfigurationModuleWithDetails, details.ModuleName, author, Resources.ConfigurationLocal)}");
    154             }
    155             else
    156             {
    157                 sb.AppendLine($"  {string.Format(Resources.ConfigurationModuleWithDetails, details.ModuleName, author, details.ModuleSource)}");
    158             }
    159 
    160             // TODO: see signature information in ConfigurationFlow.cpp
    161             var moduleUri = details.PublishedModuleUri;
    162             if (moduleUri == null)
    163             {
    164                 moduleUri = details.ModuleDocumentationUri;
    165             }
    166 
    167             if (moduleUri != null)
    168             {
    169                 sb.AppendLine($"    {moduleUri.AbsoluteUri}");
    170             }
    171 
    172             var moduleDescription = details.ModuleDescription;
    173             if (!string.IsNullOrEmpty(moduleDescription))
    174             {
    175                 sb.AppendLine($"    {moduleDescription}");
    176             }
    177         }
    178 
    179         // -- Sample output when no IConfigurationUnitProcessorDetails present --
    180         // Intent :: UnitName <from unit> [identifier]
    181         //   Description (from directives)
    182         //   "Module": module <directive>
    183         private void CreateInformationWithoutDetails(ref StringBuilder sb, ValueSet directives)
    184         {
    185             var unitDescriptionFromDirectives = directives.TryGetStringValue(Description);
    186             if (!string.IsNullOrEmpty(unitDescriptionFromDirectives))
    187             {
    188                 sb.AppendLine($"  {unitDescriptionFromDirectives}");
    189             }
    190 
    191             var unitModuleFromDirectives = directives.TryGetStringValue(Module);
    192             if (!string.IsNullOrEmpty(unitModuleFromDirectives))
    193             {
    194                 sb.AppendLine($"  {string.Format(Resources.ConfigurationModuleNameOnly, unitModuleFromDirectives)}");
    195             }
    196         }
    197 
    198         private void AppendValueSet(ref StringBuilder sb, ValueSet valueSet, int indent)
    199         {
    200             var indentString = new string(' ', indent);
    201 
    202             foreach (var value in valueSet)
    203             {
    204                 sb.Append($"{indentString}{value.Key}:");
    205 
    206                 // Can't use IPropertyValue here...
    207                 var obj = value.Value;
    208                 var innerValueSet = obj as ValueSet;
    209                 if (innerValueSet != null)
    210                 {
    211                     sb.AppendLine();
    212                     if (innerValueSet.ContainsKey(TreatAsArray))
    213                     {
    214                         this.AppendValueSetAsArray(ref sb, innerValueSet, indent + 2);
    215                     }
    216                     else
    217                     {
    218                         this.AppendValueSet(ref sb, innerValueSet, indent + 2);
    219                     }
    220                 }
    221                 else
    222                 {
    223                     this.AppendPropertyValue(ref sb, obj);
    224                 }
    225             }
    226         }
    227 
    228         private void AppendValueSetAsArray(ref StringBuilder sb, ValueSet valueSet, int indent)
    229         {
    230             var indentString = new string(' ', indent);
    231 
    232             var sortedList = new SortedList<int, object>();
    233 
    234             foreach (var keyValuePair in valueSet)
    235             {
    236                 if (keyValuePair.Key != TreatAsArray)
    237                 {
    238                     if (int.TryParse(keyValuePair.Key, out int key))
    239                     {
    240                         sortedList.Add(key, keyValuePair.Value);
    241                     }
    242                     else
    243                     {
    244                         throw new InvalidOperationException(keyValuePair.Key);
    245                     }
    246                 }
    247             }
    248 
    249             foreach (var arrayValue in sortedList)
    250             {
    251                 sb.Append($"{indentString}-");
    252                 var obj = arrayValue.Value;
    253 
    254                 var innerValueSet = obj as ValueSet;
    255                 if (innerValueSet == null)
    256                 {
    257                     this.AppendPropertyValue(ref sb, obj);
    258                 }
    259                 else
    260                 {
    261                     var size = innerValueSet.Count;
    262                     if (size > 0)
    263                     {
    264                         // First one is special.
    265                         var first = innerValueSet.First();
    266                         sb.Append($" {first.Key}:");
    267 
    268                         var firstValueSet = first.Value as ValueSet;
    269                         if (firstValueSet == null)
    270                         {
    271                             this.AppendPropertyValue(ref sb, first.Value);
    272                         }
    273                         else
    274                         {
    275                             sb.AppendLine();
    276                             this.AppendValueSet(ref sb, firstValueSet, indent + 4);
    277                         }
    278 
    279                         if (size > 1)
    280                         {
    281                             innerValueSet.Remove(first.Key);
    282                             this.AppendValueSet(ref sb, innerValueSet, indent + 2);
    283                             innerValueSet.Add(first);
    284                         }
    285                     }
    286                 }
    287             }
    288         }
    289 
    290         private void AppendPropertyValue(ref StringBuilder sb, object value)
    291         {
    292             Type type = value.GetType();
    293             if (type == typeof(string))
    294             {
    295                 sb.AppendLine($" {(string)value}");
    296             }
    297             else if (type == typeof(bool))
    298             {
    299                 string message = (bool)value ? "true" : "false";
    300                 sb.AppendLine($" {message}");
    301             }
    302             else if (type == typeof(long))
    303             {
    304                 sb.AppendLine($" {(long)value}");
    305             }
    306             else
    307             {
    308                 sb.AppendLine($" [Debug:PropertyType={type}]");
    309             }
    310         }
    311 
    312         private string IntentToString(ConfigurationUnitIntent intent)
    313         {
    314             return intent switch
    315             {
    316                 ConfigurationUnitIntent.Assert => Resources.ConfigurationAssert,
    317                 ConfigurationUnitIntent.Inform => Resources.ConfigurationInform,
    318                 ConfigurationUnitIntent.Apply => Resources.ConfigurationApply,
    319                 _ => string.Empty,
    320             };
    321         }
    322     }
    323 }