如何使用 System.CommandLine 绑定 NodaTime.Duration?

How to bind NodaTime.Duration using System.CommandLine?

是否可以使用 System.CommandLine 直接将命令行输入绑定到 NodaTime.Duration(或 System.TimeSpan)?也许通过在某处提供自定义转换器?

static void Main(string[] args)
{
    var rootCommand = new RootCommand
    {
        new Option<Duration>("--my-duration"),
    };

    rootCommand.Handler = CommandHandler.Create(
        (Duration myDuration) => { Console.WriteLine(myDuration); });

    rootCommand.InvokeAsync(args);
}

我用 System.CommandLine 的工作不多,关于自定义解析的文档也不多,但我相信您想要的是 ParseArgument<T>。幸运的是,在 NodaTime IPattern<T> 上编写扩展方法来创建其实例相当容易。

这里有一个未经充分测试的扩展方法:

using NodaTime.Text;
using System.CommandLine.Parsing;

namespace Demo
{
    public static class PatternExtensions
    {
        public static ParseArgument<T> ToParseArgument<T>(this IPattern<T> pattern) =>
            result => ParseResult<T>(pattern, result);

        private static T ParseResult<T>(IPattern<T> pattern, ArgumentResult result)
        {
            // TODO: Check whether Tokens is actually what we want to use.
            if (result.Tokens.Count != 1)
            {
                result.ErrorMessage = "Only a single token can be parsed";
                return default;
            }
            var token = result.Tokens[0];
            var nodaResult = pattern.Parse(token.Value);
            if (nodaResult.Success)
            {
                return nodaResult.Value;
            }
            else
            {
                result.ErrorMessage = nodaResult.Exception.Message;
                return default;
            }
        }
    }
}

以及使用它的代码:

using NodaTime;
using NodaTime.Text;
using System;
using System.CommandLine;
using System.CommandLine.Invocation;
using Demo; // To make the extension method available

class Program
{
    static void Main(string[] args)
    {
        var rootCommand = new RootCommand
        {
            new Option<Duration>(
                "--my-duration",
                DurationPattern.JsonRoundtrip.ToParseArgument()),
        };

        rootCommand.Handler = CommandHandler.Create(
            (Duration myDuration) => Console.WriteLine(myDuration));

        rootCommand.Invoke(args);
    }
}

正在解析的模式 (DurationPattern.JsonRoundtrip) 使用“小时数”作为最重要的部分,而默认格式(在本例中由 Console.WriteLine 使用)使用“天数” , 所以很容易看出它确实在解析:

$ dotnet run -- --my-duration=27:23:45
1:03:23:45