From d741a39fddb39d63fb6d5556003f4283231fdb32 Mon Sep 17 00:00:00 2001 From: Toshihiko SHIMOKAWA Date: Thu, 23 Jul 2026 00:54:29 +0900 Subject: [PATCH 1/4] feat: support :integer option type in CLI spec Widen option_def's type union and render a VALUE placeholder for any non-boolean option. Needed by ecosystem-manager's --max-concurrency; registry-manager's byte-identical help fixtures are unchanged. Resolves #12 --- lib/tool_kit/cli/spec.ex | 7 ++++--- test/tool_kit/cli/spec_test.exs | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/tool_kit/cli/spec.ex b/lib/tool_kit/cli/spec.ex index a36093a..6009258 100644 --- a/lib/tool_kit/cli/spec.ex +++ b/lib/tool_kit/cli/spec.ex @@ -21,7 +21,7 @@ defmodule ToolKit.CLI.Spec do defstruct [:tool_name, :tool_summary, :option_catalog, :global_option_names, :commands] @type option_def :: %{ - type: :boolean | :string, + type: :boolean | :string | :integer, alias: atom() | nil, values: [String.t()] | nil, doc: String.t() @@ -78,7 +78,7 @@ defmodule ToolKit.CLI.Spec do end @doc "OptionParser の strict リスト(全オプションの和集合)" - @spec strict_switches(t()) :: [{atom(), :boolean | :string}] + @spec strict_switches(t()) :: [{atom(), :boolean | :string | :integer}] def strict_switches(%__MODULE__{option_catalog: catalog}) do Enum.map(catalog, fn {name, %{type: type}} -> {name, type} end) end @@ -206,7 +206,8 @@ defmodule ToolKit.CLI.Spec do end defp render_option_line(option) do - value = if option.type == :string, do: " #{render_values(option)}", else: "" + # 値を取る型(string / integer)には VALUE プレースホルダを表示する + value = if option.type == :boolean, do: "", else: " #{render_values(option)}" if single_char_name?(option.name) do " -#{option.name}#{value} #{option.doc}" diff --git a/test/tool_kit/cli/spec_test.exs b/test/tool_kit/cli/spec_test.exs index 3c554c6..d81c9d9 100644 --- a/test/tool_kit/cli/spec_test.exs +++ b/test/tool_kit/cli/spec_test.exs @@ -45,6 +45,32 @@ defmodule ToolKit.CLI.SpecTest do end end + test "integer options derive strict switches and render a VALUE placeholder" do + spec = %Spec{ + tool_name: "demo", + tool_summary: "demo tool", + option_catalog: %{ + help: %{type: :boolean, alias: :h, values: nil, doc: "help"}, + jobs: %{type: :integer, alias: nil, values: nil, doc: "並列数"} + }, + global_option_names: [:help], + commands: [ + %{ + name: "run", + aliases: [], + usage: ["run"], + summary: "run", + options: [:jobs], + examples: ["run --jobs 4"] + } + ] + } + + assert {:jobs, :integer} in Spec.strict_switches(spec) + assert Spec.render_command_help(spec, "run") =~ "--jobs VALUE" + assert Spec.validate_opts(spec, "run", jobs: 4) == :ok + end + test "command option overrides replace values and doc", %{spec: spec} do list_command = Spec.find_command(spec, "list") sort = spec |> Spec.options_for(list_command) |> Enum.find(&(&1.name == :sort)) From 2b08f443820ae61fb2e9954e9b789da6d5ea5fbe Mon Sep 17 00:00:00 2001 From: Toshihiko SHIMOKAWA Date: Thu, 23 Jul 2026 00:56:22 +0900 Subject: [PATCH 2/4] test: cover rejection of non-integer values for :integer options Refs #12 --- test/tool_kit/cli/spec_test.exs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/tool_kit/cli/spec_test.exs b/test/tool_kit/cli/spec_test.exs index d81c9d9..c46a82f 100644 --- a/test/tool_kit/cli/spec_test.exs +++ b/test/tool_kit/cli/spec_test.exs @@ -69,6 +69,11 @@ defmodule ToolKit.CLI.SpecTest do assert {:jobs, :integer} in Spec.strict_switches(spec) assert Spec.render_command_help(spec, "run") =~ "--jobs VALUE" assert Spec.validate_opts(spec, "run", jobs: 4) == :ok + + # 整数でない値の拒否は OptionParser(strict)の責務で、 + # Parser がパース段階のエラーに変換する + assert {:error, message} = ToolKit.CLI.Parser.parse(spec, ["run", "--jobs", "four"]) + assert message =~ "--jobs" end test "command option overrides replace values and doc", %{spec: spec} do From 5629776990a6bf6ceb20fec08e43e9f0775ea388 Mon Sep 17 00:00:00 2001 From: Toshihiko SHIMOKAWA Date: Thu, 23 Jul 2026 08:54:27 +0900 Subject: [PATCH 3/4] chore: retrigger CI (no runs started for previous push) Refs #12 From 6adf83e332b57dfe2173aeb480f1df6327a11f2a Mon Sep 17 00:00:00 2001 From: Toshihiko SHIMOKAWA Date: Thu, 23 Jul 2026 08:56:54 +0900 Subject: [PATCH 4/4] style: alias ToolKit.CLI.Parser in spec test Refs #12 --- test/tool_kit/cli/spec_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/tool_kit/cli/spec_test.exs b/test/tool_kit/cli/spec_test.exs index c46a82f..458cdf4 100644 --- a/test/tool_kit/cli/spec_test.exs +++ b/test/tool_kit/cli/spec_test.exs @@ -1,6 +1,7 @@ defmodule ToolKit.CLI.SpecTest do use ExUnit.Case, async: true + alias ToolKit.CLI.Parser alias ToolKit.CLI.Spec alias ToolKit.Test.RegistryManagerSpecFixture, as: Fixture @@ -72,7 +73,7 @@ defmodule ToolKit.CLI.SpecTest do # 整数でない値の拒否は OptionParser(strict)の責務で、 # Parser がパース段階のエラーに変換する - assert {:error, message} = ToolKit.CLI.Parser.parse(spec, ["run", "--jobs", "four"]) + assert {:error, message} = Parser.parse(spec, ["run", "--jobs", "four"]) assert message =~ "--jobs" end