Search code examples
.netf#spectre.console

Converting Spectre.Console's Introduction example to F#


I'd like to use Spectre.Console in an F# console app. I'm trying to convert the Introduction example from C# to F#. Below is my attempt:

open System

open Spectre.Console.Cli

type AddSettings() =
    inherit CommandSettings()

    [<CommandArgument(0, "[PROJECT]")>]
    member val Project = "" with get, set

type AddPackageSettings() =
    inherit AddSettings()

    [<CommandArgument(0, "<PACKAGE_NAME>")>]
    member val PackageName = "" with get, set

    [<CommandOption("-v|--version <VERSION>")>]
    member val Version = "" with get, set

 type AddReferenceSettings() =
    inherit AddSettings()

    [<CommandArgument(0, "<PROJECT_REFERENCE>")>]
    member val ProjectReference = "" with get, set

type AddPackageCommand() =
    inherit Command<AddPackageSettings>()

    override _.Execute (context,  settings) = 0

type AddReferenceCommand() =
    inherit Command<AddReferenceSettings>()

    override _.Execute(context, settings) = 0


// Define a function to construct a message to print
let from whom =
    sprintf "from %s" whom

[<EntryPoint>]
let main argv =
    let message = from "F#" // Call the function
    let app = CommandApp()

    app.Configure(fun config ->
        config.AddBranch<AddSettings>("add", fun add ->
            add.AddCommand<AddPackageCommand>("package")
            add.AddCommand<AddReferenceCommand>("reference")
        )
    )

    app.Run(argv)

Unfortunately, I get the following compiler error:

The type 'AddPackageCommand' is not compatible with the type 'ICommandLimiter<AddSettings>'

The type 'AddPackageCommand' is not compatible with the type 'ICommandLimiter'

Thank you for any and all help.


Solution

  • Here's how I solved that issue:

    1. Explicitly add interface ICommandLimiter<AddSettings> to the Command subclasses that use inheritance in the settings from AddSettings.
    2. Change the AddBranch call to infer the generic argument by only annotating the IConfigurator's type: config.AddBranch("add", fun (add: IConfigurator<AddSettings>) -> ....
    open System
    
    open Spectre.Console.Cli
    
    type AddSettings() =
        inherit CommandSettings()
    
        [<CommandArgument(0, "[PROJECT]")>]
        member val Project = "" with get, set
    
    type AddPackageSettings() =
        inherit AddSettings()
    
        [<CommandArgument(0, "<PACKAGE_NAME>")>]
        member val PackageName = "" with get, set
    
        [<CommandOption("-v|--version <VERSION>")>]
        member val Version = "" with get, set
    
     type AddReferenceSettings() =
        inherit AddSettings()
    
        [<CommandArgument(0, "<PROJECT_REFERENCE>")>]
        member val ProjectReference = "" with get, set
    
    type AddPackageCommand() =
        inherit Command<AddPackageSettings>()
    
        interface ICommandLimiter<AddSettings>
    
        override _.Execute (context,  settings) = 0
    
    type AddReferenceCommand() =
        inherit Command<AddReferenceSettings>()
    
        interface ICommandLimiter<AddSettings>
    
        override _.Execute(context, settings) = 0
    
    
    // Define a function to construct a message to print
    let from whom =
        sprintf "from %s" whom
    
    [<EntryPoint>]
    let main argv =
        let message = from "F#" // Call the function
        let app = CommandApp()
    
        app.Configure(fun config ->
            config.AddBranch("add", fun (add: IConfigurator<AddSettings>) ->
                add.AddCommand<AddPackageCommand>("package") |> ignore
                add.AddCommand<AddReferenceCommand>("reference") |> ignore
            )
        )
    
        app.Run(argv)