File: ResultNavigationExtensions.cs
Web Access
Project: src\src\sdk\src\Cli\Microsoft.DotNet.Cli.CommandLine\Microsoft.DotNet.Cli.CommandLine.csproj (Microsoft.DotNet.Cli.CommandLine)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.CommandLine;
using System.CommandLine.Parsing;

namespace Microsoft.DotNet.Cli.CommandLine;

/// <summary>
/// Extension methods for safely navigating ParseResult and SymbolResult to get option values.
/// </summary>
public static class ResultNavigationExtensions
{
    /// <summary>
    /// Only returns the value for this option if the option is present and there are no parse errors for that option.
    /// This allows cross-cutting code like the telemetry filters to safely get the value without throwing on null-ref errors.
    /// If you are inside a command handler or 'normal' System.CommandLine code then you don't need this - the parse error handling
    /// will have covered these cases.
    /// </summary>
    public static T? SafelyGetValueForOption<T>(this ParseResult parseResult, Option<T> optionToGet)
    {
        if (parseResult.GetResult(optionToGet) is OptionResult optionResult // only return a value if there _is_ a value - default or otherwise
            && !parseResult.Errors.Any(e => e.SymbolResult == optionResult) // only return a value if this isn't a parsing error
            && optionResult.Option.ValueType.IsAssignableTo(typeof(T))) // only return a value if coercing the type won't error
        {
            // shouldn't happen because of the above checks, but we can be safe since this is only used in telemetry, and should
            // be resistant to errors
            try
            {
                return optionResult.GetValue(optionToGet);
            }
            catch
            {
                return default;
            }
        }
        else
        {
            return default;
        }
    }

    /// <summary>
    /// Only returns the value for this option if the option is present and there are no parse errors for that option.
    /// This allows cross-cutting code like the telemetry filters to safely get the value without throwing on null-ref errors.
    /// If you are inside a command handler or 'normal' System.CommandLine code then you don't need this - the parse error handling
    /// will have covered these cases.
    /// </summary>
    public static T? SafelyGetValueForOption<T>(this ParseResult parseResult, string name)
    {
        if (parseResult.GetResult(name) is OptionResult optionResult // only return a value if there _is_ a value - default or otherwise
            && !parseResult.Errors.Any(e => e.SymbolResult == optionResult) // only return a value if this isn't a parsing error
            && optionResult.Option.ValueType.IsAssignableTo(typeof(T))) // only return a value if coercing the type won't error
        {
            // shouldn't happen because of the above checks, but we can be safe since this is only used in telemetry, and should
            // be resistant to errors
            try
            {
                return optionResult.GetValue<T>(name);
            }
            catch
            {
                return default;
            }
        }
        else
        {
            return default;
        }
    }

    /// <summary>
    /// Checks if the option is present and not implicit (i.e. not set by default).
    /// This is useful for checking if the user has explicitly set an option, as opposed to it being set by default.
    /// </summary>
    public static bool HasOption(this ParseResult parseResult, Option option) => parseResult.GetResult(option) is OptionResult or && !or.Implicit;

    /// <summary>
    /// Checks if the option with given name is present and not implicit (i.e. not set by default).
    /// This is useful for checking if the user has explicitly set an option, as opposed to it being set by default.
    /// </summary>
    public static bool HasOption(this ParseResult parseResult, string name)
        => parseResult.GetResult(name) is OptionResult or && !or.Implicit;

    /// <summary>
    /// Checks if the option is present and not implicit (i.e. not set by default).
    /// This is useful for checking if the user has explicitly set an option, as opposed to it being set by default.
    /// </summary>
    public static bool HasOption(this SymbolResult symbolResult, Option option) => symbolResult.GetResult(option) is OptionResult or && !or.Implicit;

    /// <summary>
    /// Checks if the option with given name is present and not implicit (i.e. not set by default).
    /// This is useful for checking if the user has explicitly set an option, as opposed to it being set by default.
    /// </summary>
    public static bool HasOption(this SymbolResult symbolResult, string name)
        => symbolResult.GetResult(name) is OptionResult or && !or.Implicit;
}