< Summary

Information
Class: NGql.Tool.Program
Assembly: NGql.Tool
File(s): /home/runner/work/NGql/NGql/tools/Tool/Program.cs
Line coverage
100%
Covered lines: 40
Uncovered lines: 0
Coverable lines: 40
Total lines: 94
Line coverage: 100%
Branch coverage
100%
Covered branches: 12
Total branches: 12
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Main()100%66100%
EscapeStdInArgument(...)100%22100%
GetVersion()100%11100%
GetVersion(...)100%44100%

File(s)

/home/runner/work/NGql/NGql/tools/Tool/Program.cs

#LineLine coverage
 1using System.Reflection;
 2using Spectre.Console.Cli;
 3
 4namespace NGql.Tool;
 5
 6internal static class Program
 7{
 8    public static async Task<int> Main(string[] args)
 9    {
 10        // Handle --version and -v before delegating to Spectre, since Spectre 0.49 doesn't
 11        // automatically fire the built-in version path when the default command claims unknown
 12        // args. Doing it here keeps the contract: `ngql --version` prints a single semver line
 13        // on stdout and exits 0.
 17714        if (args.Length == 1 && (args[0] == "--version" || args[0] == "-v"))
 15        {
 916            Console.WriteLine(GetVersion());
 917            return 0;
 18        }
 19
 16820        args = EscapeStdInArgument(args);
 21
 16822        var app = new CommandApp<RenderCommand>();
 16823        app.Configure(config =>
 16824        {
 16825            config.SetApplicationName("ngql");
 16826            config.SetApplicationVersion(GetVersion());
 16827
 16828            // Internal subcommands used by the project's regression harness (`make skill-eval`).
 16829            // Hidden from --help but discoverable via direct invocation.
 16830            config.AddCommand<FixturesCommand>("fixtures")
 16831                .IsHidden()
 16832                .WithDescription("Run all built-in regression fixtures (contributor use only).");
 16833            config.AddCommand<FixtureCommand>("fixture")
 16834                .IsHidden()
 16835                .WithDescription("Run a single named regression fixture (contributor use only).");
 16836
 16837            config.PropagateExceptions();
 33638        });
 39
 40        try
 41        {
 16842            return await app.RunAsync(args);
 43        }
 944        catch (CommandRuntimeException ex)
 45        {
 46            // Spectre's parse / validation / runtime failures (CommandParseException derives from this).
 947            await Console.Error.WriteLineAsync($"ngql: {ex.Message}");
 948            await Console.Error.WriteLineAsync("See `ngql --help`.");
 949            return ExitCodes.InvalidUsage;
 50        }
 17751    }
 52
 53    /// <summary>
 54    /// Spectre 0.49 reads a bare "-" as a malformed option ("Option does not have a name") rather
 55    /// than as the documented stdin argument, so it never reaches the command. Swap it for the
 56    /// marker the command recognises before parsing.
 57    /// </summary>
 58    private static string[] EscapeStdInArgument(string[] args)
 59    {
 16860        var dash = Array.IndexOf(args, "-");
 16861        if (dash < 0)
 16262            return args;
 63
 664        var escaped = (string[])args.Clone();
 665        escaped[dash] = RenderCommand.StdInMarker;
 666        return escaped;
 67    }
 68
 17769    private static string GetVersion() => GetVersion(typeof(Program).Assembly);
 70
 71    public static string GetVersion(Assembly asm)
 72    {
 18973        var info = asm.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
 74        // AssemblyName.Version is always populated for a loaded assembly, so no fallback is needed.
 18975        var v = info.Length > 0
 18976            ? ((AssemblyInformationalVersionAttribute)info[0]).InformationalVersion
 18977            : asm.GetName().Version!.ToString();
 78
 79        // Strip the "+<commit-sha>" SourceLink suffix.
 18980        var plus = v.IndexOf('+');
 18981        return plus > 0 ? v[..plus] : v;
 82    }
 83}
 84
 85internal static class ExitCodes
 86{
 87    public const int Ok                = 0;
 88    public const int RenderFailed      = 1;
 89    public const int GraphQlErrors     = 2;
 90    public const int HttpFailure       = 3;
 91    public const int MutationBlocked   = 4;
 92    public const int InvalidUsage      = 64;
 93    public const int FileNotFound      = 66;
 94}