Skip to content

Commit 1a95314

Browse files
author
Your Name
committed
Add smart install dry-run parser
1 parent 6f61ef2 commit 1a95314

1 file changed

Lines changed: 71 additions & 4 deletions

File tree

opsm_ex/lib/opsm/cli.ex

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ defmodule Opsm.CLI do
4444
json: :boolean,
4545
limit: :integer,
4646
native: :boolean,
47-
dev: :boolean
47+
dev: :boolean,
48+
apply: :boolean
4849
],
4950
aliases: [
5051
h: :help,
@@ -69,9 +70,7 @@ defmodule Opsm.CLI do
6970
["repolist" | _] -> {:repolist, opts}
7071

7172
# Install/Remove
72-
["install", "@" <> forth, package | _] -> {:install, forth, package, opts}
73-
["install", package | _] -> {:install, nil, package, opts}
74-
["install"] -> {:error, "install requires a package argument"}
73+
["install" | rest] -> parse_install_args(rest, opts)
7574

7675
["remove", package | _] -> {:remove, package, opts}
7776
["uninstall", package | _] -> {:remove, package, opts}
@@ -143,6 +142,22 @@ defmodule Opsm.CLI do
143142
end
144143
end
145144

145+
defp parse_install_args([], _opts), do: {:error, "install requires a package argument"}
146+
147+
defp parse_install_args(["@" <> forth, package | _], opts) do
148+
{:install, forth, package, opts}
149+
end
150+
151+
defp parse_install_args([package], opts) do
152+
if String.contains?(package, ":") or String.contains?(package, "[") or String.contains?(package, "]") do
153+
{:smart_install, [package], opts}
154+
else
155+
{:install, nil, package, opts}
156+
end
157+
end
158+
159+
defp parse_install_args(rest, opts), do: {:smart_install, rest, opts}
160+
146161
defp run({:help, _opts}) do
147162
IO.puts("""
148163
opsm - Odds-and-sods Package Manager
@@ -154,6 +169,7 @@ defmodule Opsm.CLI do
154169
155170
PACKAGE COMMANDS:
156171
install [@forth] <pkg> Install package (optionally from specific registry)
172+
install [backend:] ... Smart install with backend grouping
157173
remove <package> Remove an installed package
158174
reinstall <package> Reinstall a package
159175
update [packages...] Update packages (all if none specified)
@@ -198,6 +214,7 @@ defmodule Opsm.CLI do
198214
--user Install for current user (default)
199215
-g, --global Global install (native mode)
200216
-D, --dev Developsment dependency
217+
--apply Apply smart install plan (default is dry-run)
201218
--native Use native toolchain (npm, cargo, mix, pip)
202219
-y, --yes Assume yes to prompts
203220
-q, --quiet Minimal output
@@ -233,6 +250,7 @@ defmodule Opsm.CLI do
233250
EXAMPLES:
234251
opsm install lodash # From default/detected registry
235252
opsm install @npm lodash --version 4.17 # Specific version from npm
253+
opsm install rpm-ostree: gcc clang # Smart install grouping
236254
opsm install @cargo tokio --allow rc # Release candidate
237255
opsm install @hex phoenix --systemwide # System-wide
238256
opsm install @dnf htop # Via system package manager
@@ -325,6 +343,17 @@ defmodule Opsm.CLI do
325343
end
326344
end
327345

346+
defp run({:smart_install, rest, opts}) do
347+
plan = parse_smart_install(rest)
348+
print_smart_plan(plan)
349+
350+
if opts[:apply] do
351+
IO.puts("Apply is not wired yet. This is a dry-run plan only.")
352+
else
353+
IO.puts("Dry-run only. Use --apply to execute when available.")
354+
end
355+
end
356+
328357
defp run({:remove, package, opts}) do
329358
alias Opsm.Package.Installer
330359

@@ -1035,4 +1064,42 @@ defmodule Opsm.CLI do
10351064
IO.puts(" opsm install @cargo #{package}")
10361065
System.halt(0)
10371066
end
1067+
1068+
defp parse_smart_install(tokens) do
1069+
cleaned =
1070+
tokens
1071+
|> Enum.map(&String.trim/1)
1072+
|> Enum.map(&String.trim_leading(&1, "["))
1073+
|> Enum.map(&String.trim_trailing(&1, "]"))
1074+
1075+
backends = MapSet.new(["rpm-ostree", "toolbox", "distrobox", "container", "native", "git", "source", "auto"])
1076+
1077+
Enum.reduce(cleaned, %{current: "auto", plan: %{}}, fn token, acc ->
1078+
case String.split(token, ":", parts: 2) do
1079+
[backend, ""] ->
1080+
backend = String.downcase(backend)
1081+
backend = if MapSet.member?(backends, backend), do: backend, else: "auto"
1082+
%{acc | current: backend}
1083+
1084+
[backend, rest] when rest != "" ->
1085+
backend = String.downcase(backend)
1086+
backend = if MapSet.member?(backends, backend), do: backend, else: "auto"
1087+
pkg = String.trim(rest)
1088+
plan = Map.update(acc.plan, backend, [pkg], fn pkgs -> pkgs ++ [pkg] end)
1089+
%{acc | current: backend, plan: plan}
1090+
1091+
[pkg] ->
1092+
plan = Map.update(acc.plan, acc.current, [pkg], fn pkgs -> pkgs ++ [pkg] end)
1093+
%{acc | plan: plan}
1094+
end
1095+
end)
1096+
|> Map.get(:plan)
1097+
end
1098+
1099+
defp print_smart_plan(plan) do
1100+
IO.puts("Smart install plan (grouped by backend):")
1101+
Enum.each(plan, fn {backend, pkgs} ->
1102+
IO.puts(" - #{backend}: #{Enum.join(pkgs, \", \")}")
1103+
end)
1104+
end
10381105
end

0 commit comments

Comments
 (0)