Skip to content
36 changes: 36 additions & 0 deletions README.md
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jules the documentation needs to be expanded a lot about how to use these new commands.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've expanded the README.md section on CLI usage, including examples of using standard commands with options (strings2 kebab --first-upper, strings2 snake --screaming) and a comprehensive list of all the newly exposed flags.

Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,44 @@ fmt.Println(strings2.ToKebabCase(words, strings2.OptionDelimiter("|")))
fmt.Println(strings2.ToSnakeCase(words, strings2.OptionCaseMode(strings2.CMScreaming)))
```

### CLI Mode

The library also provides a command-line interface that exposes all these options, ensuring that the CLI mode has as much flexibility as the code (without being obligated to use smart defaults).

```bash
strings2 camel "hello world"
# Result: helloWorld

strings2 snake --screaming "hello world"
# Result: HELLO_WORLD

strings2 kebab --first-upper "hello world"
# Result: Hello-world
```

You can pipe input into the CLI as well:
```bash
echo "hello world" | strings2 pascal
# Result: HelloWorld
```

Available flags across commands:
- `--delimiter`, `-d` (string): Override the delimiter
- `--screaming`, `-S`: Enforce uppercase formatting
- `--whispering`, `-w`: Enforce lowercase formatting
- `--first-upper`, `-U`: Capitalize the first letter
- `--first-lower`, `-l`: Lowercase the first letter
- `--mix-case-support`, `-m`: Enable splitting of mixed case words
- `--no-smart-acronyms`: Disable acronym preservation
- `--number-splitting`: Enable letter-digit boundary splitting
```

Options are composable so multiple behaviours can be applied at once. See the documentation in `types.go` for details on further options.

## TODO

- Support slices for flags when the gosubc version supports it.

## License

This project is licensed under the BSD 3-Clause License - see the [LICENSE](LICENSE) file for details.
92 changes: 82 additions & 10 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/arran4/strings2"
)

func process(input string, output string, args []string, fn func(string, ...any) (string, error)) {
func process(input string, output string, args []string, fn func(string, ...any) (string, error), opts ...any) {
var in io.Reader
if input == "-" {
in = os.Stdin
Expand All @@ -33,7 +33,7 @@ func process(input string, output string, args []string, fn func(string, ...any)
os.Exit(1)
}

res, err := fn(string(b))
res, err := fn(string(b), opts...)
if err != nil {
fmt.Fprintf(os.Stderr, "Error processing: %v\n", err)
os.Exit(1)
Expand All @@ -55,15 +55,57 @@ func process(input string, output string, args []string, fn func(string, ...any)
fmt.Fprintln(out, res)
}

func buildOpts(delimiter string, screaming bool, whispering bool, firstUpper bool, firstLower bool, mixCaseSupport bool, noSmartAcronyms bool, numberSplitting bool, strict bool) []any {
var opts []any
if delimiter != "" {
opts = append(opts, strings2.OptionDelimiter(delimiter))
}
if screaming {
opts = append(opts, strings2.OptionCaseMode(strings2.CMScreaming))
}
if whispering {
opts = append(opts, strings2.OptionCaseMode(strings2.CMWhispering))
}
if firstUpper {
opts = append(opts, strings2.OptionFirstUpper())
}
if firstLower {
opts = append(opts, strings2.OptionFirstLower())
}
if mixCaseSupport {
opts = append(opts, strings2.OptionMixCaseSupport())
}
if noSmartAcronyms {
opts = append(opts, strings2.WithSmartAcronyms(false))
}
if numberSplitting {
opts = append(opts, strings2.WithNumberSplitting(true))
}
if strict {
opts = append(opts, strings2.OptionStrict())
}
return opts
}

// Camel is a subcommand `strings2 camel`
//
// Flags:
//
// input: -i --input (default: "") Input file or - for stdin
// output: -o --output (default: "") Output file or - for stdout
// delimiter: -d --delimiter (default: "") Delimiter
// screaming: -S --screaming (default: false) Screaming mode
// whispering: -w --whispering (default: false) Whispering mode
// firstUpper: -U --first-upper (default: false) First char upper
// firstLower: -l --first-lower (default: false) First char lower
// mixCaseSupport: -m --mix-case-support (default: false) Mix case support
// noSmartAcronyms: --no-smart-acronyms (default: false) Disable smart acronyms
// numberSplitting: --number-splitting (default: false) Enable number splitting
// strict: --strict (default: false) Strict UTF8 mode
// args: ... String to convert if file/stdin not provided
func Camel(input string, output string, args ...string) {
process(input, output, args, strings2.ToCamel)
func Camel(input string, output string, delimiter string, screaming bool, whispering bool, firstUpper bool, firstLower bool, mixCaseSupport bool, noSmartAcronyms bool, numberSplitting bool, strict bool, args ...string) {
opts := buildOpts(delimiter, screaming, whispering, firstUpper, firstLower, mixCaseSupport, noSmartAcronyms, numberSplitting, strict)
process(input, output, args, strings2.ToCamel, opts...)
}

// Snake is a subcommand `strings2 snake`
Expand All @@ -72,9 +114,19 @@ func Camel(input string, output string, args ...string) {
//
// input: -i --input (default: "") Input file or - for stdin
// output: -o --output (default: "") Output file or - for stdout
// delimiter: -d --delimiter (default: "") Delimiter
// screaming: -S --screaming (default: false) Screaming mode
// whispering: -w --whispering (default: false) Whispering mode
// firstUpper: -U --first-upper (default: false) First char upper
// firstLower: -l --first-lower (default: false) First char lower
// mixCaseSupport: -m --mix-case-support (default: false) Mix case support
// noSmartAcronyms: --no-smart-acronyms (default: false) Disable smart acronyms
// numberSplitting: --number-splitting (default: false) Enable number splitting
// strict: --strict (default: false) Strict UTF8 mode
// args: ... String to convert if file/stdin not provided
func Snake(input string, output string, args ...string) {
process(input, output, args, strings2.ToSnake)
func Snake(input string, output string, delimiter string, screaming bool, whispering bool, firstUpper bool, firstLower bool, mixCaseSupport bool, noSmartAcronyms bool, numberSplitting bool, strict bool, args ...string) {
opts := buildOpts(delimiter, screaming, whispering, firstUpper, firstLower, mixCaseSupport, noSmartAcronyms, numberSplitting, strict)
process(input, output, args, strings2.ToSnake, opts...)
}

// Kebab is a subcommand `strings2 kebab`
Expand All @@ -83,9 +135,19 @@ func Snake(input string, output string, args ...string) {
//
// input: -i --input (default: "") Input file or - for stdin
// output: -o --output (default: "") Output file or - for stdout
// delimiter: -d --delimiter (default: "") Delimiter
// screaming: -S --screaming (default: false) Screaming mode
// whispering: -w --whispering (default: false) Whispering mode
// firstUpper: -U --first-upper (default: false) First char upper
// firstLower: -l --first-lower (default: false) First char lower
// mixCaseSupport: -m --mix-case-support (default: false) Mix case support
// noSmartAcronyms: --no-smart-acronyms (default: false) Disable smart acronyms
// numberSplitting: --number-splitting (default: false) Enable number splitting
// strict: --strict (default: false) Strict UTF8 mode
// args: ... String to convert if file/stdin not provided
func Kebab(input string, output string, args ...string) {
process(input, output, args, strings2.ToKebab)
func Kebab(input string, output string, delimiter string, screaming bool, whispering bool, firstUpper bool, firstLower bool, mixCaseSupport bool, noSmartAcronyms bool, numberSplitting bool, strict bool, args ...string) {
opts := buildOpts(delimiter, screaming, whispering, firstUpper, firstLower, mixCaseSupport, noSmartAcronyms, numberSplitting, strict)
process(input, output, args, strings2.ToKebab, opts...)
}

// Pascal is a subcommand `strings2 pascal`
Expand All @@ -94,7 +156,17 @@ func Kebab(input string, output string, args ...string) {
//
// input: -i --input (default: "") Input file or - for stdin
// output: -o --output (default: "") Output file or - for stdout
// delimiter: -d --delimiter (default: "") Delimiter
// screaming: -S --screaming (default: false) Screaming mode
// whispering: -w --whispering (default: false) Whispering mode
// firstUpper: -U --first-upper (default: false) First char upper
// firstLower: -l --first-lower (default: false) First char lower
// mixCaseSupport: -m --mix-case-support (default: false) Mix case support
// noSmartAcronyms: --no-smart-acronyms (default: false) Disable smart acronyms
// numberSplitting: --number-splitting (default: false) Enable number splitting
// strict: --strict (default: false) Strict UTF8 mode
// args: ... String to convert if file/stdin not provided
func Pascal(input string, output string, args ...string) {
process(input, output, args, strings2.ToPascal)
func Pascal(input string, output string, delimiter string, screaming bool, whispering bool, firstUpper bool, firstLower bool, mixCaseSupport bool, noSmartAcronyms bool, numberSplitting bool, strict bool, args ...string) {
opts := buildOpts(delimiter, screaming, whispering, firstUpper, firstLower, mixCaseSupport, noSmartAcronyms, numberSplitting, strict)
process(input, output, args, strings2.ToPascal, opts...)
}
79 changes: 79 additions & 0 deletions cmd/agents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!-- Code generated by github.com/arran4/go-subcommand/cmd/gosubc. DO NOT EDIT. -->
All code under /cmd is generated code, do not place files here.

# Go Subcommand Information

Go Subcommand (`gosubc`) generates subcommand code for command-line interfaces (CLIs) in Go from source code comments.

## Key Features

- **Convention over Configuration:** Define your CLI structure with simple, intuitive code comments.
- **Zero Dependencies:** The generated code is self-contained and doesn't require any external libraries.
- **Automatic Code Generation:** `gosubc` parses your Go files and generates a complete, ready-to-use CLI.

## Installation

`gosubc` is a standalone tool and should not be added as a dependency in your `go.mod`. Install it using:

```bash
go install github.com/arran4/go-subcommand/cmd/gosubc@latest
```

## How it works

1. **Define Your Commands**: Create a Go file and define a function that will serve as your command. Add a comment above the function.
2. **Generate**: Run `gosubc generate` (or via `go generate`).
3. **Result**: `gosubc` creates a `cmd/<app-name>` directory containing the generated CLI code.

## Comment Syntax

### Command Definition

```go
// FuncName is a subcommand 'root-cmd parent child'
func FuncName() {}
```

### Flags

Use a `Flags:` block or inline comments. Adhere to Go formatting.

```go
// FuncName is a subcommand 'root-cmd parent child'
//
// Flags:
//
// username: --username -u (default: "guest") The user to greet
// count: --count -c (default: 1) Number of times
func FuncName(username string, count int) {}
```

### Positional Arguments

Map positional arguments using `@N`.

```go
// Greet is a subcommand 'app greet'
//
// Flags:
//
// name: @1 The name to greet
func Greet(name string) {}
```

### Variadic Arguments

Map remaining arguments using `...`.

```go
// Process is a subcommand 'app process'
//
// Flags:
//
// files: ... List of files
func Process(files ...string) {}
```

## Important Note

Do not edit files in this directory directly if you can avoid it. They are overwritten on every generation. Modify the source code comments instead.
19 changes: 18 additions & 1 deletion cmd/errors.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading