より良いエンジニアを目指して

1日1つ。良くなる!上手くなる!

CommandLineParserを使ってみて

C# にはコマンドラインをパースするライブラリがあります。

github.com

コマンドラインパーサーなんて自分でも作れなくもないですが、

既存のライブラリがあるなら一から作ることもないので下記サイトを参考に試してみました。

qiita.com  

プロジェクトファイルのファイル追加漏れを防ぐコンソールアプリを作ってみようというときに使ってみたのですが、私がやりたかったことは……  

  • どうしてもプロセスの結果として返して失敗したか成功したかを判断できるようにしたいので、返り値に0かそうでないかを返す。
  • さすがに非同期で処理するまでもないかなと、async伝播させたくない。

ということで、下記みたいな形で私は実装してみました。

public class ProjectAssistantParser
{
    public static int Parse(string[] args)
    {
        using (var parser = new Parser(setting => setting.HelpWriter = null))
        {
            var parsed = parser.ParseArguments<CommandLineArgument>(args);
            bool isSuccess = true;
            parsed.WithNotParsed(er =>
            {
                    var helpText = HelpText.AutoBuild(parsed);
                    Console.WriteLine($"parse failed: {helpText}");
                    isSuccess = false;
            });
            parsed.WithParsed(
                p => { ProjectAssistantService.TryValidate(p.ProjectFileName, p.ChildDirectoryName); });

                return (isSuccess ? 0 : -1);
            }
    }

    /// <summary>
    /// コマンドライン引数を扱うクラスです。
    /// </summary>
    class CommandLineArgument
    {
        [Value(0, MetaName = "projectFileName", HelpText = "更新対象のプロジェクトファイル名",Required = true)]
        public string ProjectFileName { get; set; }

        [Value(1, MetaName = "ChildDirectoryName", HelpText = "ファイル走査対象とするサブディレクトリのフォルダ相対パス",Required = true)]
        public string ChildDirectoryName { get; set; }
    }
}

とりあえず、狙い通りにできたので良さそう。

テストコードとしても

        /// <summary>
        /// <see cref="ProjectAssistantParser.Parse"/>をテストします。
        /// </summary>
        /// <returns></returns>
        [TestCaseSource(nameof(ParseTestCaseSource))]
        public int Parse(IList<string> args)
        {
            return ProjectAssistantParser.Parse(args.ToArray());
        }

        /// <summary>
        /// <see cref="Parse"/>のテストケースです。
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<TestCaseData> ParseTestCaseSource()
        {
            string projectFileName = "hoge";
            string subDirectoryPath = "test";

            yield return new TestCaseData(new List<string> {projectFileName}).Returns(-1)
                .SetName($"{nameof(Parse)} 引数が足りない");
            yield return new TestCaseData(new List<string> {projectFileName, subDirectoryPath}).Returns(0)
                .SetName($"{nameof(Parse)} 正常系");
        }

とテスタブルに実装できますね。