Поэтому я заметил отсутствие nuget outdated или dotnet nuget outdated, которые, как можно было бы ожидать, будут действовать аналогично npm outdated.

Итак, я подумал, что пора попробовать, и теперь я полностью по умолчанию использую F# для написания своих утилит и приложений.

Существует этот API командной строки, который Microsoft, похоже, использует для своего CLI dotnet, и я действительно узнал об этом, потому что я наткнулся на него, читая их исходный код (чтобы увидеть, где эта новая команда может вписаться!)

Его легко использовать. Запустите dotnet new console -lang f# -o mycmdline -n MyCmdLine или любое другое название проекта, которое вы хотите использовать, затем выполните dotnet add package System.CommandLine --prerelease, и все будет готово.

Прямо из коробки, когда вы делаете dotnet build или dotnet watch build, а затем делаете MyCmdLine.exe, вы получаете это из коробки

Что очень круто — игнорировать параметры кеша, это то, что я вставил.

Итак, теперь мы можем создать то, что называется корневым обработчиком:

Итак, чтобы разбить это, поскольку мы используем .net 6 и минимальный API, нам нужно получить аргументы командной строки из статического класса Environment.

Команда root означает, что без каких-либо аргументов она работает так

Теперь мы можем добавить несколько подкоманд и опций 😃

Здесь у нас есть опция verbose, она действует как переключатель. Поэтому, когда вы запустите его с -h, вы будете проинформированы

Нет переключателя

С переключателем

Довольно просто, ау!

Подкоманды

Я накручиваю интенсивность здесь, так что голая. Команды иерархичны, поэтому вы строите их как таковые.

Итак, если вы хотите получить индекс службы nuget таким образом, что вам нужен MyCmdLine get serviceIndex, давайте начнем с команды serviceIndex.

Так что игнорируйте cache и EasyClient, это просто небольшие абстракции, которые я сделал, EasyClient — это просто HttpClient, основанный на nuget, а cache просто хранит любые HttpResponse, поэтому я не заливаю серверы nuget.

Суть еще в том, что fetchData теперь является функцией handle с сигнатурой Func<Task>let fetchData () : Task делает эту работу, я обнаружил, что без нее Task<unit> просто не компилируется.

Легкие дни.

Теперь нам нужно создать команду get (родитель serviceIndex )

Итак, поскольку эта команда может принимать несколько подкоманд, я получил commands: Command list, а commands |> List.iter (cmd.AddCommand) добавляет каждую команду в родительский элемент.

Теперь мы можем связать это с командой root.

Здесь я абстрагировался от корневого обработчика. Я также создал этот getCmd, который просто возвращает родительскую команду GetCommand из более ранней и добавляется через cmd.AddCommand(getCmd ())

Бег с -h

Бег с get -h

И вот… easy peasy — ваша очень мощная полнофункциональная программа командной строки…

Мне это нравится, я совершенно поражен профессионализмом, который дает коробку и гибкость, чтобы разбить ее из парка!

Предостережения

Как и во всем, есть некоторые предостережения

  1. Единственный обработчик, который запускается, когда вы выполняете команду, — это обработчик этой команды — это не промежуточное ПО, где каждый обработчик запускается по порядку…
  2. Внедрения зависимостей нет, но это потому, что нет открытого запущенного процесса, что меня и привлекло в первый раз. Кэш изначально был в памяти, но забыли, что каждый запуск — это новый запуск процесса… поэтому любое хранение материала должно выполняться снаружи (я только что создал файл .cache.json…) (Сказав это, если вы хотите сохранить в памяти параметры конфигурации, тогда вы можете создать одноэлементную зависимость, которая может быть маршрутизирована через частичное приложение или монаду чтения)
  3. Параметры можно использовать только в обработчике, к которому они подключены, поэтому, если вы хотите --namespace charlie get serviceIndex и прикрепите Option<string>("namespace", "Scope the subsequent commands") к корневому обработчику, это не сработает. Поэтому вам лучше абстрагироваться от логики, которая обрабатывает пространство имен, и иметь этот «базовый» параметр, прикрепленный к каждой команде, где вы хотите его использовать. Таким образом, вы могли бы по существу создать базовый обработчик, который обрабатывает всю конфигурацию, который затем принимает фактический обработчик в качестве параметра (о котором я только что подумал, когда писал это, и звучит круто)

Сходить с ума!