Add options support #24
29
FAQ.md
29
FAQ.md
|
@ -48,20 +48,21 @@ See the [documentation](https://golang.org/pkg/text/template/) for every availab
|
|||
|
||||
For a full list, see [helper.go](registry/helper.go)
|
||||
|
||||
| Helper | Example | Output |
|
||||
|-------------|----------------------------------|-------------------------------------------------------------------------------------------------------|
|
||||
| upper | `{{upper project}}` | `MY-PROJECT` |
|
||||
| lower | `{{lower project}}` | `my-project` |
|
||||
| title | `{{title project}}` | `My-Project` |
|
||||
| snake | `{{snake project}}` | `my_project` |
|
||||
| kebab | `{{kebab project}}` | `my-project` |
|
||||
| pascal | `{{pascal project}}` | `MyProject` |
|
||||
| camel | `{{camel project}}` | `myProject` |
|
||||
| env | `{{env "USER"}}` | The current user |
|
||||
| sep | `{{sep}}` | Filepath separator for current OS |
|
||||
| time | `{{time "01/02/2006"}}` | `11/21/2020` - The time according to the given [format](https://flaviocopes.com/go-date-time-format/) |
|
||||
| trim_prefix | `{{trim_prefix "foobar" "foo"}}` | `bar` |
|
||||
| trim_suffix | `{{trim_suffix "foobar" "bar"}}` | `foo` |
|
||||
| Helper | Example | Output |
|
||||
|-------------|------------------------------------|-------------------------------------------------------------------------------------------------------|
|
||||
| upper | `{{upper project}}` | `MY-PROJECT` |
|
||||
| lower | `{{lower project}}` | `my-project` |
|
||||
| title | `{{title project}}` | `My-Project` |
|
||||
| snake | `{{snake project}}` | `my_project` |
|
||||
| kebab | `{{kebab project}}` | `my-project` |
|
||||
| pascal | `{{pascal project}}` | `MyProject` |
|
||||
| camel | `{{camel project}}` | `myProject` |
|
||||
| env | `{{env "USER"}}` | The current user |
|
||||
| sep | `{{sep}}` | Filepath separator for current OS |
|
||||
| time | `{{time "01/02/2006"}}` | `11/21/2020` - The time according to the given [format](https://flaviocopes.com/go-date-time-format/) |
|
||||
| trim_prefix | `{{trim_prefix "foobar" "foo"}}` | `bar` |
|
||||
| trim_suffix | `{{trim_suffix "foobar" "bar"}}` | `foo` |
|
||||
| replace | `{{replace "foobar" "bar" "baz"}}` | `foobaz` |
|
||||
|
||||
## Sources
|
||||
|
||||
|
|
13
cmd/init.go
13
cmd/init.go
|
@ -36,7 +36,7 @@ func runInit(_ *cli.Context) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := fi.WriteString(comments); err != nil {
|
||||
if _, err := fi.WriteString(initConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Mkdir("template", os.ModePerm); err != nil {
|
||||
|
@ -46,12 +46,15 @@ func runInit(_ *cli.Context) error {
|
|||
return fi.Close()
|
||||
}
|
||||
|
||||
var comments = `# tmpl.yaml
|
||||
var initConfig = `# tmpl.yaml
|
||||
# Write any template args here to prompt the user for, giving any defaults/options as applicable
|
||||
|
||||
prompts:
|
||||
- id: name # The unique ID for the prompt
|
||||
label: Project Name # The prompt message/label
|
||||
help: The name to use in the project # Optional help message for the prompt
|
||||
default: tmpl # Prompt default
|
||||
label: Project Name # (Optional) Prompt message/label, defaults to id
|
||||
help: The name to use in the project # (Optional) Help message for the prompt
|
||||
default: tmpl # (Optional) Prompt default
|
||||
options: # (Optional) Set of options the user can choose from
|
||||
- coolproject123
|
||||
- ${USER}'s cool project
|
||||
`
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"go.jolheiser.com/tmpl/schema"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
func TestInitSchema(t *testing.T) {
|
||||
assert := is.New(t)
|
||||
|
||||
err := schema.Lint(strings.NewReader(initConfig))
|
||||
assert.NoErr(err) // Init config should conform to schema
|
||||
}
|
|
@ -16,10 +16,11 @@ type Config struct {
|
|||
|
||||
// Prompt is a tmpl prompt
|
||||
type Prompt struct {
|
||||
ID string `yaml:"id"`
|
||||
Label string `yaml:"label"`
|
||||
Help string `yaml:"help"`
|
||||
Default any `yaml:"default"`
|
||||
ID string `yaml:"id"`
|
||||
Label string `yaml:"label"`
|
||||
Help string `yaml:"help"`
|
||||
Default string `yaml:"default"`
|
||||
Options []string `yaml:"options"`
|
||||
}
|
||||
|
||||
// Load loads a tmpl config
|
||||
|
|
|
@ -10,11 +10,6 @@ func (e ErrTemplateExists) Error() string {
|
|||
return fmt.Sprintf("template %s already exists", e.Name)
|
||||
}
|
||||
|
||||
func IsErrTemplateExists(err error) bool {
|
||||
_, ok := err.(ErrTemplateExists)
|
||||
return ok
|
||||
}
|
||||
|
||||
type ErrTemplateNotFound struct {
|
||||
Name string
|
||||
}
|
||||
|
@ -30,8 +25,3 @@ type ErrSourceNotFound struct {
|
|||
func (e ErrSourceNotFound) Error() string {
|
||||
return fmt.Sprintf("Source not found for %s", e.Name)
|
||||
}
|
||||
|
||||
func IsErrSourceNotFound(err error) bool {
|
||||
_, ok := err.(ErrSourceNotFound)
|
||||
return ok
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
)
|
||||
|
||||
var funcMap = map[string]any{
|
||||
// String conversions
|
||||
"upper": strings.ToUpper,
|
||||
"lower": strings.ToLower,
|
||||
"title": strings.Title,
|
||||
|
@ -20,15 +19,10 @@ var funcMap = map[string]any{
|
|||
"camel": func(in string) string {
|
||||
return xstrings.FirstRuneToLower(xstrings.ToCamelCase(in))
|
||||
},
|
||||
"trim_prefix": func(in, trim string) string {
|
||||
return strings.TrimPrefix(in, trim)
|
||||
},
|
||||
"trim_suffix": func(in, trim string) string {
|
||||
return strings.TrimSuffix(in, trim)
|
||||
},
|
||||
|
||||
// Other
|
||||
"env": os.Getenv,
|
||||
"trim_prefix": strings.TrimPrefix,
|
||||
"trim_suffix": strings.TrimSuffix,
|
||||
"replace": strings.ReplaceAll,
|
||||
"env": os.Getenv,
|
||||
"sep": func() string {
|
||||
return string(filepath.Separator)
|
||||
},
|
||||
|
|
|
@ -33,9 +33,6 @@ func prompt(dir string, defaults bool) (templatePrompts, error) {
|
|||
if tp.Label == "" {
|
||||
tp.Label = tp.ID
|
||||
}
|
||||
if tp.Default == nil {
|
||||
tp.Default = ""
|
||||
}
|
||||
prompts = append(prompts, tp)
|
||||
}
|
||||
|
||||
|
@ -50,52 +47,32 @@ func prompt(dir string, defaults bool) (templatePrompts, error) {
|
|||
|
||||
// Check if we are using defaults
|
||||
if defaults {
|
||||
val := prompt.Default
|
||||
switch t := prompt.Default.(type) {
|
||||
case []string:
|
||||
for idy, s := range t {
|
||||
t[idy] = os.ExpandEnv(s)
|
||||
}
|
||||
val = t
|
||||
case string:
|
||||
val = os.ExpandEnv(t)
|
||||
}
|
||||
s := fmt.Sprint(val)
|
||||
prompts[idx].Value = s
|
||||
os.Setenv(fmt.Sprintf("TMPL_PROMPT_%s", envKey), s)
|
||||
val := os.ExpandEnv(prompt.Default)
|
||||
prompts[idx].Value = val
|
||||
os.Setenv(fmt.Sprintf("TMPL_PROMPT_%s", envKey), val)
|
||||
continue
|
||||
}
|
||||
|
||||
// Otherwise, prompt
|
||||
var p survey.Prompt
|
||||
switch t := prompt.Default.(type) {
|
||||
case []string:
|
||||
for idy, s := range t {
|
||||
t[idy] = os.ExpandEnv(s)
|
||||
if len(prompt.Options) > 0 {
|
||||
opts := make([]string, 0, len(prompt.Options))
|
||||
for idy, opt := range prompt.Options {
|
||||
opts[idy] = os.ExpandEnv(opt)
|
||||
}
|
||||
p = &survey.Select{
|
||||
Message: prompt.Label,
|
||||
Options: t,
|
||||
Options: opts,
|
||||
Help: prompt.Help,
|
||||
}
|
||||
case bool:
|
||||
p = &survey.Confirm{
|
||||
Message: prompt.Label,
|
||||
Default: t,
|
||||
Help: prompt.Help,
|
||||
}
|
||||
case string:
|
||||
} else {
|
||||
p = &survey.Input{
|
||||
Message: prompt.Label,
|
||||
Default: os.ExpandEnv(t),
|
||||
Help: prompt.Help,
|
||||
}
|
||||
default:
|
||||
p = &survey.Input{
|
||||
Message: prompt.Label,
|
||||
Default: fmt.Sprint(t),
|
||||
Default: os.ExpandEnv(prompt.Default),
|
||||
Help: prompt.Help,
|
||||
}
|
||||
}
|
||||
|
||||
var a string
|
||||
if err := survey.AskOne(p, &a); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package schema
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
)
|
||||
|
||||
//go:embed testdata
|
||||
var testdata embed.FS
|
||||
|
||||
func TestSchema(t *testing.T) {
|
||||
tt := []struct {
|
||||
Name string
|
||||
NumErr int
|
||||
}{
|
||||
{Name: "good", NumErr: 0},
|
||||
{Name: "bad", NumErr: 10},
|
||||
{Name: "empty", NumErr: 1},
|
||||
}
|
||||
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
assert := is.New(t)
|
||||
|
||||
fi, err := testdata.Open(fmt.Sprintf("testdata/%s.yaml", tc.Name))
|
||||
assert.NoErr(err) // Should open test file
|
||||
|
||||
err = Lint(fi)
|
||||
if tc.NumErr > 0 {
|
||||
var rerrs ResultErrors
|
||||
assert.True(errors.As(err, &rerrs)) // Error should be ResultErrors
|
||||
assert.True(len(rerrs) == tc.NumErr) // Number of errors should match test case
|
||||
} else {
|
||||
assert.NoErr(err) // Good schemas shouldn't return errors
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
prompts:
|
||||
- label: Bar
|
||||
default: baz
|
||||
help: |
|
||||
This is a foobar!
|
||||
options:
|
||||
- "1"
|
||||
- bonk
|
||||
- "false"
|
||||
- id: test
|
||||
label: 1234
|
||||
- id: test123
|
||||
options: []
|
||||
- label: 1234
|
||||
default: false
|
||||
help: # nil
|
||||
options:
|
||||
- 1
|
||||
- 2
|
||||
- true
|
|
@ -0,0 +1,10 @@
|
|||
prompts:
|
||||
- id: foo
|
||||
label: Bar
|
||||
default: baz
|
||||
help: |
|
||||
This is a foobar!
|
||||
options:
|
||||
- "1"
|
||||
- bonk
|
||||
- "false"
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://git.jojodev.com/jolheiser/tmpl/src/branch/main/schema/tmpl.json",
|
||||
"$id": "https://git.jojodev.com/jolheiser/tmpl/raw/branch/main/schema/tmpl.json",
|
||||
"title": "tmpl template",
|
||||
"description": "A template for tmpl",
|
||||
"type": "object",
|
||||
|
@ -28,16 +28,16 @@
|
|||
"description": "A label to show instead of the ID when prompting",
|
||||
"type": "string"
|
||||
},
|
||||
"default": {
|
||||
"description": "A default value for the prompt",
|
||||
"type": "string"
|
||||
},
|
||||
"help": {
|
||||
"description": "A help message for more information on a prompt",
|
||||
"type": "string"
|
||||
},
|
||||
"depends_on": {
|
||||
"description": "A list of prompt IDs that this prompt depends on",
|
||||
"default": {
|
||||
"description": "A default value for the prompt",
|
||||
"type": "string"
|
||||
},
|
||||
"options": {
|
||||
"description": "A set of options for this prompt",
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
|
|
Loading…
Reference in New Issue