tools/tools.go (215 lines of code) (raw):
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package tools
import (
"embed"
"fmt"
"log"
"os"
"runtime"
"strings"
gojq "github.com/itchyny/gojq/cli"
envsubst "github.com/nuvolaris/envsubst/cmd/envsubstmain"
replace "github.com/nuvolaris/go-replace"
"github.com/nuvolaris/goawk"
"github.com/nuvolaris/gron"
"github.com/nuvolaris/jj"
"golang.org/x/exp/slices"
)
//go:embed *.md
var markDownHelp embed.FS
var tracing = os.Getenv("TRACE") != ""
func trace(args ...any) {
if tracing {
log.Println(append([]any{"TRACE: "}, args...))
}
}
// shared with main
func GetOS() string {
res := os.Getenv("__OS")
if res == "" {
res = runtime.GOOS
}
return res
}
func GetARCH() string {
res := os.Getenv("__ARCH")
if res == "" {
res = runtime.GOARCH
}
return res
}
type Tool struct {
Name string
HasDoc bool
}
// available in taskfiles
// note some of them are implemented in main.go (config, retry)
// Note the comment with @DOC which is used to generate the list of tools in documentation
// put one here if you add a markdown file for a tool
var ToolList = []Tool{
{"wsk", false},
{"awk", false},
{"jq", false},
{"sh", true}, // @DOC
{"envsubst", false},
{"filetype", true}, // @DOC
{"random", true}, // @DOC
{"datefmt", true}, // @DOC
{"die", false},
{"urlenc", true}, // @DOC
{"replace", false},
{"base64", true}, // @DOC
{"validate", true}, // @DOC
{"echoif", true}, // @DOC
{"echoifempty", true}, // @DOC
{"echoifexists", true}, // @DOC
{"needupdate", true}, // @DOC
{"gron", false},
{"jj", false},
{"rename", true}, // @DOC
{"remove", true}, // @DOC
{"executable", true}, // @DOC
{"empty", true}, // @DOC
{"extract", true}, // @DOC
{"opspath", true}, // @DOC
}
func IsTool(name string) bool {
for _, s := range ToolList {
if s.Name == name {
return true
}
}
return false
}
func RunTool(name string, args []string) (int, error) {
switch name {
case "wsk":
//fmt.Println("=== wsk ===")
cmd := append([]string{"wsk"}, args...)
if err := Wsk(cmd); err != nil {
return 1, err
}
case "awk":
// fmt.Println("=== awk ===")
os.Args = append([]string{"goawk"}, args...)
if err := goawk.AwkMain(); err != nil {
return 1, err
}
case "jq":
os.Args = append([]string{"gojq"}, args...)
return gojq.Run(), nil
case "sh":
os.Args = append([]string{"sh"}, args...)
return Sh()
case "envsubst":
os.Args = append([]string{"envsubst"}, args...)
if err := envsubst.EnvsubstMain(); err != nil {
return 1, err
}
case "filetype":
os.Args = append([]string{"mkdir"}, args...)
if err := Filetype(); err != nil {
return 1, err
}
case "random":
if err := RandTool(args...); err != nil {
return 1, err
}
case "datefmt":
if err := DateFmtTool(append([]string{"datefmt"}, args...)); err != nil {
return 1, err
}
case "die":
if len(args) > 0 {
fmt.Println(strings.Join(args, " "))
}
return 1, nil
case "urlenc":
os.Args = append([]string{"urlenc"}, args...)
if err := URLEncTool(); err != nil {
return 1, err
}
case "replace":
os.Args = append([]string{"replace"}, args...)
return replace.ReplaceMain()
case "base64":
os.Args = append([]string{"base64"}, args...)
if err := base64Tool(); err != nil {
return 1, err
}
case "validate":
os.Args = append([]string{"validate"}, args...)
if err := validateTool(); err != nil {
return 1, err
}
case "echoif":
os.Args = append([]string{"echoif"}, args...)
if err := echoIfTool(); err != nil {
return 1, err
}
case "echoifempty":
os.Args = append([]string{"echoifempty"}, args...)
if err := echoIfEmptyTool(); err != nil {
return 1, err
}
case "echoifexists":
os.Args = append([]string{"echoifexists"}, args...)
if err := echoIfExistsTool(); err != nil {
return 1, err
}
case "needupdate":
if err := needUpdateTool(args); err != nil {
return 1, err
}
case "gron":
os.Args = append([]string{"gron"}, args...)
return gron.GronMain()
case "jj":
os.Args = append([]string{"jj"}, args...)
return jj.JJMain()
case "rename":
os.Args = append([]string{"rename"}, args...)
return Rename()
case "remove":
os.Args = append([]string{"remove"}, args...)
return Remove()
case "executable":
os.Args = append([]string{"executable"}, args...)
return Executable()
case "extract":
os.Args = append([]string{"extract"}, args...)
return Extract()
case "empty":
os.Args = append([]string{"empty"}, args...)
return Empty()
case "opspath":
os.Args = append([]string{"opspath"}, args...)
return OpsPath()
default:
return 1, fmt.Errorf("unknown tool")
}
return 0, nil
}
func MergeToolsList(mainTools []string) []string {
availableTools := mainTools
for _, tool := range ToolList {
availableTools = append(availableTools, tool.Name)
}
return availableTools
}
func Help(mainTools []string) {
fmt.Println("Tools (use -<tool> -h for help):")
availableTools := MergeToolsList(mainTools)
slices.Sort(availableTools)
for _, x := range availableTools {
fmt.Printf("-%s\n", x)
}
}
func GetMarkDown(toolName string) (string, error) {
// extract markdown from embedded resource
markDownFile := fmt.Sprintf("%s.md", toolName)
fileData, err := markDownHelp.ReadFile(markDownFile)
if err != nil {
return "", err
}
return string(fileData), nil
}
func MarkdownHelp(toolName string) string {
// extract markdown from embedded resource
fileData, err := GetMarkDown(toolName)
if err != nil {
return ""
}
result := string(fileData)
// convert to markdown
help := MarkdownToText(result)
// return opts and help markdown
return help
}