воскресенье, 22 июля 2007 г.

Command Line Arguments Parser

UPDATE Feb.09
Выложил парсер командной строки, которым мы пользуемся, в Google Code:
http://code.google.com/p/commandlineargs/

Собственно, сам пост

Разбираясь с исходниками NUnit, наткнулся на любопытный способ разбора параметров командной строки.

Вообще-то, не могу понять, почему в .Net Framework не предусмотрели классов для парсинга командной строки. Банальный же массив string[] args, который передается в Main(), для серъезного дела вообще никак не подходит. Вот и пишет каждый разработчик себе такой класс самостоятельно, потому что дело вроде плевое и импортить что-то чужое не хочется. Я вот тоже писал, и не раз.
Что нужно хорошему парсеру аргументов командной строки? Несколько вещей, на мой взгляд:
- поддержка опции /help. То есть утилита должна напомнить пользователю, как ее правильно вызывать
- поддержка именованных параметров, вне зависимости от очередности их расположения в строке вызова. То есть util.exe /console /admin должен интерпретироваться так же, как и util.exe /admin /console
- грамотная валидация. То есть утилита должна объяснить пользователю, что он ввел не так
Так вот, в NUnit используется класс Codeblast.CommandLineOptions. В нем используется интересный подход, суть которого в следующем:

Разработчик command line утилиты создает наследник класса CommandLineOptions, в котором описывает требуемые параметры командной строки просто как properties. Такие properties он помечает специальным атрибутом Option("..."), указывая описание свойства, которое потом автоматически сгенерируется и покажется пользователю.

Авторы (Gert Lombard и James Newkirk) в коментариях к коду дают вот такое описание возможных параметров:


// I define 3 types of "options":
// 1. Boolean options (yes/no values), e.g: /r to recurse
// 2. Value options, e.g: /loglevel=3
// 2. Parameters: standalone strings like file names
//
// An example to explain:
// csc /nologo /t:exe myfile.cs
//
// + parameter
//
// + value option
//
// + boolean option


Вот так этот класс применяется в NUnit console runner. Посмотрите на диаграмму классов:



Пользоваться таким парсером очень просто:


ConsoleOptions options = new ConsoleOptions(args);

if(!options.nologo)
WriteCopyright();

if(options.help)
{
options.Help();
return 0;
}


Решение, на мой взгляд, простое и зрелое - проверяется безопасность типов (при парсинге args[] проверяется PropertyInfo требуемых свойств) и использующему параметры коду уже не нужно беспокоиться об парсинге строковых значений, как это бывает в не столь красивых парсерах. Плюс к тому - описывать набор принимаемых параметров через атрибуты очень удобно.

Во всем этом великолепии, на мой взгляд, не хватает всего пары вещей

  • Декларативного описания правил валидации. Ну например, через атрибут RequiredOption().
  • Множественных layout-ов, когда в зависимости от значения какого-нибудь параметра изменяются требования к наличию-отсутствию других параметров. Можно сделать, например, через дерево классов-наследников CommandLineOptions.
  • Неименованных параметров, чтобы можно было бы сделать так: util.exe /update Module1/src

Комментариев нет: