Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

T4 generated strongly typed docopt return object #2

Closed
voieducode opened this issue Jul 20, 2013 · 4 comments
Closed

T4 generated strongly typed docopt return object #2

voieducode opened this issue Jul 20, 2013 · 4 comments
Assignees
Milestone

Comments

@voieducode
Copy link
Member

As an alternative to the return dictionary we should use a T4 template to generate a strongly typed return object for each usage text defined in the project.

  • T4 template called docopt.tt
  • Usage strings should be defined in files called *.usage.txt
  • For each usage file X.usage.txt, when applied the T4 template will create a new C# file called X.docopt.cs defining a class called XArgs with a strongly typed property for each option or argument.
@ghost ghost assigned voieducode Jul 20, 2013
@keleshev
Copy link
Member

Sounds interesting, looking forward to see how it works out! You might be the first one to implement code generation for docopt.

I don't know this T4 stuff :-), but I would guess that usually you need a single usage.txt per project, no?

@voieducode
Copy link
Member Author

T4 is a design time code generator integrated in Visual Studio. It's a little bit ugly but it's really powerful.

You're right: in 90% of the use cases there will be a single usage.txt. However looking at the git example, it sounds like there could be multiple usage.txt if the implementor chooses to group all the command line parsing in one project.

@voieducode
Copy link
Member Author

So I managed to put together a working version of the code generator. It was much easier than I expected. Sample generated code here.

Design

I have introduced a Docopt.GenerateCode() method that parses out the doc string and scans through the resulting flattened list of LeafPattern objects to generate code. Basically the code generation is hardcoded in the GenerateCode() method implementation in the LeafPattern subclasses. Argument instances produce "ArgXXX" fields. Command instances produce "CmdXXX" fields. Option instances produce "OptXXX" fields.

The original arguments dictionary is still available through an Args property. In fact the generated code is just a wrapper around this dictionary.

In a second iteration, I will move the code generation code out of the core docopt implementation as I don't feel it really belong there. It should be in the T4 module.

Worked example

Main.usage.txt

Test.
Usage: prog command ARG FILES... [-o --switch --long=ARG]

Generated code: T4DocoptNet.cs

// Generated class for Main.usage.txt
public class MainArgs
{
    public const string USAGE = @"Test.

Usage: prog command ARG FILES... [-o --switch --long=ARG]
";
    private readonly IDictionary<string, ValueObject> _args;
    public MainArgs(ICollection<string> argv, bool help = true,
                                                  object version = null, bool optionsFirst = false, bool exit = false)
    {
        _args = new Docopt().Apply(USAGE, argv, help, version, optionsFirst, exit);
    }

    public IDictionary<string, ValueObject> Args
    {
        get { return _args; }
    }

    public bool CmdCommand { get { return _args["command"].IsTrue; } }
    public string ArgArg { get { return _args["ARG"].ToString(); } }
    public bool OptO { get { return _args["-o"].IsTrue; } }
    public string OptLong { get { return _args["--long"].ToString(); } }
    public bool OptSwitch { get { return _args["--switch"].IsTrue; } }
    public ArrayList ArgFiles { get { return _args["FILES"].AsList; } }

}

Using the generated code

class Program
{

   static void DoStuff(string arg, bool flagO, string longValue)
   {
     // ...
   }

    static void Main(string[] args)
    {
        var args = new MainArgs(args);
        if (args.CmdCommand)
        {
            Console.WriteLine("First command");
            DoStuff(args.ArgArg, args.OptO, args.OptLong);
        }
    }
}

Planned improvements

  • Add unit tests
  • Move the code generation logic out of the Docopt module.
  • Enable using embedded resources to avoid repeating the usage string in the generated class.
  • Allow custom specification of int options i.e. -vvv
  • Add the T4 files to the nuget package.

@keleshev
Copy link
Member

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants