ICmdLineRepresentable.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Globalization; 7 using System.Reflection; 8 9 namespace Microsoft.PowerToys.Settings.UI.Library; 10 11 /// <summary> 12 /// A helper interface to allow parsing property values from their command line representation. 13 /// </summary> 14 public interface ICmdLineRepresentable 15 { 16 public static abstract bool TryParseFromCmd(string cmd, out object result); 17 18 public abstract bool TryToCmdRepresentable(out string result); 19 20 public static sealed bool TryToCmdRepresentableFor(Type type, object value, out string result) 21 { 22 result = null; 23 if (!typeof(ICmdLineRepresentable).IsAssignableFrom(type)) 24 { 25 throw new ArgumentException($"{type} doesn't implement {nameof(ICmdLineRepresentable)}"); 26 } 27 28 var method = type.GetMethod(nameof(TryToCmdRepresentable)); 29 var parameters = new object[] { result }; 30 if ((bool)method.Invoke(value, parameters)) 31 { 32 result = (string)parameters[0]; 33 return true; 34 } 35 36 return false; 37 } 38 39 public static sealed bool TryParseFromCmdFor(Type type, string cmd, out object result) 40 { 41 result = null; 42 if (!typeof(ICmdLineRepresentable).IsAssignableFrom(type)) 43 { 44 throw new ArgumentException($"{type} doesn't implement {nameof(ICmdLineRepresentable)}"); 45 } 46 47 var method = type.GetMethod(nameof(TryParseFromCmd), BindingFlags.Static | BindingFlags.Public); 48 var parameters = new object[] { cmd, null }; 49 if ((bool)method.Invoke(null, parameters)) 50 { 51 result = parameters[1]; 52 return true; 53 } 54 55 return false; 56 } 57 58 public static sealed object ParseFor(Type type, string cmdRepr) 59 { 60 if (type.IsEnum) 61 { 62 return Enum.Parse(type, cmdRepr); 63 } 64 else if (type.IsPrimitive) 65 { 66 if (type == typeof(bool)) 67 { 68 return bool.Parse(cmdRepr.ToLowerInvariant()); 69 } 70 else 71 { 72 // Converts numeric types like Uint32 73 return Convert.ChangeType(cmdRepr, type, CultureInfo.InvariantCulture); 74 } 75 } 76 else if (type.IsValueType && type == typeof(DateTimeOffset)) 77 { 78 if (DateTimeOffset.TryParse(cmdRepr, out var structResult)) 79 { 80 return structResult; 81 } 82 83 throw new ArgumentException($"Invalid DateTimeOffset format '{cmdRepr}'"); 84 } 85 else if (type.IsClass) 86 { 87 if (type == typeof(string)) 88 { 89 return cmdRepr; 90 } 91 else 92 { 93 TryParseFromCmdFor(type, cmdRepr, out var classResult); 94 return classResult; 95 } 96 } 97 98 throw new NotImplementedException($"Parsing type {type} is not supported yet"); 99 } 100 101 public static string ToCmdRepr(Type type, object value) 102 { 103 if (type.IsEnum || type.IsPrimitive) 104 { 105 return value.ToString(); 106 } 107 else if (type.IsValueType && type == typeof(DateTimeOffset)) 108 { 109 return ((DateTimeOffset)value).ToString("o"); 110 } 111 else if (type.IsClass) 112 { 113 if (type == typeof(string)) 114 { 115 return (string)value; 116 } 117 else 118 { 119 TryToCmdRepresentableFor(type, value, out var result); 120 return result; 121 } 122 } 123 124 throw new NotImplementedException($"CmdRepr of {type} is not supported yet"); 125 } 126 }