Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
JordanMarr committed Mar 13, 2022
1 parent c7d6127 commit b9fef8d
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 0 deletions.
54 changes: 54 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
obj
bin
*.bak
.vs
.fake
.ionide
node_modules
deploy
packages
paket-files
nupkg
*.dll
*UCASE.txt

# JetBrains Raider - - - - - -

# User specific
**/.idea/**/workspace.xml
**/.idea/**/tasks.xml
**/.idea/shelf/*
**/.idea/dictionaries
.idea

# Sensitive or high-churn files
**/.idea/**/dataSources/
**/.idea/**/dataSources.ids
**/.idea/**/dataSources.xml
**/.idea/**/dataSources.local.xml
**/.idea/**/sqlDataSources.xml
**/.idea/**/dynamic.xml

# Rider
# Rider auto-generates .iml files, and contentModel.xml
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml

*.suo
*.user
.vs/
[Bb]in/
[Oo]bj/
_UpgradeReport_Files/
[Pp]ackages/

Thumbs.db
Desktop.ini
.DS_Store

*.refactorlog
*.dbmdl
*.jfm
/src/Tests/TestData/AdventureWorksLT_Temp.db
**/Verify/*.received.*
25 changes: 25 additions & 0 deletions src/FSharp.SystemCommandLine.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32126.317
MinimumVisualStudioVersion = 10.0.40219.1
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.SystemCommandLine", "FSharp.SystemCommandLine\FSharp.SystemCommandLine.fsproj", "{209D39F4-8C8B-4919-9515-1A9905EF6398}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{209D39F4-8C8B-4919-9515-1A9905EF6398}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{209D39F4-8C8B-4919-9515-1A9905EF6398}.Debug|Any CPU.Build.0 = Debug|Any CPU
{209D39F4-8C8B-4919-9515-1A9905EF6398}.Release|Any CPU.ActiveCfg = Release|Any CPU
{209D39F4-8C8B-4919-9515-1A9905EF6398}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {14A511E8-56FF-4D50-9F1C-494D09EB96DC}
EndGlobalSection
EndGlobal
180 changes: 180 additions & 0 deletions src/FSharp.SystemCommandLine/CommandBuilders.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
module FSharp.SystemCommandLine.CommandBuilders

open System
open System.Threading.Tasks
open System.CommandLine
open System.CommandLine.Binding

let private def<'T> = Unchecked.defaultof<'T>
exception MaxArgumentsExceeded

type CommandSpec<'Args, 'Result> =
{
Description: string
Options: System.CommandLine.Option list
Handler: 'Args -> 'Result
SubCommands: System.CommandLine.Command list
}
static member Default =
{
Description = "My Command"
Options = []
Handler = def<unit -> 'Result> // Support unit -> 'Result handler by default
SubCommands = []
}

/// Contains shared operations for building a `RootCommand` or `Command`.
type BaseCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, 'Result>() =

let newHandler handler spec =
{
Description = spec.Description
Options = spec.Options
Handler = handler
SubCommands = spec.SubCommands
}

member this.Yield _ =
// Set default of `unit * obj * obj` so that CE fails if no `options` are set
CommandSpec<unit * obj * obj *obj, 'Result>.Default

// Prevents errors while typing join statement if rest of query is not filled in yet.
member this.Zero _ =
CommandSpec<_, _>.Default

[<CustomOperation("description")>]
member this.Description (spec: CommandSpec<'T, 'U>, description) =
{ spec with Description = description }

[<CustomOperation("options")>]
member this.Options (spec: CommandSpec<'T, 'Result>, a: Opt<'A>) =
{ newHandler def<'A -> 'Result> spec with Options = [ a ] }

[<CustomOperation("options")>]
member this.Options (spec: CommandSpec<'T, 'Result>, (a: Opt<'A>, b: Opt<'B>)) =
{ newHandler def<'A * 'B -> 'Result> spec with Options = [ a; b ] }

[<CustomOperation("options")>]
member this.Options (spec: CommandSpec<'T, 'Result>, (a: Opt<'A>, b: Opt<'B>, c: Opt<'C>)) =
{ newHandler def<'A * 'B * 'C -> 'Result> spec with Options = [ a; b; c ] }

[<CustomOperation("setHandler")>]
member this.SetHandler (spec: CommandSpec<'Args, 'Return>, handler: 'Args -> 'Return) =
newHandler handler spec

[<CustomOperation("setCommand")>]
member this.SetHandler (spec: CommandSpec<'Args, 'Return>, subCommand: System.CommandLine.Command) =
{ spec with SubCommands = spec.SubCommands @ [ subCommand ] }


/// Builds a `System.CommandLine.RootCommand`.
type RootCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, 'Result>() =
inherit BaseCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, 'Result>()

let initRootCmd (spec: CommandSpec<'T, 'U>) =
let cmd = RootCommand()
cmd.Description <- spec.Description
spec.Options |> List.iter cmd.AddOption
spec.SubCommands |> List.iter cmd.AddCommand
cmd

/// Executes a command that returns unit.
member this.Run (spec: CommandSpec<'Args, unit>) =
let cmd = initRootCmd spec
let opts = spec.Options |> Seq.cast<IValueDescriptor> |> Seq.toArray
let handler (args: obj) = spec.Handler (args :?> 'Args)

match spec.Options.Length with
| 0 -> cmd.SetHandler(Action(fun () -> handler ()))
| 1 -> cmd.SetHandler(Action<'A>(fun a -> handler (a)), opts)
| 2 -> cmd.SetHandler(Action<'A, 'B>(fun a b -> handler (a, b)), opts)
| 3 -> cmd.SetHandler(Action<'A, 'B, 'C>(fun a b c -> handler (a, b, c)), opts)
| 4 -> cmd.SetHandler(Action<'A, 'B, 'C, 'D>(fun a b c d -> handler (a, b, c, d)), opts)
| _ -> raise MaxArgumentsExceeded

let args = Environment.GetCommandLineArgs()
cmd.Invoke args

/// Executes a command that returns a Task.
member this.Run (spec: CommandSpec<'Args, Task<unit>>) =
let cmd = initRootCmd spec
let opts = spec.Options |> Seq.cast<IValueDescriptor> |> Seq.toArray
let handler (args: obj) =
task {
do! spec.Handler (args :?> 'Args)
}

match spec.Options.Length with
| 1 -> cmd.SetHandler(Func<'A, Task>(fun a -> handler (a)), opts)
| 2 -> cmd.SetHandler(Func<'A, 'B, Task>(fun a b -> handler (a, b)), opts)
| 3 -> cmd.SetHandler(Func<'A, 'B, 'C, Task>(fun a b c -> handler (a, b, c)), opts)
| 4 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, Task>(fun a b c d -> handler (a, b, c, d)), opts)
| _ -> raise MaxArgumentsExceeded

let args = Environment.GetCommandLineArgs()
cmd.InvokeAsync args


/// Builds a `System.CommandLine.Command`.
type CommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, 'Result>(name: string) =
inherit BaseCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, 'Result>()

let initRootCmd (spec: CommandSpec<'T, 'U>) =
let cmd = Command(name)
cmd.Description <- spec.Description
spec.Options |> List.iter cmd.AddOption
spec.SubCommands |> List.iter cmd.AddCommand
cmd

/// Executes a command that returns unit.
member this.Run (spec: CommandSpec<'Args, unit>) =
let cmd = initRootCmd spec
let opts = spec.Options |> Seq.cast<IValueDescriptor> |> Seq.toArray
let handler (args: obj) = spec.Handler (args :?> 'Args)

match spec.Options.Length with
| 0 -> cmd.SetHandler(Action(fun () -> handler ()))
| 1 -> cmd.SetHandler(Action<'A>(fun a -> handler (a)), opts)
| 2 -> cmd.SetHandler(Action<'A, 'B>(fun a b -> handler (a, b)), opts)
| 3 -> cmd.SetHandler(Action<'A, 'B, 'C>(fun a b c -> handler (a, b, c)), opts)
| 4 -> cmd.SetHandler(Action<'A, 'B, 'C, 'D>(fun a b c d -> handler (a, b, c, d)), opts)
| _ -> raise MaxArgumentsExceeded
cmd

/// Executes a command that returns a Task.
member this.Run (spec: CommandSpec<'Args, Task<unit>>) =
let cmd = initRootCmd spec
let opts = spec.Options |> Seq.cast<IValueDescriptor> |> Seq.toArray
let handler (args: obj) =
task {
do! spec.Handler (args :?> 'Args)
}

match spec.Options.Length with
| 01 -> cmd.SetHandler(Func<'A, Task>(fun a -> handler (a)), opts)
| 02 -> cmd.SetHandler(Func<'A, 'B, Task>(fun a b -> handler (a, b)), opts)
| 03 -> cmd.SetHandler(Func<'A, 'B, 'C, Task>(fun a b c -> handler (a, b, c)), opts)
| 04 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, Task>(fun a b c d -> handler (a, b, c, d)), opts)
| 05 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, Task>(fun a b c d e -> handler (a, b, c, d, e)), opts)
| 06 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, Task>(fun a b c d e f -> handler (a, b, c, d, e, f)), opts)
| 07 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, Task>(fun a b c d e f g -> handler (a, b, c, d, e, f, g)), opts)
| 08 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, Task>(fun a b c d e f g h -> handler (a, b, c, d, e, f, g, h)), opts)
| 09 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, Task>(fun a b c d e f g h i -> handler (a, b, c, d, e, f, g, h, i)), opts)
| 10 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, Task>(fun a b c d e f g h i j -> handler (a, b, c, d, e, f, g, h, i, j)), opts)
| 11 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, Task>(fun a b c d e f g h i j k -> handler (a, b, c, d, e, f, g, h, i, j, k)), opts)
| 12 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, Task>(fun a b c d e f g h i j k l -> handler (a, b, c, d, e, f, g, h, i, j, k, l)), opts)
| 13 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, Task>(fun a b c d e f g h i j k l m -> handler (a, b, c, d, e, f, g, h, i, j, k, l, m)), opts)
| 14 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, Task>(fun a b c d e f g h i j k l m n -> handler (a, b, c, d, e, f, g, h, i, j, k, l, m, n)), opts)
| 15 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, Task>(fun a b c d e f g h i j k l m n o -> handler (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o)), opts)
| 16 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, Task>(fun a b c d e f g h i j k l m n o p -> handler (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)), opts)
| _ -> raise MaxArgumentsExceeded
cmd


/// Builds a `System.CommandLine.RootCommand` using computation expression syntax.
let rootCommand<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, 'Result> =
RootCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, 'Result>()

/// Builds a `System.CommandLine.Command` using computation expression syntax.
let command<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, 'Result> (name: string) =
CommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, 'Result>(name)
17 changes: 17 additions & 0 deletions src/FSharp.SystemCommandLine/FSharp.SystemCommandLine.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>

<ItemGroup>
<Compile Include="Types.fs" />
<Compile Include="CommandBuilders.fs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="System.CommandLine" Version="2.0.0-beta3.22114.1" />
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions src/FSharp.SystemCommandLine/Types.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace FSharp.SystemCommandLine

/// An alias for `System.CommandLine.Option`.
type Opt<'T> = System.CommandLine.Option<'T>

/// An alias for `System.CommandLine.Argument`.
type Arg<'T> = System.CommandLine.Argument<'T>

0 comments on commit b9fef8d

Please sign in to comment.