diff --git a/Makefile b/Makefile index 40003f6..4cec5e4 100644 --- a/Makefile +++ b/Makefile @@ -6,31 +6,37 @@ help: ## Show help .PHONY: check check: ## Checks version, runs tests. - grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$$' < version.txt - $(eval VERSION := $(shell head -n 1 version.txt)) - grep -E '^version: $(VERSION)$$' < cmd/cyamli/cli.yaml + grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$$' ./info/version.txt + $(eval VERSION := $(shell head -n 1 ./info/version.txt)) + grep -E '^version: $(VERSION)$$' ./cyamli/cli.yaml go test ./... -.PHONY: gen-cli -gen-cli: ## Generates Go CLI for cyamli command. - go run ./internal/tools/gen-cli-golang/main.go < cmd/cyamli/cli.yaml > cmd/cyamli/cli.gen.go +.PHONY: install +install: ## Install cyamli built in present status. + go run ./internal/cmd/gen-golang < cyamli/cli.yaml > cyamli/cli.gen.go + go install . + cyamli generate golang -package=cyamli < cyamli/cli.yaml > cyamli/cli.gen.go + go install . .PHONY: version-apply version-apply: ## Generates Go CLI for cyamli command. - grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$$' < version.txt - grep -E '^version: v[0-9]+\.[0-9]+\.[0-9]+$$' < cmd/cyamli/cli.yaml - $(eval VERSION := $(shell head -n 1 version.txt)) - sed -E -i.backup "s/^version: v[0-9]+\.[0-9]+\.[0-9]+$$/version: $(VERSION)/g" cmd/cyamli/cli.yaml - rm cmd/cyamli/cli.yaml.backup - make gen-cli + grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$$' < ./info/version.txt + grep -E '^version: v[0-9]+\.[0-9]+\.[0-9]+$$' < ./cyamli/cli.yaml + $(eval VERSION := $(shell head -n 1 ./info/version.txt)) + sed -E -i.backup "s/^version: v[0-9]+\.[0-9]+\.[0-9]+$$/version: $(VERSION)/g" ./cyamli/cli.yaml + rm ./cyamli/cli.yaml.backup + make install make examples + make docs .PHONY: examples -examples: ## Generates Go CLI for cyamli command. - go run ./internal/tools/gen-cli-golang/main.go < examples/cmd/example/cli.yaml > examples/cmd/example/cli.gen.go - go run ./internal/tools/gen-cli-golang/main.go < examples/cmd/greet/cli.yaml > examples/cmd/greet/cli.gen.go - go run ./internal/tools/gen-cli-golang/main.go < examples/cmd/demo-app/cli.yaml > examples/cmd/demo-app/cli.gen.go +examples: install ## Generates Go CLI for cyamli command. + go run . generate golang < examples/cmd/example/cli.yaml > examples/cmd/example/cli.gen.go + go run . generate golang < examples/cmd/demo-app/cli.yaml > examples/cmd/demo-app/cli.gen.go - go run ./internal/tools/gen-cli-python3/main.go < examples/cmd/example/cli.yaml > examples/cmd/example/cli_gen.py - go run ./internal/tools/gen-cli-python3/main.go < examples/cmd/greet/cli.yaml > examples/cmd/greet/cli_gen.py - go run ./internal/tools/gen-cli-python3/main.go < examples/cmd/demo-app/cli.yaml > examples/cmd/demo-app/cli_gen.py + go run . generate python3 < examples/cmd/example/cli.yaml > examples/cmd/example/cli_gen.py + go run . generate python3 < examples/cmd/demo-app/cli.yaml > examples/cmd/demo-app/cli_gen.py + +.PHONY: docs +docs: install ## Generates documentation of cyamli. + go run . generate docs -all -format=markdown < cyamli/cli.yaml > cyamli-docs.md diff --git a/README.md b/README.md index 9d4095e..9410d96 100644 --- a/README.md +++ b/README.md @@ -1,163 +1,154 @@ # cyamli -A command line tool to generate command line interfaces for your command line tools from the YAML-based CLI schemas. +A command line tool to generate interfaces for command line tools from YAML-based CLI schemas. -## Highlights +## Overview -This repository: -- defines the CLI schema in YAML. -- provides a typed CLI code generator from the CLI schema. +Developing console apps involves defining and parsing command line interfaces (CLIs) such as command line arguments, which consist of subcommands, options, and positional arguments. -## CLI schema +`cyamli` is a schema-based code generator that generates APIs (Application Programming Interfaces, such as types and functions) to handle typed CLIs. +The schema of a typed CLI can be written in YAML according to the CLI schema definition ( https://github.com/Jumpaku/cyamli/blob/main/cli-schema-definition.ts ). -The CLI schema definition is provided in [`cli-schema-definition.ts`](https://github.com/Jumpaku/cyamli/blob/main/cli-schema-definition.ts). +## Motivation -## CLI code generator +- Schema-based approach leveraging standardized and consistent sources. +- Promoting typed CLIs for the benefits of static checking and code completion. +- Reducing boilerplate by automatically generating the necessary code. -From a YAML file written according to the CLI schema definition, `cyamli` generates a typed code for handling command line arguments. -### Installation +## Installation -`cyamli` can be installed as follows: - -```sh -go install "github.com/Jumpaku/cyamli/cmd/cyamli@latest" +```shell +go install github.com/Jumpaku/cyamli@latest ``` -or use go generate as follows: +## Usage with an example -```go -//go:generate go run "github.com/Jumpaku/cyamli/cmd/cyamli@latest" golang -schema-path=path/to/cli.yaml -out-path=path/to/cli.gen.go -``` +Assume a situation where you need to develop a console app in Go to fetch information from a database. -### Usage +Usage of `cyamli` is as follows: -1. Generating the CLI type. -2. Overwriting the CLI object. -3. Executing program. +1. Define a CLI as a YAML file. +2. Generate the API to parse the CLI in Go. +3. Assign functions to the generated API. -#### Generating the CLI type +### Define a CLI as a YAML file -Prepare the CLI schema for your application, for example: +The following YAML file, `cli.yaml`, defines a CLI for the example console app. ```yaml -name: greet -description: this is an example program -options: - -help: - short: -h - description: Show help information. - type: boolean +name: demo +description: demo app to get table information from databases subcommands: - hello: - description: Prints "Hello, ! My name is !" + list: + description: list tables options: - -target-name: - short: -t - description: The name of the person to be said hello. + -config: + description: path to config file + short: -c + fetch: + description: show information of tables + options: + -config: + description: path to config file + short: -c + -verbose: + description: show detailed contents for specified tables + short: -v + type: boolean arguments: - - name: greeter - description: The name of the person who says hello. + - name: tables + variadic: true + description: names of tables to be described ``` -Run `cyamli` as follows: +### Generate API to parse the CLI in Go + +The following command reads a schema from `cli.yaml` and writes the Go API into `cli.gen.go`. -```sh -cyamli golang < path/to/cli-schema.yaml > path/to/generated/code.go +```shell +cyamli generate golang -schema-path=cli.yaml -out-path=cli.gen.go ``` -The above generates a Go code which includes: +`cli.gen.go` includes the following API: ```go -type CLI -type CLI_Input -type CLI_Hello -type CLI_Hello_Input - +// CLI represents a root command. +type CLI struct +// NewCLI returns a CLI object. func NewCLI() CLI +// Run parses command line arguments args and calls a corresponding function assigned in cli. func Run(cli CLI, args []string) error +// GetDoc returns a help message corresponding to subcommand. +func GetDoc(subcommand []string) string ``` -#### Overwriting the CLI object +### Assign functions to the generated API. -To define the behavior of your program, you can utilize the generated types and functions as follows: +`NewCLI()` returns an object `cli` which represents a root command, and its descendant objects represent subcommands. +Each of them has a `FUNC` field. +A function assigned to this field will be called by `Run(cli, os.Args)`. -```go -// Create the CLI object -var cli = NewCLI() +The following code snippet demonstrates an implementation for the example console app. -func main() { - // Overwrite behaviors - cli.FUNC = showHelp - cli.Hello.FUNC = sayHello - // Run with command line arguments - if err := Run(cli, os.Args); err != nil { - panic(err) - } -} -``` +```go +package main -Example implementations for `showHelp` and `sayHello` are as follows: +import ( + "fmt" + "os" +) -```go -func showHelp(subcommand []string, input CLI_Input, inputErr error) (err error) { - if inputErr != nil { - fmt.Println(cli.DESC_Simple()) - panic(inputErr) +func main() { + cli := NewCLI() + cli.FUNC = func(subcommand []string, input CLI_Input, inputErr error) (err error) { + fmt.Println(input, inputErr) + fmt.Println(GetDoc(subcommand)) + return nil } - if input.Opt_Help { - fmt.Println(cli.DESC_Detail()) + cli.List.FUNC = func(subcommand []string, input CLI_List_Input, inputErr error) (err error) { + fmt.Println(input, inputErr) + fmt.Println(GetDoc(subcommand)) + return nil } - return nil -} -func sayHello(subcommand []string, input CLI_Hello_Input, inputErr error) (err error) { - if inputErr != nil { - fmt.Println(cli.Hello.DESC_Simple()) - return inputErr + cli.Fetch.FUNC = func(subcommand []string, input CLI_Fetch_Input, inputErr error) (err error) { + fmt.Println(input, inputErr) + fmt.Println(GetDoc(subcommand)) + return nil } - if input.Opt_TargetName != "" { - fmt.Printf("Hello, %s! My name is %s!\n", input.Opt_TargetName, input.Arg_Greeter) - } else { - fmt.Printf("Hello! My name is %s!\n", input.Arg_Greeter) + if err := Run(cli, os.Args); err != nil { + panic(err) } - return nil } ``` -#### Executing program - -Execute the `main` function as follows +The example console app can be executed as follows: -```sh -go run main.go -h -# => This is an example program. -go run main.go hello Alice -# => Hello! My name is Alice! -go run main.go hello -target-name=Bob Alice -# => Hello, Bob! My name is Alice! +```shell +go run main.go list -c=config.yaml +go run main.go fetch -c=config.yaml -v table1 table2 ``` -## Examples - -The example CLI applications are found in https://github.com/Jumpaku/cyamli/tree/main/examples . - ## Details ### Supported programming languages -The following programming languages are supported currently. +The following programming languages are currently supported: * Go * Python3 +* Documentation in text, HTML, and Markdown ### Handling command line arguments +Command line arguments according to the following syntax can be handled by the generated API. + ``` [