861 lines
33 KiB
C#
861 lines
33 KiB
C#
// -----------------------------------------------------------------------------
|
|
// file="RegistryCli"
|
|
// Project: StgSharp
|
|
// Copyright (c) Nitload.
|
|
// SPDX-License-Identifier: MIT
|
|
// -----------------------------------------------------------------------------
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using StgSharp.GenerateGL.Registry;
|
|
|
|
namespace StgSharp.GenerateGL
|
|
{
|
|
/// <summary>
|
|
/// Presents the parsed registry model as the primary CLI surface.
|
|
/// </summary>
|
|
internal static class RegistryCli
|
|
{
|
|
|
|
private const int DefaultFindLimit = 10;
|
|
private const int DefaultListLimit = 20;
|
|
private const int QueryErrorExitCode = 1;
|
|
private const int SuccessExitCode = 0;
|
|
private const int UsageErrorExitCode = 2;
|
|
|
|
public static int Execute(
|
|
GlRegistryModel registry,
|
|
string command,
|
|
IReadOnlyList<string> arguments
|
|
)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(registry);
|
|
return command switch
|
|
{
|
|
"SUMMARY" or "REGISTRY" or "MODEL" => QuerySummary(
|
|
registry,
|
|
arguments),
|
|
"LIST" or "LS" => QueryList(registry, arguments),
|
|
"FIND" or "SEARCH" => QueryFind(registry, arguments),
|
|
"SHOW" or "INFO" => QueryShow(registry, arguments),
|
|
_ => WriteUsageError($"Unknown registry command '{command}'."),
|
|
};
|
|
}
|
|
|
|
private static int QuerySummary(
|
|
GlRegistryModel registry,
|
|
IReadOnlyCollection<string> arguments
|
|
)
|
|
{
|
|
if (!ExpectNoArguments("summary", arguments))
|
|
{
|
|
return UsageErrorExitCode;
|
|
}
|
|
|
|
GlRequirementBlock[] requirements = registry.Features.SelectMany(
|
|
value => value.Requirements)
|
|
.Concat(
|
|
registry.Extensions.SelectMany(
|
|
value => value.Requirements))
|
|
.ToArray();
|
|
Console.WriteLine("OpenGL registry");
|
|
WriteProperty("source", registry.SourcePath);
|
|
WriteProperty("types", registry.Types.Count);
|
|
WriteProperty("kinds", registry.Kinds.Count);
|
|
WriteProperty("enum groups", registry.EnumGroups.Count);
|
|
WriteProperty("enum blocks", registry.EnumBlocks.Count);
|
|
WriteProperty(
|
|
"enums",
|
|
registry.EnumBlocks.Sum(value => value.Enums.Count));
|
|
WriteProperty(
|
|
"unused enum ranges",
|
|
registry.EnumBlocks.Sum(value => value.UnusedRanges.Count));
|
|
WriteProperty("commands", registry.Commands.Count);
|
|
WriteProperty(
|
|
"parameters",
|
|
registry.Commands.Sum(value => value.Parameters.Count));
|
|
WriteProperty("features", registry.Features.Count);
|
|
WriteProperty("extensions", registry.Extensions.Count);
|
|
WriteProperty(
|
|
"require blocks",
|
|
requirements.Count(
|
|
value => value.Operation == GlRequirementOperation.Require));
|
|
WriteProperty(
|
|
"remove blocks",
|
|
requirements.Count(
|
|
value => value.Operation == GlRequirementOperation.Remove));
|
|
WriteProperty(
|
|
"named references",
|
|
requirements.Sum(value => value.References.Count));
|
|
return SuccessExitCode;
|
|
}
|
|
|
|
private static int QueryList(
|
|
GlRegistryModel registry,
|
|
IReadOnlyList<string> arguments
|
|
)
|
|
{
|
|
if (!TryReadListOptions(
|
|
arguments,
|
|
out RegistryObjectKind kind,
|
|
out string? filter,
|
|
out int limit,
|
|
out string? error))
|
|
{
|
|
return WriteUsageError(error);
|
|
}
|
|
|
|
RegistrySearchResult[] values = Enumerate(registry, kind)
|
|
.Where(
|
|
value => filter is null || value.Name.Contains(
|
|
filter,
|
|
StringComparison.OrdinalIgnoreCase))
|
|
.ToArray();
|
|
if (values.Length == 0)
|
|
{
|
|
Console.Error.WriteLine(
|
|
filter is null
|
|
? $"No {ReadKindName(kind, true)} are registered."
|
|
: $"No {ReadKindName(kind, true)} contain '{filter}'.");
|
|
return QueryErrorExitCode;
|
|
}
|
|
|
|
int displayedCount = Math.Min(values.Length, limit);
|
|
for (int index = 0; index < displayedCount; index++)
|
|
{
|
|
WriteCompactResult(index + 1, values[index]);
|
|
}
|
|
|
|
WriteTruncation(values.Length, displayedCount);
|
|
return SuccessExitCode;
|
|
}
|
|
|
|
private static int QueryFind(
|
|
GlRegistryModel registry,
|
|
IReadOnlyList<string> arguments
|
|
)
|
|
{
|
|
if (!TryReadFindOptions(
|
|
arguments,
|
|
out string text,
|
|
out bool exact,
|
|
out int limit,
|
|
out string? error))
|
|
{
|
|
return WriteUsageError(error);
|
|
}
|
|
|
|
RegistrySearchResult[] values = Enumerate(registry)
|
|
.Where(
|
|
value => exact
|
|
? string.Equals(
|
|
value.Name,
|
|
text,
|
|
StringComparison.OrdinalIgnoreCase)
|
|
: value.Name.Contains(
|
|
text,
|
|
StringComparison.OrdinalIgnoreCase))
|
|
.OrderBy(
|
|
value => value.Name,
|
|
StringComparer.Ordinal)
|
|
.ThenBy(value => value.Kind)
|
|
.ToArray();
|
|
if (values.Length == 0)
|
|
{
|
|
Console.Error.WriteLine($"No registry names match '{text}'.");
|
|
return QueryErrorExitCode;
|
|
}
|
|
|
|
int displayedCount = Math.Min(values.Length, limit);
|
|
for (int index = 0; index < displayedCount; index++)
|
|
{
|
|
WriteCompactResult(index + 1, values[index]);
|
|
}
|
|
|
|
WriteTruncation(values.Length, displayedCount);
|
|
return SuccessExitCode;
|
|
}
|
|
|
|
private static int QueryShow(
|
|
GlRegistryModel registry,
|
|
IReadOnlyList<string> arguments
|
|
)
|
|
{
|
|
if (!TryReadShowOptions(
|
|
arguments,
|
|
out RegistryObjectKind kind,
|
|
out string name,
|
|
out int limit,
|
|
out string? error))
|
|
{
|
|
return WriteUsageError(error);
|
|
}
|
|
|
|
RegistrySearchResult[] values = Enumerate(registry, kind)
|
|
.Where(
|
|
value => string.Equals(
|
|
value.Name,
|
|
name,
|
|
StringComparison.Ordinal))
|
|
.ToArray();
|
|
if (values.Length == 0)
|
|
{
|
|
Console.Error.WriteLine(
|
|
$"No {ReadKindName(kind, false)} named '{name}' is registered.");
|
|
Console.Error.WriteLine($"Try: find {name}");
|
|
return QueryErrorExitCode;
|
|
}
|
|
|
|
for (int index = 0; index < values.Length; index++)
|
|
{
|
|
if (index != 0)
|
|
{
|
|
Console.WriteLine();
|
|
}
|
|
|
|
if (values.Length > 1)
|
|
{
|
|
Console.WriteLine($"[{index + 1}]");
|
|
}
|
|
|
|
WriteDetailedResult(values[index], limit);
|
|
}
|
|
|
|
return SuccessExitCode;
|
|
}
|
|
|
|
private static IEnumerable<RegistrySearchResult> Enumerate(
|
|
GlRegistryModel registry
|
|
)
|
|
{
|
|
foreach (RegistryObjectKind kind in Enum.GetValues<RegistryObjectKind>())
|
|
{
|
|
foreach (RegistrySearchResult value in Enumerate(registry, kind))
|
|
{
|
|
yield return value;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<RegistrySearchResult> Enumerate(
|
|
GlRegistryModel registry,
|
|
RegistryObjectKind kind
|
|
)
|
|
{
|
|
return kind switch
|
|
{
|
|
RegistryObjectKind.Type => registry.Types.Select(
|
|
value => new RegistrySearchResult(
|
|
kind,
|
|
value.Name,
|
|
value,
|
|
value.Element)),
|
|
RegistryObjectKind.Kind => registry.Kinds.Select(
|
|
value => new RegistrySearchResult(
|
|
kind,
|
|
value.Name,
|
|
value,
|
|
value.Element)),
|
|
RegistryObjectKind.EnumGroup => registry.EnumGroups.Select(
|
|
value => new RegistrySearchResult(
|
|
kind,
|
|
value.Name,
|
|
value,
|
|
value.Element)),
|
|
RegistryObjectKind.Enum => registry.EnumBlocks.SelectMany(
|
|
value => value.Enums).Select(
|
|
value => new RegistrySearchResult(
|
|
kind,
|
|
value.Name,
|
|
value,
|
|
value.Element)),
|
|
RegistryObjectKind.Command => registry.Commands.Select(
|
|
value => new RegistrySearchResult(
|
|
kind,
|
|
value.Name,
|
|
value,
|
|
value.Element)),
|
|
RegistryObjectKind.Feature => registry.Features.Select(
|
|
value => new RegistrySearchResult(
|
|
kind,
|
|
value.Name,
|
|
value,
|
|
value.Element)),
|
|
RegistryObjectKind.Extension => registry.Extensions.Select(
|
|
value => new RegistrySearchResult(
|
|
kind,
|
|
value.Name,
|
|
value,
|
|
value.Element)),
|
|
_ => throw new ArgumentOutOfRangeException(nameof(kind)),
|
|
};
|
|
}
|
|
|
|
private static void WriteCompactResult(
|
|
int index,
|
|
RegistrySearchResult result
|
|
)
|
|
{
|
|
Console.WriteLine(
|
|
$"[{index}] {ReadKindName(result.Kind, false)}: {result.Name}");
|
|
switch (result.Value)
|
|
{
|
|
case GlTypeDefinition type:
|
|
WriteProperty("declaration", type.DeclarationText);
|
|
break;
|
|
|
|
case GlKindDefinition kind:
|
|
WriteProperty("description", kind.Description);
|
|
break;
|
|
|
|
case GlEnumGroup group:
|
|
WriteProperty("members", group.Members.Count);
|
|
break;
|
|
|
|
case GlEnumDefinition enumValue:
|
|
WriteOptionalProperty("value", enumValue.Value);
|
|
WriteOptionalProperty("alias", enumValue.Alias);
|
|
WriteOptionalProperty("group", enumValue.Group);
|
|
break;
|
|
|
|
case GlCommandDefinition command:
|
|
WriteProperty("declaration", ReadCommandDeclaration(command));
|
|
break;
|
|
|
|
case GlFeatureDefinition feature:
|
|
WriteProperty("api", feature.Api);
|
|
WriteProperty("version", feature.Number);
|
|
break;
|
|
|
|
case GlExtensionDefinition extension:
|
|
WriteOptionalProperty("supported", extension.SupportedApis);
|
|
break;
|
|
}
|
|
|
|
WriteProperty("line", result.Element.Location.LineNumber);
|
|
}
|
|
|
|
private static void WriteDetailedResult(
|
|
RegistrySearchResult result,
|
|
int referenceLimit
|
|
)
|
|
{
|
|
switch (result.Value)
|
|
{
|
|
case GlTypeDefinition type:
|
|
WriteType(type);
|
|
break;
|
|
|
|
case GlKindDefinition kind:
|
|
WriteKind(kind);
|
|
break;
|
|
|
|
case GlEnumGroup group:
|
|
WriteEnumGroup(group, referenceLimit);
|
|
break;
|
|
|
|
case GlEnumDefinition enumValue:
|
|
WriteEnum(enumValue);
|
|
break;
|
|
|
|
case GlCommandDefinition command:
|
|
WriteCommand(command);
|
|
break;
|
|
|
|
case GlFeatureDefinition feature:
|
|
Console.WriteLine($"feature: {feature.Name}");
|
|
WriteProperty("api", feature.Api);
|
|
WriteProperty("version", feature.Number);
|
|
WriteOptionalProperty("protect", feature.Protect);
|
|
WriteOptionalProperty("comment", feature.Comment);
|
|
WriteProperty("line", feature.Element.Location.LineNumber);
|
|
WriteRequirements(feature.Requirements, referenceLimit);
|
|
break;
|
|
|
|
case GlExtensionDefinition extension:
|
|
Console.WriteLine($"extension: {extension.Name}");
|
|
WriteOptionalProperty("supported", extension.SupportedApis);
|
|
WriteOptionalProperty("protect", extension.Protect);
|
|
WriteOptionalProperty("comment", extension.Comment);
|
|
WriteProperty("line", extension.Element.Location.LineNumber);
|
|
WriteRequirements(extension.Requirements, referenceLimit);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static void WriteType(
|
|
GlTypeDefinition value
|
|
)
|
|
{
|
|
Console.WriteLine($"type: {value.Name}");
|
|
WriteProperty("declaration", value.DeclarationText);
|
|
WriteOptionalProperty("api", value.Api);
|
|
WriteOptionalProperty("requires", value.RequiredType);
|
|
WriteOptionalProperty("category", value.Category);
|
|
WriteOptionalProperty("comment", value.Comment);
|
|
WriteProperty("line", value.Element.Location.LineNumber);
|
|
}
|
|
|
|
private static void WriteKind(
|
|
GlKindDefinition value
|
|
)
|
|
{
|
|
Console.WriteLine($"kind: {value.Name}");
|
|
WriteProperty("description", value.Description);
|
|
WriteProperty("line", value.Element.Location.LineNumber);
|
|
}
|
|
|
|
private static void WriteEnumGroup(
|
|
GlEnumGroup value,
|
|
int limit
|
|
)
|
|
{
|
|
Console.WriteLine($"enum group: {value.Name}");
|
|
WriteOptionalProperty("comment", value.Comment);
|
|
WriteProperty("line", value.Element.Location.LineNumber);
|
|
WriteProperty("members", value.Members.Count);
|
|
int displayedCount = Math.Min(value.Members.Count, limit);
|
|
for (int index = 0; index < displayedCount; index++)
|
|
{
|
|
Console.WriteLine($" [{index + 1}] {value.Members[index].Name}");
|
|
}
|
|
|
|
WriteIndentedTruncation(value.Members.Count, displayedCount, 4);
|
|
}
|
|
|
|
private static void WriteEnum(
|
|
GlEnumDefinition value
|
|
)
|
|
{
|
|
Console.WriteLine($"enum: {value.Name}");
|
|
WriteOptionalProperty("value", value.Value);
|
|
WriteOptionalProperty("api", value.Api);
|
|
WriteOptionalProperty("type suffix", value.TypeSuffix);
|
|
WriteOptionalProperty("group", value.Group);
|
|
WriteOptionalProperty("alias", value.Alias);
|
|
WriteOptionalProperty("comment", value.Comment);
|
|
WriteProperty("line", value.Element.Location.LineNumber);
|
|
}
|
|
|
|
private static void WriteCommand(
|
|
GlCommandDefinition value
|
|
)
|
|
{
|
|
Console.WriteLine($"command: {value.Name}");
|
|
WriteProperty("declaration", ReadCommandDeclaration(value));
|
|
WriteProperty("line", value.Element.Location.LineNumber);
|
|
WriteProperty("return declaration", value.Return.DeclarationText);
|
|
WriteOptionalProperty("return type", value.Return.TypeName);
|
|
WriteOptionalProperty("return group", value.Return.Group);
|
|
WriteOptionalProperty("return kind", value.Return.Kind);
|
|
WriteOptionalProperty("return class", value.Return.ObjectClass);
|
|
WriteOptionalProperty("comment", value.Comment);
|
|
WriteProperty("parameters", value.Parameters.Count);
|
|
for (int index = 0; index < value.Parameters.Count; index++)
|
|
{
|
|
GlParameterDefinition parameter = value.Parameters[index];
|
|
Console.WriteLine($" [{index + 1}] {parameter.Name}");
|
|
WriteIndentedProperty("declaration", parameter.DeclarationText, 6);
|
|
WriteIndentedOptionalProperty("type", parameter.TypeName, 6);
|
|
WriteIndentedOptionalProperty("group", parameter.Group, 6);
|
|
WriteIndentedOptionalProperty("kind", parameter.Kind, 6);
|
|
WriteIndentedOptionalProperty("class", parameter.ObjectClass, 6);
|
|
WriteIndentedOptionalProperty("length", parameter.Length, 6);
|
|
WriteIndentedProperty("line", parameter.Element.Location.LineNumber, 6);
|
|
}
|
|
|
|
WriteNamedReferences("aliases", value.Aliases);
|
|
WriteNamedReferences("vector equivalents", value.VectorEquivalents);
|
|
if (value.Protocols.Count != 0)
|
|
{
|
|
WriteProperty("protocols", value.Protocols.Count);
|
|
foreach (GlCommandProtocol protocol in value.Protocols)
|
|
{
|
|
Console.WriteLine($" {protocol.Type}: {protocol.Opcode}");
|
|
WriteIndentedOptionalProperty("name", protocol.Name, 6);
|
|
WriteIndentedOptionalProperty("comment", protocol.Comment, 6);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void WriteRequirements(
|
|
IReadOnlyList<GlRequirementBlock> requirements,
|
|
int referenceLimit
|
|
)
|
|
{
|
|
WriteProperty("requirement blocks", requirements.Count);
|
|
for (int index = 0; index < requirements.Count; index++)
|
|
{
|
|
GlRequirementBlock block = requirements[index];
|
|
Console.WriteLine(
|
|
$" [{index + 1}] " +
|
|
$"{(block.Operation == GlRequirementOperation.Require ? "require" : "remove")}");
|
|
WriteIndentedOptionalProperty("api", block.Api, 6);
|
|
WriteIndentedOptionalProperty("profile", block.Profile, 6);
|
|
WriteIndentedOptionalProperty("comment", block.Comment, 6);
|
|
WriteIndentedProperty("references", block.References.Count, 6);
|
|
int displayedCount = Math.Min(block.References.Count, referenceLimit);
|
|
for (int referenceIndex = 0;
|
|
referenceIndex < displayedCount;
|
|
referenceIndex++)
|
|
{
|
|
GlRegistryReference reference = block.References[referenceIndex];
|
|
Console.WriteLine(
|
|
$" {ReadReferenceKind(reference.Kind)}: {reference.Name}");
|
|
}
|
|
|
|
WriteIndentedTruncation(block.References.Count, displayedCount, 8);
|
|
}
|
|
}
|
|
|
|
private static void WriteNamedReferences(
|
|
string name,
|
|
IReadOnlyList<GlNamedReference> values
|
|
)
|
|
{
|
|
if (values.Count == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WriteProperty(name, values.Count);
|
|
foreach (GlNamedReference value in values)
|
|
{
|
|
Console.WriteLine($" - {value.Name}");
|
|
}
|
|
}
|
|
|
|
private static string ReadCommandDeclaration(
|
|
GlCommandDefinition command
|
|
)
|
|
{
|
|
string returnType = command.Return.DeclarationText.Replace(
|
|
command.Name,
|
|
string.Empty,
|
|
StringComparison.Ordinal).Trim();
|
|
return $"{returnType} {command.Name}(" +
|
|
string.Join(", ", command.Parameters.Select(value => value.DeclarationText)) +
|
|
")";
|
|
}
|
|
|
|
private static bool TryReadListOptions(
|
|
IReadOnlyList<string> arguments,
|
|
out RegistryObjectKind kind,
|
|
out string? filter,
|
|
out int limit,
|
|
out string? error
|
|
)
|
|
{
|
|
kind = default;
|
|
if (arguments.Count == 0 || !TryReadKind(arguments[0], out kind))
|
|
{
|
|
filter = null;
|
|
limit = 0;
|
|
error = "list requires a registry category: types, kinds, groups, enums, " +
|
|
"commands, features, or extensions.";
|
|
return false;
|
|
}
|
|
|
|
filter = null;
|
|
limit = DefaultListLimit;
|
|
int index = 1;
|
|
if (index < arguments.Count && !arguments[index].StartsWith(
|
|
"--",
|
|
StringComparison.Ordinal))
|
|
{
|
|
filter = arguments[index++];
|
|
}
|
|
|
|
while (index < arguments.Count)
|
|
{
|
|
string argument = arguments[index++];
|
|
if (argument == "--limit" && TryReadPositiveInt(
|
|
arguments,
|
|
ref index,
|
|
out limit))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
error = $"Unknown or invalid list option '{argument}'.";
|
|
return false;
|
|
}
|
|
|
|
error = null;
|
|
return true;
|
|
}
|
|
|
|
private static bool TryReadFindOptions(
|
|
IReadOnlyList<string> arguments,
|
|
out string text,
|
|
out bool exact,
|
|
out int limit,
|
|
out string? error
|
|
)
|
|
{
|
|
if (arguments.Count == 0 || arguments[0].StartsWith(
|
|
"--",
|
|
StringComparison.Ordinal))
|
|
{
|
|
text = string.Empty;
|
|
exact = false;
|
|
limit = 0;
|
|
error = "find requires a registry name or partial name.";
|
|
return false;
|
|
}
|
|
|
|
text = arguments[0];
|
|
exact = false;
|
|
limit = DefaultFindLimit;
|
|
int index = 1;
|
|
while (index < arguments.Count)
|
|
{
|
|
string argument = arguments[index++];
|
|
if (argument == "--exact")
|
|
{
|
|
exact = true;
|
|
continue;
|
|
}
|
|
|
|
if (argument == "--limit" && TryReadPositiveInt(
|
|
arguments,
|
|
ref index,
|
|
out limit))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
error = $"Unknown or invalid find option '{argument}'.";
|
|
return false;
|
|
}
|
|
|
|
error = null;
|
|
return true;
|
|
}
|
|
|
|
private static bool TryReadShowOptions(
|
|
IReadOnlyList<string> arguments,
|
|
out RegistryObjectKind kind,
|
|
out string name,
|
|
out int limit,
|
|
out string? error
|
|
)
|
|
{
|
|
kind = default;
|
|
if (arguments.Count < 2 || !TryReadKind(arguments[0], out kind))
|
|
{
|
|
name = string.Empty;
|
|
limit = 0;
|
|
error = "show requires a registry category and exact name.";
|
|
return false;
|
|
}
|
|
|
|
name = arguments[1];
|
|
limit = DefaultListLimit;
|
|
int index = 2;
|
|
while (index < arguments.Count)
|
|
{
|
|
string argument = arguments[index++];
|
|
if (argument == "--limit" && TryReadPositiveInt(
|
|
arguments,
|
|
ref index,
|
|
out limit))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
error = $"Unknown or invalid show option '{argument}'.";
|
|
return false;
|
|
}
|
|
|
|
error = null;
|
|
return true;
|
|
}
|
|
|
|
private static bool TryReadKind(
|
|
string value,
|
|
out RegistryObjectKind kind
|
|
)
|
|
{
|
|
kind = value.ToUpperInvariant() switch
|
|
{
|
|
"TYPE" or "TYPES" => RegistryObjectKind.Type,
|
|
"KIND" or "KINDS" => RegistryObjectKind.Kind,
|
|
"GROUP" or "GROUPS" or "ENUMGROUP" or "ENUMGROUPS" =>
|
|
RegistryObjectKind.EnumGroup,
|
|
"ENUM" or "ENUMS" => RegistryObjectKind.Enum,
|
|
"COMMAND" or "COMMANDS" or "FUNCTION" or "FUNCTIONS" =>
|
|
RegistryObjectKind.Command,
|
|
"FEATURE" or "FEATURES" or "VERSION" or "VERSIONS" =>
|
|
RegistryObjectKind.Feature,
|
|
"EXTENSION" or "EXTENSIONS" => RegistryObjectKind.Extension,
|
|
_ => default,
|
|
};
|
|
return value.ToUpperInvariant() is
|
|
"TYPE" or "TYPES" or
|
|
"KIND" or "KINDS" or
|
|
"GROUP" or "GROUPS" or "ENUMGROUP" or "ENUMGROUPS" or
|
|
"ENUM" or "ENUMS" or
|
|
"COMMAND" or "COMMANDS" or "FUNCTION" or "FUNCTIONS" or
|
|
"FEATURE" or "FEATURES" or "VERSION" or "VERSIONS" or
|
|
"EXTENSION" or "EXTENSIONS";
|
|
}
|
|
|
|
private static string ReadKindName(
|
|
RegistryObjectKind kind,
|
|
bool plural
|
|
)
|
|
{
|
|
string name = kind switch
|
|
{
|
|
RegistryObjectKind.Type => "type",
|
|
RegistryObjectKind.Kind => "kind",
|
|
RegistryObjectKind.EnumGroup => "enum group",
|
|
RegistryObjectKind.Enum => "enum",
|
|
RegistryObjectKind.Command => "command",
|
|
RegistryObjectKind.Feature => "feature",
|
|
RegistryObjectKind.Extension => "extension",
|
|
_ => throw new ArgumentOutOfRangeException(nameof(kind)),
|
|
};
|
|
return plural ? $"{name}s" : name;
|
|
}
|
|
|
|
private static string ReadReferenceKind(
|
|
GlRegistryReferenceKind kind
|
|
)
|
|
{
|
|
return kind switch
|
|
{
|
|
GlRegistryReferenceKind.Type => "type",
|
|
GlRegistryReferenceKind.Enum => "enum",
|
|
GlRegistryReferenceKind.Command => "command",
|
|
_ => throw new ArgumentOutOfRangeException(nameof(kind)),
|
|
};
|
|
}
|
|
|
|
private static bool TryReadPositiveInt(
|
|
IReadOnlyList<string> arguments,
|
|
ref int index,
|
|
out int value
|
|
)
|
|
{
|
|
if (index >= arguments.Count)
|
|
{
|
|
value = 0;
|
|
return false;
|
|
}
|
|
|
|
return int.TryParse(
|
|
arguments[index++],
|
|
NumberStyles.None,
|
|
CultureInfo.InvariantCulture,
|
|
out value) && value > 0;
|
|
}
|
|
|
|
private static bool ExpectNoArguments(
|
|
string command,
|
|
IReadOnlyCollection<string> arguments
|
|
)
|
|
{
|
|
if (arguments.Count == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Console.Error.WriteLine($"error: {command} does not accept arguments.");
|
|
return false;
|
|
}
|
|
|
|
private static int WriteUsageError(
|
|
string? error
|
|
)
|
|
{
|
|
Console.Error.WriteLine($"error: {error}");
|
|
return UsageErrorExitCode;
|
|
}
|
|
|
|
private static void WriteTruncation(
|
|
int totalCount,
|
|
int displayedCount
|
|
)
|
|
{
|
|
if (displayedCount != totalCount)
|
|
{
|
|
Console.WriteLine($"... {totalCount - displayedCount} more result(s)");
|
|
}
|
|
}
|
|
|
|
private static void WriteIndentedTruncation(
|
|
int totalCount,
|
|
int displayedCount,
|
|
int indentation
|
|
)
|
|
{
|
|
if (displayedCount != totalCount)
|
|
{
|
|
Console.WriteLine(
|
|
$"{new string(' ', indentation)}... " +
|
|
$"{totalCount - displayedCount} more item(s)");
|
|
}
|
|
}
|
|
|
|
private static void WriteProperty(
|
|
string name,
|
|
object value
|
|
)
|
|
{
|
|
WriteIndentedProperty(name, value, 2);
|
|
}
|
|
|
|
private static void WriteOptionalProperty(
|
|
string name,
|
|
string? value
|
|
)
|
|
{
|
|
WriteIndentedOptionalProperty(name, value, 2);
|
|
}
|
|
|
|
private static void WriteIndentedProperty(
|
|
string name,
|
|
object value,
|
|
int indentation
|
|
)
|
|
{
|
|
Console.WriteLine($"{new string(' ', indentation)}{name}: {value}");
|
|
}
|
|
|
|
private static void WriteIndentedOptionalProperty(
|
|
string name,
|
|
string? value,
|
|
int indentation
|
|
)
|
|
{
|
|
if (value is not null)
|
|
{
|
|
WriteIndentedProperty(name, value, indentation);
|
|
}
|
|
}
|
|
|
|
private sealed record RegistrySearchResult(
|
|
RegistryObjectKind Kind,
|
|
string Name,
|
|
object Value,
|
|
GlRegistryElement Element
|
|
);
|
|
|
|
private enum RegistryObjectKind
|
|
{
|
|
Type,
|
|
Kind,
|
|
EnumGroup,
|
|
Enum,
|
|
Command,
|
|
Feature,
|
|
Extension,
|
|
}
|
|
|
|
}
|
|
}
|