cuelang.org/go@v0.10.1/pkg/tool/cli/cli.go (about) 1 // Copyright 2019 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cli 16 17 import ( 18 "bufio" 19 "fmt" 20 "io" 21 "strings" 22 23 "cuelang.org/go/cue" 24 "cuelang.org/go/internal/task" 25 ) 26 27 func init() { 28 task.Register("tool/cli.Print", newPrintCmd) 29 task.Register("tool/cli.Ask", newAskCmd) 30 31 // For backwards compatibility. 32 task.Register("print", newPrintCmd) 33 } 34 35 type printCmd struct{} 36 37 func newPrintCmd(v cue.Value) (task.Runner, error) { 38 return &printCmd{}, nil 39 } 40 41 func (c *printCmd) Run(ctx *task.Context) (res interface{}, err error) { 42 str := ctx.String("text") 43 if ctx.Err != nil { 44 return nil, ctx.Err 45 } 46 fmt.Fprintln(ctx.Stdout, str) 47 return nil, nil 48 } 49 50 type askCmd struct{} 51 52 func newAskCmd(v cue.Value) (task.Runner, error) { 53 return &askCmd{}, nil 54 } 55 56 type oneByteReader struct { 57 r io.Reader 58 } 59 60 func (r *oneByteReader) Read(p []byte) (int, error) { 61 if len(p) == 0 { 62 return 0, nil 63 } 64 return r.r.Read(p[:1]) 65 } 66 67 func (c *askCmd) Run(ctx *task.Context) (res interface{}, err error) { 68 str := ctx.String("prompt") 69 if ctx.Err != nil { 70 return nil, ctx.Err 71 } 72 if str != "" { 73 fmt.Fprint(ctx.Stdout, str+" ") 74 } 75 76 // Roger is convinced that bufio.Scanner will only issue as many reads 77 // as it needs, so that limiting it to one-byte reads should be enough 78 // to not read any bytes after a newline. 79 // This behavior is true today but technically not documented, 80 // so Roger will send a CL to properly document it. 81 // 82 // TODO(mvdan): come back to remove this notice once Roger's CL is 83 // approved, or to rewrite the code if it is rejected. 84 scanner := bufio.NewScanner(&oneByteReader{ctx.Stdin}) 85 var response string 86 if scanner.Scan() { 87 response = scanner.Text() 88 } 89 if err := scanner.Err(); err != nil { 90 return nil, err 91 } 92 93 update := map[string]interface{}{"response": response} 94 95 switch v := ctx.Lookup("response"); v.IncompleteKind() { 96 case cue.BoolKind: 97 update["response"] = strings.ToLower(response) == "yes" 98 case cue.StringKind: 99 // already set above 100 } 101 return update, nil 102 }