150 lines
5.5 KiB
C#
150 lines
5.5 KiB
C#
using System.Management.Automation;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Text.Json;
|
|
using LANCommander.SDK.PowerShell.Extensions;
|
|
|
|
if (args.Length == 0)
|
|
{
|
|
Console.Error.WriteLine("Usage: LANCommander.CompletionGenerator <output-path>");
|
|
return 1;
|
|
}
|
|
|
|
var outputPath = args[0];
|
|
|
|
var assembly = typeof(InitialSessionStateExtensions).Assembly;
|
|
|
|
var cmdletTypes = assembly.GetTypes()
|
|
.Where(t => t.GetCustomAttribute<CmdletAttribute>() != null)
|
|
.OrderBy(t => t.GetCustomAttribute<CmdletAttribute>()!.VerbName + "-" + t.GetCustomAttribute<CmdletAttribute>()!.NounName);
|
|
|
|
var sb = new StringBuilder();
|
|
sb.AppendLine("// Auto-generated by LANCommander.CompletionGenerator — do not edit manually");
|
|
sb.AppendLine();
|
|
sb.AppendLine("export interface CmdletParameter {");
|
|
sb.AppendLine(" name: string;");
|
|
sb.AppendLine(" type: string;");
|
|
sb.AppendLine(" mandatory: boolean;");
|
|
sb.AppendLine(" position: number | null;");
|
|
sb.AppendLine(" helpMessage: string | null;");
|
|
sb.AppendLine(" aliases: string[];");
|
|
sb.AppendLine("}");
|
|
sb.AppendLine();
|
|
sb.AppendLine("export interface CmdletDefinition {");
|
|
sb.AppendLine(" name: string;");
|
|
sb.AppendLine(" description: string | null;");
|
|
sb.AppendLine(" outputType: string | null;");
|
|
sb.AppendLine(" parameters: CmdletParameter[];");
|
|
sb.AppendLine("}");
|
|
sb.AppendLine();
|
|
sb.AppendLine("export const cmdlets: CmdletDefinition[] = [");
|
|
|
|
foreach (var type in cmdletTypes)
|
|
{
|
|
var cmdletAttr = type.GetCustomAttribute<CmdletAttribute>()!;
|
|
var name = $"{cmdletAttr.VerbName}-{cmdletAttr.NounName}";
|
|
|
|
var outputTypeAttr = type.GetCustomAttribute<OutputTypeAttribute>();
|
|
string? outputType = null;
|
|
if (outputTypeAttr?.Type?.Length > 0)
|
|
outputType = GetFriendlyTypeName(outputTypeAttr.Type[0].Type);
|
|
|
|
// Use XML doc summary if available via the Summary property pattern, otherwise use null
|
|
var descriptionLines = type.GetCustomAttributes()
|
|
.Where(a => a.GetType().Name == "DescriptionAttribute")
|
|
.Select(a => a.GetType().GetProperty("Description")?.GetValue(a)?.ToString())
|
|
.FirstOrDefault();
|
|
|
|
// Fall back to XML summary comment — not available via reflection, use class summary convention
|
|
string? description = descriptionLines;
|
|
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine($" name: {JsonEncode(name)},");
|
|
sb.AppendLine($" description: {JsonEncode(description)},");
|
|
sb.AppendLine($" outputType: {JsonEncode(outputType)},");
|
|
sb.AppendLine(" parameters: [");
|
|
|
|
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
|
.Where(p => p.GetCustomAttribute<ParameterAttribute>() != null)
|
|
.OrderBy(p =>
|
|
{
|
|
var pa = p.GetCustomAttribute<ParameterAttribute>()!;
|
|
return pa.Position == int.MinValue ? int.MaxValue : pa.Position;
|
|
})
|
|
.ThenBy(p => p.Name);
|
|
|
|
foreach (var prop in properties)
|
|
{
|
|
var paramAttr = prop.GetCustomAttribute<ParameterAttribute>()!;
|
|
var aliasAttr = prop.GetCustomAttribute<AliasAttribute>();
|
|
var aliases = aliasAttr?.AliasNames?.ToArray() ?? Array.Empty<string>();
|
|
var position = paramAttr.Position == int.MinValue ? (int?)null : paramAttr.Position;
|
|
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine($" name: {JsonEncode(prop.Name)},");
|
|
sb.AppendLine($" type: {JsonEncode(GetFriendlyTypeName(prop.PropertyType))},");
|
|
sb.AppendLine($" mandatory: {(paramAttr.Mandatory ? "true" : "false")},");
|
|
sb.AppendLine($" position: {(position.HasValue ? position.Value.ToString() : "null")},");
|
|
sb.AppendLine($" helpMessage: {JsonEncode(paramAttr.HelpMessage)},");
|
|
sb.AppendLine($" aliases: [{string.Join(", ", aliases.Select(a => JsonEncode(a)))}],");
|
|
sb.AppendLine(" },");
|
|
}
|
|
|
|
sb.AppendLine(" ],");
|
|
sb.AppendLine(" },");
|
|
}
|
|
|
|
sb.AppendLine("];");
|
|
|
|
var directory = Path.GetDirectoryName(outputPath);
|
|
if (!string.IsNullOrEmpty(directory))
|
|
Directory.CreateDirectory(directory);
|
|
|
|
File.WriteAllText(outputPath, sb.ToString(), Encoding.UTF8);
|
|
|
|
Console.WriteLine($"Generated completions for {cmdletTypes.Count()} cmdlets -> {outputPath}");
|
|
return 0;
|
|
|
|
static string JsonEncode(string? value)
|
|
{
|
|
if (value == null) return "null";
|
|
return JsonSerializer.Serialize(value);
|
|
}
|
|
|
|
static string GetFriendlyTypeName(Type? type)
|
|
{
|
|
if (type == null) return "object";
|
|
|
|
// Handle nullable types
|
|
var underlying = Nullable.GetUnderlyingType(type);
|
|
if (underlying != null)
|
|
return GetFriendlyTypeName(underlying) + "?";
|
|
|
|
// Handle arrays
|
|
if (type.IsArray)
|
|
return GetFriendlyTypeName(type.GetElementType()) + "[]";
|
|
|
|
// Handle SwitchParameter
|
|
if (type == typeof(SwitchParameter))
|
|
return "SwitchParameter";
|
|
|
|
// Handle common types
|
|
if (type == typeof(string)) return "string";
|
|
if (type == typeof(int)) return "int";
|
|
if (type == typeof(long)) return "long";
|
|
if (type == typeof(uint)) return "uint";
|
|
if (type == typeof(double)) return "double";
|
|
if (type == typeof(float)) return "float";
|
|
if (type == typeof(bool)) return "bool";
|
|
if (type == typeof(byte)) return "byte";
|
|
if (type == typeof(byte[])) return "byte[]";
|
|
if (type == typeof(Guid)) return "Guid";
|
|
if (type == typeof(Uri)) return "Uri";
|
|
if (type == typeof(object)) return "object";
|
|
|
|
// Handle SecureString
|
|
if (type == typeof(System.Security.SecureString))
|
|
return "SecureString";
|
|
|
|
return type.Name;
|
|
}
|