cli

package
v2.2.20 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 23, 2026 License: Apache-2.0 Imports: 24 Imported by: 0

README

CLI Package - Functional Wrappers for urfave/cli/v3

This package provides functional programming wrappers for the github.com/urfave/cli/v3 library, enabling Effect-based command actions and type-safe flag handling through Prisms.

Features

1. Effect-Based Command Actions

Transform CLI command actions into composable Effects that follow functional programming principles.

Key Functions
  • ToAction(effect CommandEffect) func(context.Context, *C.Command) error

    • Converts a CommandEffect into a standard urfave/cli Action function
    • Enables Effect-based command handlers to work with cli/v3 framework
  • FromAction(action func(context.Context, *C.Command) error) CommandEffect

    • Lifts existing cli/v3 action handlers into the Effect type
    • Allows gradual migration to functional style
  • MakeCommand(name, usage string, flags []C.Flag, effect CommandEffect) *C.Command

    • Creates a new Command with an Effect-based action
    • Convenience function combining command creation with Effect conversion
  • MakeCommandWithSubcommands(...) *C.Command

    • Creates a Command with subcommands and an Effect-based action
Example Usage
import (
    "context"
    
    E "github.com/IBM/fp-go/v2/effect"
    F "github.com/IBM/fp-go/v2/function"
    R "github.com/IBM/fp-go/v2/result"
    "github.com/IBM/fp-go/v2/cli"
    C "github.com/urfave/cli/v3"
)

// Define an Effect-based command action
processEffect := func(cmd *C.Command) E.Thunk[F.Void] {
    return func(ctx context.Context) E.IOResult[F.Void] {
        return func() R.Result[F.Void] {
            input := cmd.String("input")
            // Process input...
            return R.Of(F.Void{})
        }
    }
}

// Create command with Effect
command := cli.MakeCommand(
    "process",
    "Process input files",
    []C.Flag{
        &C.StringFlag{Name: "input", Usage: "Input file path"},
    },
    processEffect,
)

// Or convert existing action to Effect
existingAction := func(ctx context.Context, cmd *C.Command) error {
    // Existing logic...
    return nil
}
effect := cli.FromAction(existingAction)
2. Flag Type Prisms

Type-safe extraction and manipulation of CLI flags using Prisms from the optics package.

Available Prisms
  • StringFlagPrism() - Extract *C.StringFlag from C.Flag
  • IntFlagPrism() - Extract *C.IntFlag from C.Flag
  • BoolFlagPrism() - Extract *C.BoolFlag from C.Flag
  • Float64FlagPrism() - Extract *C.Float64Flag from C.Flag
  • DurationFlagPrism() - Extract *C.DurationFlag from C.Flag
  • TimestampFlagPrism() - Extract *C.TimestampFlag from C.Flag
  • StringSliceFlagPrism() - Extract *C.StringSliceFlag from C.Flag
  • IntSliceFlagPrism() - Extract *C.IntSliceFlag from C.Flag
  • Float64SliceFlagPrism() - Extract *C.Float64SliceFlag from C.Flag
  • UintFlagPrism() - Extract *C.UintFlag from C.Flag
  • Uint64FlagPrism() - Extract *C.Uint64Flag from C.Flag
  • Int64FlagPrism() - Extract *C.Int64Flag from C.Flag
Example Usage
import (
    O "github.com/IBM/fp-go/v2/option"
    "github.com/IBM/fp-go/v2/cli"
    C "github.com/urfave/cli/v3"
)

// Extract a StringFlag from a Flag interface
var flag C.Flag = &C.StringFlag{Name: "input", Value: "default"}
prism := cli.StringFlagPrism()

// Safe extraction returns Option
result := prism.GetOption(flag)
if O.IsSome(result) {
    strFlag := O.MonadFold(result, 
        func() *C.StringFlag { return nil },
        func(f *C.StringFlag) *C.StringFlag { return f },
    )
    // Use strFlag...
}

// Type mismatch returns None
var intFlag C.Flag = &C.IntFlag{Name: "count"}
result = prism.GetOption(intFlag)  // Returns None

// Convert back to Flag
strFlag := &C.StringFlag{Name: "output"}
flag = prism.ReverseGet(strFlag)

Type Definitions

CommandEffect
type CommandEffect = E.Effect[*C.Command, F.Void]

A CommandEffect represents a CLI command action as an Effect. It takes a *C.Command as context and produces a result wrapped in the Effect monad.

The Effect structure is:

func(*C.Command) -> func(context.Context) -> func() -> Result[Void]

This allows for:

  • Composability: Effects can be composed using standard functional combinators
  • Testability: Pure functions are easier to test
  • Error Handling: Errors are explicitly represented in the Result type
  • Context Management: Context flows naturally through the Effect

Benefits

1. Functional Composition

Effects can be composed using standard functional programming patterns:

import (
    F "github.com/IBM/fp-go/v2/function"
    RRIOE "github.com/IBM/fp-go/v2/context/readerreaderioresult"
)

// Compose multiple effects
validateInput := func(cmd *C.Command) E.Thunk[F.Void] { /* ... */ }
processData := func(cmd *C.Command) E.Thunk[F.Void] { /* ... */ }
saveResults := func(cmd *C.Command) E.Thunk[F.Void] { /* ... */ }

// Chain effects together
pipeline := F.Pipe3(
    validateInput,
    RRIOE.Chain(func(F.Void) E.Effect[*C.Command, F.Void] { return processData }),
    RRIOE.Chain(func(F.Void) E.Effect[*C.Command, F.Void] { return saveResults }),
)
2. Type Safety

Prisms provide compile-time type safety when working with flags:

// Type-safe flag extraction
flags := []C.Flag{
    &C.StringFlag{Name: "input"},
    &C.IntFlag{Name: "count"},
}

for _, flag := range flags {
    // Safe extraction with pattern matching
    O.MonadFold(
        cli.StringFlagPrism().GetOption(flag),
        func() { /* Not a string flag */ },
        func(sf *C.StringFlag) { /* Handle string flag */ },
    )
}
3. Error Handling

Errors are explicitly represented in the Result type:

effect := func(cmd *C.Command) E.Thunk[F.Void] {
    return func(ctx context.Context) E.IOResult[F.Void] {
        return func() R.Result[F.Void] {
            if err := validateInput(cmd); err != nil {
                return R.Left[F.Void](err)  // Explicit error
            }
            return R.Of(F.Void{})  // Success
        }
    }
}
4. Testability

Pure functions are easier to test:

func TestCommandEffect(t *testing.T) {
    cmd := &C.Command{Name: "test"}
    effect := myCommandEffect(cmd)
    
    // Execute effect
    result := effect(context.Background())()
    
    // Assert on result
    assert.True(t, R.IsRight(result))
}

Migration Guide

From Standard Actions to Effects

Before:

command := &C.Command{
    Name: "process",
    Action: func(ctx context.Context, cmd *C.Command) error {
        input := cmd.String("input")
        // Process...
        return nil
    },
}

After:

effect := func(cmd *C.Command) E.Thunk[F.Void] {
    return func(ctx context.Context) E.IOResult[F.Void] {
        return func() R.Result[F.Void] {
            input := cmd.String("input")
            // Process...
            return R.Of(F.Void{})
        }
    }
}

command := cli.MakeCommand("process", "Process files", flags, effect)
Gradual Migration

You can mix both styles during migration:

// Wrap existing action
existingAction := func(ctx context.Context, cmd *C.Command) error {
    // Legacy code...
    return nil
}

// Use as Effect
effect := cli.FromAction(existingAction)
command := cli.MakeCommand("legacy", "Legacy command", flags, effect)

See Also

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplyCommand

func ApplyCommand() *C.Command

func BindCommand

func BindCommand() *C.Command

func BoolFlagPrism added in v2.2.20

func BoolFlagPrism() P.Prism[C.Flag, *C.BoolFlag]

BoolFlagPrism creates a Prism for extracting a BoolFlag from a Flag. This provides a type-safe way to work with boolean flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.BoolFlag] for safe BoolFlag extraction

Example Usage

prism := BoolFlagPrism()

// Extract BoolFlag from Flag
var flag C.Flag = &C.BoolFlag{Name: "verbose", Value: true}
result := prism.GetOption(flag)  // Some(*C.BoolFlag{...})

func Commands

func Commands() []*C.Command

func ContextReaderIOEitherCommand

func ContextReaderIOEitherCommand() *C.Command

func DICommand

func DICommand() *C.Command

func DurationFlagPrism added in v2.2.20

func DurationFlagPrism() P.Prism[C.Flag, *C.DurationFlag]

DurationFlagPrism creates a Prism for extracting a DurationFlag from a Flag. This provides a type-safe way to work with duration flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.DurationFlag] for safe DurationFlag extraction

Example Usage

prism := DurationFlagPrism()

// Extract DurationFlag from Flag
var flag C.Flag = &C.DurationFlag{Name: "timeout", Value: 30 * time.Second}
result := prism.GetOption(flag)  // Some(*C.DurationFlag{...})

func EitherCommand

func EitherCommand() *C.Command

func Float64FlagPrism added in v2.2.20

func Float64FlagPrism() P.Prism[C.Flag, *C.Float64Flag]

Float64FlagPrism creates a Prism for extracting a Float64Flag from a Flag. This provides a type-safe way to work with float64 flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.Float64Flag] for safe Float64Flag extraction

Example Usage

prism := Float64FlagPrism()

// Extract Float64Flag from Flag
var flag C.Flag = &C.Float64Flag{Name: "ratio", Value: 0.5}
result := prism.GetOption(flag)  // Some(*C.Float64Flag{...})

func Float64SliceFlagPrism added in v2.2.20

func Float64SliceFlagPrism() P.Prism[C.Flag, *C.Float64SliceFlag]

Float64SliceFlagPrism creates a Prism for extracting a Float64SliceFlag from a Flag. This provides a type-safe way to work with float64 slice flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.Float64SliceFlag] for safe Float64SliceFlag extraction

Example Usage

prism := Float64SliceFlagPrism()

// Extract Float64SliceFlag from Flag
var flag C.Flag = &C.Float64SliceFlag{Name: "ratios"}
result := prism.GetOption(flag)  // Some(*C.Float64SliceFlag{...})

func IOCommand

func IOCommand() *C.Command

func IOEitherCommand

func IOEitherCommand() *C.Command

func IOOptionCommand

func IOOptionCommand() *C.Command

func IdentityCommand

func IdentityCommand() *C.Command

func Int64FlagPrism added in v2.2.20

func Int64FlagPrism() P.Prism[C.Flag, *C.Int64Flag]

Int64FlagPrism creates a Prism for extracting an Int64Flag from a Flag. This provides a type-safe way to work with int64 flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.Int64Flag] for safe Int64Flag extraction

Example Usage

prism := Int64FlagPrism()

// Extract Int64Flag from Flag
var flag C.Flag = &C.Int64Flag{Name: "offset"}
result := prism.GetOption(flag)  // Some(*C.Int64Flag{...})

func IntFlagPrism added in v2.2.20

func IntFlagPrism() P.Prism[C.Flag, *C.IntFlag]

IntFlagPrism creates a Prism for extracting an IntFlag from a Flag. This provides a type-safe way to work with integer flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.IntFlag] for safe IntFlag extraction

Example Usage

prism := IntFlagPrism()

// Extract IntFlag from Flag
var flag C.Flag = &C.IntFlag{Name: "count", Value: 10}
result := prism.GetOption(flag)  // Some(*C.IntFlag{...})

func IntSliceFlagPrism added in v2.2.20

func IntSliceFlagPrism() P.Prism[C.Flag, *C.IntSliceFlag]

IntSliceFlagPrism creates a Prism for extracting an IntSliceFlag from a Flag. This provides a type-safe way to work with int slice flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.IntSliceFlag] for safe IntSliceFlag extraction

Example Usage

prism := IntSliceFlagPrism()

// Extract IntSliceFlag from Flag
var flag C.Flag = &C.IntSliceFlag{Name: "ports"}
result := prism.GetOption(flag)  // Some(*C.IntSliceFlag{...})

func LensCommand

func LensCommand() *C.Command

LensCommand creates the CLI command for lens generation

func MakeCommand added in v2.2.20

func MakeCommand(
	name string,
	usage string,
	flags []C.Flag,
	effect CommandEffect,
) *C.Command

MakeCommand creates a new Command with an Effect-based action. This is a convenience function that combines command creation with Effect conversion.

Parameters

  • name: The command name
  • usage: The command usage description
  • flags: The command flags
  • effect: The CommandEffect to use as the action

Returns

  • A *C.Command configured with the Effect-based action

Example Usage

cmd := MakeCommand(
    "process",
    "Process data files",
    []C.Flag{
        &C.StringFlag{Name: "input", Usage: "Input file"},
    },
    func(cmd *C.Command) E.Thunk[F.Void] {
        return func(ctx context.Context) E.IOResult[F.Void] {
            return func() R.Result[F.Void] {
                input := cmd.String("input")
                // Process input...
                return R.Of(F.Void{})
            }
        }
    },
)

func MakeCommandWithSubcommands added in v2.2.20

func MakeCommandWithSubcommands(
	name string,
	usage string,
	flags []C.Flag,
	commands []*C.Command,
	effect CommandEffect,
) *C.Command

MakeCommandWithSubcommands creates a new Command with subcommands and an Effect-based action.

Parameters

  • name: The command name
  • usage: The command usage description
  • flags: The command flags
  • commands: The subcommands
  • effect: The CommandEffect to use as the action

Returns

  • A *C.Command configured with subcommands and the Effect-based action

Example Usage

cmd := MakeCommandWithSubcommands(
    "app",
    "Application commands",
    []C.Flag{},
    []*C.Command{subCmd1, subCmd2},
    defaultEffect,
)

func OptionCommand

func OptionCommand() *C.Command

func PipeCommand

func PipeCommand() *C.Command

func ReaderCommand

func ReaderCommand() *C.Command

func ReaderIOEitherCommand

func ReaderIOEitherCommand() *C.Command

func StringFlagPrism added in v2.2.20

func StringFlagPrism() P.Prism[C.Flag, *C.StringFlag]

StringFlagPrism creates a Prism for extracting a StringFlag from a Flag. This provides a type-safe way to work with string flags, handling type mismatches gracefully through the Option type.

The prism's GetOption attempts to cast a Flag to *C.StringFlag. If the cast succeeds, it returns Some(*C.StringFlag); if it fails, it returns None.

The prism's ReverseGet converts a *C.StringFlag back to a Flag.

Returns

  • A Prism[C.Flag, *C.StringFlag] for safe StringFlag extraction

Example Usage

prism := StringFlagPrism()

// Extract StringFlag from Flag
var flag C.Flag = &C.StringFlag{Name: "input", Value: "default"}
result := prism.GetOption(flag)  // Some(*C.StringFlag{...})

// Type mismatch returns None
var intFlag C.Flag = &C.IntFlag{Name: "count"}
result = prism.GetOption(intFlag)  // None[*C.StringFlag]()

// Convert back to Flag
strFlag := &C.StringFlag{Name: "output"}
flag = prism.ReverseGet(strFlag)

func StringSliceFlagPrism added in v2.2.20

func StringSliceFlagPrism() P.Prism[C.Flag, *C.StringSliceFlag]

StringSliceFlagPrism creates a Prism for extracting a StringSliceFlag from a Flag. This provides a type-safe way to work with string slice flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.StringSliceFlag] for safe StringSliceFlag extraction

Example Usage

prism := StringSliceFlagPrism()

// Extract StringSliceFlag from Flag
var flag C.Flag = &C.StringSliceFlag{Name: "tags"}
result := prism.GetOption(flag)  // Some(*C.StringSliceFlag{...})

func TimestampFlagPrism added in v2.2.20

func TimestampFlagPrism() P.Prism[C.Flag, *C.TimestampFlag]

TimestampFlagPrism creates a Prism for extracting a TimestampFlag from a Flag. This provides a type-safe way to work with timestamp flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.TimestampFlag] for safe TimestampFlag extraction

Example Usage

prism := TimestampFlagPrism()

// Extract TimestampFlag from Flag
var flag C.Flag = &C.TimestampFlag{Name: "created"}
result := prism.GetOption(flag)  // Some(*C.TimestampFlag{...})

func ToAction added in v2.2.20

func ToAction(effect CommandEffect) func(context.Context, *C.Command) error

ToAction converts a CommandEffect into a standard urfave/cli Action function. This allows Effect-based command handlers to be used with the cli/v3 framework.

The conversion process:

  1. Takes the Effect which expects a *C.Command context
  2. Executes it with the provided command
  3. Runs the resulting IO operation
  4. Converts the Result to either nil (success) or error (failure)

Parameters

  • effect: The CommandEffect to convert

Returns

  • A function compatible with C.Command.Action signature

Example Usage

effect := func(cmd *C.Command) E.Thunk[F.Void] {
    return func(ctx context.Context) E.IOResult[F.Void] {
        return func() R.Result[F.Void] {
            // Command logic here
            return R.Of(F.Void{})
        }
    }
}
action := ToAction(effect)
command := &C.Command{
    Name: "example",
    Action: action,
}

func TupleCommand

func TupleCommand() *C.Command

func Uint64FlagPrism added in v2.2.20

func Uint64FlagPrism() P.Prism[C.Flag, *C.Uint64Flag]

Uint64FlagPrism creates a Prism for extracting a Uint64Flag from a Flag. This provides a type-safe way to work with uint64 flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.Uint64Flag] for safe Uint64Flag extraction

Example Usage

prism := Uint64FlagPrism()

// Extract Uint64Flag from Flag
var flag C.Flag = &C.Uint64Flag{Name: "size"}
result := prism.GetOption(flag)  // Some(*C.Uint64Flag{...})

func UintFlagPrism added in v2.2.20

func UintFlagPrism() P.Prism[C.Flag, *C.UintFlag]

UintFlagPrism creates a Prism for extracting a UintFlag from a Flag. This provides a type-safe way to work with unsigned integer flags, handling type mismatches gracefully through the Option type.

Returns

  • A Prism[C.Flag, *C.UintFlag] for safe UintFlag extraction

Example Usage

prism := UintFlagPrism()

// Extract UintFlag from Flag
var flag C.Flag = &C.UintFlag{Name: "workers", Value: 4}
result := prism.GetOption(flag)  // Some(*C.UintFlag{...})

Types

type CommandEffect added in v2.2.20

type CommandEffect = E.Effect[*C.Command, F.Void]

CommandEffect represents a CLI command action as an Effect. The Effect takes a *C.Command as context and produces a result.

func FromAction added in v2.2.20

func FromAction(action func(context.Context, *C.Command) error) CommandEffect

FromAction converts a standard urfave/cli Action function into a CommandEffect. This allows existing cli/v3 action handlers to be lifted into the Effect type.

The conversion process:

  1. Takes a standard action function (context.Context, *C.Command) -> error
  2. Wraps it in the Effect structure
  3. Converts the error result to a Result type

Parameters

  • action: The standard cli/v3 action function to convert

Returns

  • A CommandEffect that wraps the original action

Example Usage

standardAction := func(ctx context.Context, cmd *C.Command) error {
    // Existing command logic
    return nil
}
effect := FromAction(standardAction)
// Now can be composed with other Effects

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL