F# Console Application Template
If you are like me and often use console applications for a variety of purposes you would have found the F# template not much use (in fact a blank code file). As such I decided to put together a more complete Project Template that I could use.
The template can be found on the Visual Studio Gallery:
https://visualstudiogallery.msdn.microsoft.com/031891a9-06c3-47db-9c7d-8c9d4a32546a
When the template is installed you get the following template added to your F# folder when creating a new F# project:
The Project Template creates a Windows Console Application with the following components:
- Program.fs: The application entry point defined
- Arguments.fs: A simple command line parser placing all arguments into a dictionary
- MyConsole.fs: A separate module for the custom console application code
The application entry point is quite simple:
module Program =
[<EntryPoint>]
let Main(args) =
MyConsole.Run args
// main entry point return
0
The custom console application handles the argument parsing, in addition to providing a TODO for the custom code:
module MyConsole =
let Run args =
// Define what arguments are expected
let defs = [
{ArgInfo.Command="a1"; Description="Argument 1"; Required=true };
{ArgInfo.Command="a2"; Description="Argument 2"; Required=false } ]
// Parse Arguments into a Dictionary
let parsedArgs = Arguments.ParseArgs args defs
// TODO add your code here
Arguments.DisplayArgs parsedArgs
Console.ReadLine() |> ignore
If one does not wish to use the argument parsing module the ArgInfo list can be removed. This list provides a simple means for ensuring that the start-up arguments are as expected. The returned parsed parameters is merely a Dictionary of the found arguments.
If one so desires one could map this over to the argument parsing provided by the FSharp PowerPack.
For completeness here is the argument parsing code:
module Arguments =
// Type for simple argument checking
type ArgInfo = { Command:string; Description:string; Required:bool }
// Displays the help arguments
let DisplayHelp (defs:ArgInfo list) =
match defs with
| [] -> Console.WriteLine "No help text defined."
| _ ->
Console.WriteLine "Command Arguments:"
defs
|> List.iter (fun def ->
let helpText = sprintf "-%s (Required=%b) : %s" def.Command def.Required def.Description
Console.WriteLine helpText )
// Displays the found arguments
let DisplayArgs (args:Dictionary<string, string>) =
match args.Keys.Count with
| 0 -> Console.WriteLine "No arguments found."
| _ ->
Console.WriteLine "Arguments Found:"
for arg in args.Keys do
if String.IsNullOrEmpty(args.[arg]) then
Console.WriteLine (sprintf "-%s" arg)
else
Console.WriteLine (sprintf "-%s '%s'" arg args.[arg])
// Parse the input arguments
let ParseArgs (args:string array) (defs:ArgInfo list) =
let parsedArgs = new Dictionary<string, string>()
// Ensure help is supported if defintions provided
let fullDefs =
if not (List.exists (fun def -> String.Equals(def.Command, "help")) defs) then
{ArgInfo.Command="help"; Description="Display Help Text"; Required=false } :: defs
else
defs
// Report errors
let reportError errorText =
DisplayArgs parsedArgs
DisplayHelp fullDefs
let errMessage = sprintf "Error occured: %A" errorText
Console.Error.WriteLine errMessage
Console.Error.Flush()
Environment.Exit(1)
// Capture variables
let captureArg command value =
match defs with
| [] -> parsedArgs.Add(command, value)
| _ ->
if not (List.exists (fun def -> String.Equals(def.Command, command)) fullDefs) then
reportError (sprintf "Command '%s' Not in definition list." command)
else
parsedArgs.Add(command, value)
let (|IsCommand|_|) (command:string) =
let m = Regex.Match(command, "^(?:-{1,2}|\/)(?<command>.*)$", RegexOptions.IgnoreCase)
if m.Success then Some(m.Groups.["command"].Value.ToLower()) else None
let rec loop (argList:string list) =
match argList with
| [] -> ()
| head::tail ->
match head with
| IsCommand command ->
match tail with
| [] -> captureArg command String.Empty
| iHead::iTail ->
match iHead with
| IsCommand iCommand ->
captureArg command String.Empty
loop tail
| _ ->
captureArg command iHead
loop iTail
| _ -> reportError (sprintf "Expected a command but got '%s'" head)
loop (Array.toList args)
// Look to see if help has been requested if not check for required
if (parsedArgs.ContainsKey("help")) then
DisplayHelp defs
else
defs
|> List.filter (fun def -> def.Required)
|> List.iter ( fun def ->
if not (parsedArgs.ContainsKey(def.Command)) then
reportError (sprintf "Command '%s' found but in argument list." def.Command))
parsedArgs
As always, hopefully you will find this template useful.