MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections;6using System.Collections.Generic;7using System.Management.Automation;8using System.Management.Automation.Internal;9using System.Management.Automation.Language;10using System.Text;11 12using Dbg = System.Management.Automation.Diagnostics;13 14//15// Now define the set of commands for manipulating modules.16//17 18namespace Microsoft.PowerShell.Commands19{20 #region Module Specification class21 22 /// <summary>23 /// Represents module specification written in a module manifest (i.e. in RequiredModules member/field).24 ///25 /// Module manifest allows 2 forms of module specification:26 /// 1. string - module name27 /// 2. hashtable - [string]ModuleName (required) + [Version]ModuleVersion/RequiredVersion (required) + [Guid]GUID (optional)28 ///29 /// so we have a constructor that takes a string and a constructor that takes a hashtable30 /// (so that LanguagePrimitives.ConvertTo can cast a string or a hashtable to this type)31 /// </summary>32 public class ModuleSpecification33 {34 /// <summary>35 /// Default constructor.36 /// </summary>37 public ModuleSpecification()38 {39 }40 41 /// <summary>42 /// Construct a module specification from the module name.43 /// </summary>44 /// <param name="moduleName">The module name.</param>45 public ModuleSpecification(string moduleName)46 {47 ArgumentException.ThrowIfNullOrEmpty(moduleName);48 49 this.Name = moduleName;50 51 // Alias name of miniumVersion52 this.Version = null;53 this.RequiredVersion = null;54 this.MaximumVersion = null;55 this.Guid = null;56 }57 58 /// <summary>59 /// Construct a module specification from a hashtable.60 /// Keys can be ModuleName, ModuleVersion, and Guid.61 /// ModuleName must be convertible to <see cref="string"/>.62 /// ModuleVersion must be convertible to <see cref="Version"/>.63 /// Guid must be convertible to <see cref="Guid"/>.64 /// </summary>65 /// <param name="moduleSpecification">The module specification as a hashtable.</param>66 public ModuleSpecification(Hashtable moduleSpecification)67 {68 ArgumentNullException.ThrowIfNull(moduleSpecification);69 70 var exception = ModuleSpecificationInitHelper(this, moduleSpecification);71 if (exception != null)72 {73 throw exception;74 }75 }76 77 /// <summary>78 /// Initialize moduleSpecification from hashtable. Return exception object, if hashtable cannot be converted.79 /// Return null, in the success case.80 /// </summary>81 /// <param name="moduleSpecification">Object to initialize.</param>82 /// <param name="hashtable">Contains info about object to initialize.</param>83 /// <returns></returns>84 internal static Exception ModuleSpecificationInitHelper(ModuleSpecification moduleSpecification, Hashtable hashtable)85 {86 StringBuilder badKeys = new StringBuilder();87 try88 {89 foreach (DictionaryEntry entry in hashtable)90 {91 string field = entry.Key.ToString();92 93 if (field.Equals("ModuleName", StringComparison.OrdinalIgnoreCase))94 {95 moduleSpecification.Name = LanguagePrimitives.ConvertTo<string>(entry.Value);96 }97 else if (field.Equals("ModuleVersion", StringComparison.OrdinalIgnoreCase))98 {99 moduleSpecification.Version = LanguagePrimitives.ConvertTo<Version>(entry.Value);100 }101 else if (field.Equals("RequiredVersion", StringComparison.OrdinalIgnoreCase))102 {103 moduleSpecification.RequiredVersion = LanguagePrimitives.ConvertTo<Version>(entry.Value);104 }105 else if (field.Equals("MaximumVersion", StringComparison.OrdinalIgnoreCase))106 {107 moduleSpecification.MaximumVersion = LanguagePrimitives.ConvertTo<string>(entry.Value);108 ModuleCmdletBase.GetMaximumVersion(moduleSpecification.MaximumVersion);109 }110 else if (field.Equals("GUID", StringComparison.OrdinalIgnoreCase))111 {112 moduleSpecification.Guid = LanguagePrimitives.ConvertTo<Guid?>(entry.Value);113 }114 else115 {116 if (badKeys.Length > 0)117 {118 badKeys.Append(", ");119 }120 121 badKeys.Append('\'');122 badKeys.Append(entry.Key.ToString());123 badKeys.Append('\'');124 }125 }126 }127 // catch all exceptions here, we are going to report them via return value.128 // Example of caught exception: one of conversions to Version failed.129 catch (Exception e)130 {131 return e;132 }133 134 string message;135 if (badKeys.Length != 0)136 {137 message = StringUtil.Format(Modules.InvalidModuleSpecificationMember, "ModuleName, ModuleVersion, RequiredVersion, GUID", badKeys);138 return new ArgumentException(message);139 }140 141 if (string.IsNullOrEmpty(moduleSpecification.Name))142 {143 message = StringUtil.Format(Modules.RequiredModuleMissingModuleName);144 return new MissingMemberException(message);145 }146 147 if (moduleSpecification.RequiredVersion == null && moduleSpecification.Version == null && moduleSpecification.MaximumVersion == null)148 {149 message = StringUtil.Format(Modules.RequiredModuleMissingModuleVersion);150 return new MissingMemberException(message);151 }152 153 if (moduleSpecification.RequiredVersion != null && moduleSpecification.Version != null)154 {155 message = StringUtil.Format(SessionStateStrings.GetContent_TailAndHeadCannotCoexist, "ModuleVersion", "RequiredVersion");156 return new ArgumentException(message);157 }158 159 if (moduleSpecification.RequiredVersion != null && moduleSpecification.MaximumVersion != null)160 {161 message = StringUtil.Format(SessionStateStrings.GetContent_TailAndHeadCannotCoexist, "MaximumVersion", "RequiredVersion");162 return new ArgumentException(message);163 }164 165 return null;166 }167 168 internal string GetRequiredModuleNotFoundVersionMessage()169 {170 if (RequiredVersion is not null)171 {172 return StringUtil.Format(173 Modules.RequiredModuleNotFoundRequiredVersion,174 Name,175 RequiredVersion);176 }177 178 bool hasVersion = Version is not null;179 bool hasMaximumVersion = MaximumVersion is not null;180 181 if (hasVersion && hasMaximumVersion)182 {183 return StringUtil.Format(184 Modules.RequiredModuleNotFoundModuleAndMaximumVersion,185 Name,186 Version,187 MaximumVersion);188 }189 190 if (hasVersion)191 {192 return StringUtil.Format(193 Modules.RequiredModuleNotFoundModuleVersion,194 Name,195 Version);196 }197 198 if (hasMaximumVersion)199 {200 return StringUtil.Format(201 Modules.RequiredModuleNotFoundMaximumVersion,202 Name,203 MaximumVersion);204 }205 206 return StringUtil.Format(207 Modules.RequiredModuleNotFoundWithoutVersion,208 Name);209 }210 211 internal ModuleSpecification(PSModuleInfo moduleInfo)212 {213 ArgumentNullException.ThrowIfNull(moduleInfo);214 215 this.Name = moduleInfo.Name;216 this.Version = moduleInfo.Version;217 this.Guid = moduleInfo.Guid;218 }219 220 /// <summary>221 /// Implements ToString() for a module specification. If the specification222 /// just contains a Name, then that is returned as is. Otherwise, the object is223 /// formatted as a PowerSHell hashtable.224 /// </summary>225 /// <returns></returns>226 public override string ToString()227 {228 if (Guid == null && Version == null && RequiredVersion == null && MaximumVersion == null)229 {230 return Name;231 }232 233 var moduleSpecBuilder = new StringBuilder();234 235 moduleSpecBuilder.Append("@{ ModuleName = '").Append(Name).Append('\'');236 237 if (Guid != null)238 {239 moduleSpecBuilder.Append("; Guid = '{").Append(Guid).Append("}' ");240 }241 242 if (RequiredVersion != null)243 {244 moduleSpecBuilder.Append("; RequiredVersion = '").Append(RequiredVersion).Append('\'');245 }246 else247 {248 if (Version != null)249 {250 moduleSpecBuilder.Append("; ModuleVersion = '").Append(Version).Append('\'');251 }252 253 if (MaximumVersion != null)254 {255 moduleSpecBuilder.Append("; MaximumVersion = '").Append(MaximumVersion).Append('\'');256 }257 }258 259 moduleSpecBuilder.Append(" }");260 261 return moduleSpecBuilder.ToString();262 }263 264 /// <summary>265 /// Parse the specified string into a ModuleSpecification object.266 /// </summary>267 /// <param name="input">The module specification string.</param>268 /// <param name="result">The ModuleSpecification object.</param>269 /// <returns></returns>270 public static bool TryParse(string input, out ModuleSpecification result)271 {272 result = null;273 try274 {275 Hashtable hashtable;276 if (Parser.TryParseAsConstantHashtable(input, out hashtable))277 {278 result = new ModuleSpecification(hashtable);279 return true;280 }281 }282 catch283 {284 // Ignoring the exceptions to return false285 }286 287 return false;288 }289 290 /// <summary>291 /// Copy the module specification while normalizing the name292 /// so that paths become absolute and use the right directory separators.293 /// </summary>294 /// <param name="context">The current execution context. Used for path normalization.</param>295 /// <param name="basePath">The base path where a relative path should be interpreted with respect to.</param>296 /// <returns>A fresh module specification object with the name normalized for use internally.</returns>297 internal ModuleSpecification WithNormalizedName(ExecutionContext context, string basePath)298 {299 // Save allocating a new module spec if we don't need to change anything300 if (!ModuleIntrinsics.IsModuleNamePath(Name))301 {302 return this;303 }304 305 return new ModuleSpecification()306 {307 Guid = Guid,308 MaximumVersion = MaximumVersion,309 Version = Version,310 RequiredVersion = RequiredVersion,311 Name = ModuleIntrinsics.NormalizeModuleName(Name, basePath, context)312 };313 }314 315 /// <summary>316 /// The module name.317 /// </summary>318 public string Name { get; internal set; }319 320 /// <summary>321 /// The module GUID, if specified.322 /// </summary>323 public Guid? Guid { get; internal set; }324 325 /// <summary>326 /// The module version number if specified, otherwise null.327 /// </summary>328 public Version Version { get; internal set; }329 330 /// <summary>331 /// The module maxVersion number if specified, otherwise null.332 /// </summary>333 public string MaximumVersion { get; internal set; }334 335 /// <summary>336 /// The exact version of the module if specified, otherwise null.337 /// </summary>338 public Version RequiredVersion { get; internal set; }339 }340 341 /// <summary>342 /// Compares two ModuleSpecification objects for equality.343 /// </summary>344 internal class ModuleSpecificationComparer : IEqualityComparer<ModuleSpecification>345 {346 /// <summary>347 /// Check if two module specifications are property-wise equal.348 /// </summary>349 /// <param name="x"></param>350 /// <param name="y"></param>351 /// <returns>True if the specifications are equal, false otherwise.</returns>352 public bool Equals(ModuleSpecification x, ModuleSpecification y)353 {354 if (x == y)355 {356 return true;357 }358 359 return x != null && y != null360 && string.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase)361 && Guid.Equals(x.Guid, y.Guid)362 && Version.Equals(x.RequiredVersion, y.RequiredVersion)363 && Version.Equals(x.Version, y.Version)364 && string.Equals(x.MaximumVersion, y.MaximumVersion);365 }366 367 /// <summary>368 /// Get a property-based hashcode for a ModuleSpecification object.369 /// </summary>370 /// <param name="obj">The module specification for the object.</param>371 /// <returns>A hashcode that is always the same for any module specification with the same properties.</returns>372 public int GetHashCode(ModuleSpecification obj)373 {374 if (obj == null)375 {376 return 0;377 }378 379 return HashCode.Combine(obj.Name, obj.Guid, obj.RequiredVersion, obj.Version, obj.MaximumVersion);380 }381 }382 383 #endregion384}385 