Using F# 2.0 Powerpack ArgParser from C# -- (ii)
Having resolved the issue of why the "--" separator wasn't triggering (PowerShell was swallowing it before it got to my code) that was stumping me in the previous post, I put together a little fluent wrapper to make this a little easier to consume from C#. The code that isolates the F# types looks like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Tinesware.ArgParser | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using Microsoft.FSharp.Core; | |
using Microsoft.FSharp.Text; | |
/// <summary> | |
/// Assembles the various command line options and then calls the parser | |
/// </summary> | |
public class Options | |
{ | |
/// <summary> | |
/// Option values | |
/// </summary> | |
private List<ArgInfo> options = new List<ArgInfo>(); | |
/// <summary> | |
/// Callbacks for boolean values | |
/// </summary> | |
private List<Flag> flags = new List<Flag>(); | |
/// <summary> | |
/// Initializes a new instance of the Options class. | |
/// </summary> | |
public Options() | |
{ | |
} | |
/// <summary> | |
/// An option with no argument | |
/// </summary> | |
/// <param name="option">The option string</param> | |
/// <param name="callback">The callback for this option</param> | |
/// <param name="usage">Usage comment</param> | |
/// <returns>This object (fluent interface)</returns> | |
public Options Unit(string option, Action callback, string usage) | |
{ | |
var type = ArgType.Unit(FuncConvert.ToFSharpFunc<Unit>(x => callback())); | |
this.options.Add(new ArgInfo(option, type, usage)); | |
return this; | |
} | |
/// <summary> | |
/// An option with a string argument | |
/// </summary> | |
/// <param name="option">The option string</param> | |
/// <param name="callback">The callback for this option</param> | |
/// <param name="usage">Usage comment</param> | |
/// <returns>This object (fluent interface)</returns> | |
public Options String(string option, Action<string> callback, string usage) | |
{ | |
var type = ArgType.String(FuncConvert.ToFSharpFunc<string>(callback)); | |
this.options.Add(new ArgInfo(option, type, usage)); | |
return this; | |
} | |
/// <summary> | |
/// An option with an integer argument | |
/// </summary> | |
/// <param name="option">The option string</param> | |
/// <param name="callback">The callback for this option</param> | |
/// <param name="usage">Usage comment</param> | |
/// <returns>This object (fluent interface)</returns> | |
public Options Int(string option, Action<int> callback, string usage) | |
{ | |
var type = ArgType.Int(FuncConvert.ToFSharpFunc<int>(callback)); | |
this.options.Add(new ArgInfo(option, type, usage)); | |
return this; | |
} | |
/// <summary> | |
/// An option with a double precision argument | |
/// </summary> | |
/// <param name="option">The option string</param> | |
/// <param name="callback">The callback for this option</param> | |
/// <param name="usage">Usage comment</param> | |
/// <returns>This object (fluent interface)</returns> | |
public Options Float(string option, Action<double> callback, string usage) | |
{ | |
var type = ArgType.Float(FuncConvert.ToFSharpFunc<double>(callback)); | |
this.options.Add(new ArgInfo(option, type, usage)); | |
return this; | |
} | |
/// <summary> | |
/// An end-of-arguments argument, usually "--" | |
/// </summary> | |
/// <param name="option">The option string, usually "--"</param> | |
/// <param name="callback">The callback for the remaining arguments</param> | |
/// <param name="usage">Usage comment</param> | |
/// <returns>This object (fluent interface)</returns> | |
public Options Rest(string option, Action<string> callback, string usage) | |
{ | |
var type = ArgType.Rest(FuncConvert.ToFSharpFunc<string>(callback)); | |
this.options.Add(new ArgInfo(option, type, usage)); | |
return this; | |
} | |
/// <summary> | |
/// An argument indicating true | |
/// </summary> | |
/// <param name="option">The option string</param> | |
/// <param name="callback">The callback for the remaining arguments</param> | |
/// <param name="usage">Usage comment</param> | |
/// <returns>This object (fluent interface)</returns> | |
public Options Set(string option, Action<bool> callback, string usage) | |
{ | |
var flag = new Flag | |
{ | |
Reference = new FSharpRef<bool>(false), | |
Callback = callback | |
}; | |
var type = ArgType.Set(flag.Reference); | |
this.options.Add(new ArgInfo(option, type, usage)); | |
this.flags.Add(flag); | |
return this; | |
} | |
/// <summary> | |
/// An argument indicating false | |
/// </summary> | |
/// <param name="option">The option string</param> | |
/// <param name="callback">The callback for the remaining arguments</param> | |
/// <param name="usage">Usage comment</param> | |
/// <returns>This object (fluent interface)</returns> | |
public Options Clear(string option, Action<bool> callback, string usage) | |
{ | |
var flag = new Flag | |
{ | |
Reference = new FSharpRef<bool>(true), | |
Callback = callback | |
}; | |
var type = ArgType.Set(flag.Reference); | |
this.options.Add(new ArgInfo(option, type, usage)); | |
this.flags.Add(flag); | |
return this; | |
} | |
/// <summary> | |
/// Parse the environment command line with no default handler or usage comment | |
/// </summary> | |
public void Parse() | |
{ | |
this.Parse(null, null); | |
} | |
/// <summary> | |
/// Parse the environment command line with no default handler | |
/// </summary> | |
/// <param name="usage">usage comment</param> | |
public void Parse(string usage) | |
{ | |
this.Parse(null, usage); | |
} | |
/// <summary> | |
/// Parse the environment command line with no usage comment | |
/// </summary> | |
/// <param name="defaultHandler">default handler for unmatched arguments</param> | |
public void Parse(Action<string> defaultHandler) | |
{ | |
this.Parse(defaultHandler, null); | |
} | |
/// <summary> | |
/// Parse the environment command line | |
/// </summary> | |
/// <param name="defaultHandler">default handler for unmatched arguments</param> | |
/// <param name="usage">usage comment</param> | |
public void Parse(Action<string> defaultHandler, string usage) | |
{ | |
var temp = usage ?? string.Empty; | |
var theUsage = string.IsNullOrEmpty(temp.Trim()) ? | |
FSharpOption<string>.None : | |
new FSharpOption<string>(usage); | |
var theDefault = defaultHandler == null ? | |
FSharpOption<FSharpFunc<string, Unit>>.None : | |
new FSharpOption<FSharpFunc<string, Unit>>( | |
FuncConvert.ToFSharpFunc<string>(defaultHandler)); | |
ArgParser.Parse(this.options, theDefault, theUsage); | |
this.flags.ForEach(x => x.Callback(x.Reference.Value)); | |
} | |
/// <summary> | |
/// Report the command usage | |
/// </summary> | |
public void Usage() | |
{ | |
this.Usage(null); | |
} | |
/// <summary> | |
/// Report the command usage | |
/// </summary> | |
/// <param name="remark">Initial comment</param> | |
public void Usage(string remark) | |
{ | |
var temp = remark ?? string.Empty; | |
var theUsage = string.IsNullOrEmpty(temp.Trim()) ? | |
FSharpOption<string>.None : | |
new FSharpOption<string>(remark); | |
ArgParser.Usage(this.options, theUsage); | |
} | |
/// <summary> | |
/// Binds a ref bool and a callback | |
/// </summary> | |
private struct Flag | |
{ | |
/// <summary> | |
/// A reference cell with a boolean value | |
/// </summary> | |
public FSharpRef<bool> Reference; | |
/// <summary> | |
/// A post-parse callback | |
/// </summary> | |
public Action<bool> Callback; | |
} | |
} | |
} |
The calling code, for the same example as before looks like
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace fsgetopt | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Action<string> compile = (s => Console.WriteLine("Compiling {0}...", s)); | |
var outputName = "a.out"; | |
var verbose = false; | |
var warningLevel = 0; | |
var rest = new List<string>(); | |
var options = new Tinesware.ArgParser.Options(). | |
String("-o", | |
x => outputName = x, | |
"Name of the output"). | |
Set("-v", | |
x => verbose = x, | |
"Display additional information"). | |
Int("--warn", | |
x => warningLevel = x, | |
"Set warning level"). | |
Rest("--", | |
x => rest.Add(x), | |
"Stop parsing command line"); | |
options.Parse(compile, "Usage options are:"); | |
Console.WriteLine("outputName = {0}", outputName); | |
Console.WriteLine("Verbose = {0}", verbose.Value); | |
Console.WriteLine("Warning level = {0}", warningLevel); | |
Console.WriteLine("rest = {0}", rest.Count); | |
} | |
} | |
} |
which is much clearer.
Now edited for FxCop.
No comments :
Post a Comment