github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/eval/builtin_fn_io.go (about) 1 package eval 2 3 import ( 4 "bufio" 5 "encoding/json" 6 "fmt" 7 "io" 8 "strconv" 9 "strings" 10 11 "github.com/markusbkk/elvish/pkg/diag" 12 "github.com/markusbkk/elvish/pkg/eval/errs" 13 "github.com/markusbkk/elvish/pkg/eval/vals" 14 "github.com/markusbkk/elvish/pkg/parse" 15 "github.com/markusbkk/elvish/pkg/strutil" 16 ) 17 18 // Input and output. 19 20 func init() { 21 addBuiltinFns(map[string]interface{}{ 22 // Value output 23 "put": put, 24 "repeat": repeat, 25 26 // Bytes input 27 "read-upto": readUpto, 28 "read-line": readLine, 29 30 // Bytes output 31 "print": print, 32 "echo": echo, 33 "pprint": pprint, 34 "repr": repr, 35 "show": show, 36 "printf": printf, 37 38 // Only bytes or values 39 // 40 // These are now implemented as commands forwarding one part of input to 41 // output and discarding the other. A future optimization the evaler can 42 // do is to connect the relevant parts directly together without any 43 // kind of forwarding. 44 "only-bytes": onlyBytes, 45 "only-values": onlyValues, 46 47 // Bytes to value 48 "slurp": slurp, 49 "from-lines": fromLines, 50 "from-json": fromJSON, 51 "from-terminated": fromTerminated, 52 53 // Value to bytes 54 "to-lines": toLines, 55 "to-json": toJSON, 56 "to-terminated": toTerminated, 57 }) 58 } 59 60 //elvdoc:fn put 61 // 62 // ```elvish 63 // put $value... 64 // ``` 65 // 66 // Takes arbitrary arguments and write them to the structured stdout. 67 // 68 // Examples: 69 // 70 // ```elvish-transcript 71 // ~> put a 72 // ▶ a 73 // ~> put lorem ipsum [a b] { ls } 74 // ▶ lorem 75 // ▶ ipsum 76 // ▶ [a b] 77 // ▶ <closure 0xc4202607e0> 78 // ``` 79 // 80 // **Note**: It is almost never necessary to use `put (...)` - just write the 81 // `...` part. For example, `put (eq a b)` is the equivalent to just `eq a b`. 82 // 83 // Etymology: Various languages, in particular 84 // [C](https://manpages.debian.org/stretch/manpages-dev/puts.3.en.html) and 85 // [Ruby](https://ruby-doc.org/core-2.2.2/IO.html#method-i-puts) as `puts`. 86 87 func put(fm *Frame, args ...interface{}) error { 88 out := fm.ValueOutput() 89 for _, a := range args { 90 err := out.Put(a) 91 if err != nil { 92 return err 93 } 94 } 95 return nil 96 } 97 98 //elvdoc:fn repeat 99 // 100 // ```elvish 101 // repeat $n $value 102 // ``` 103 // 104 // Output `$value` for `$n` times. Example: 105 // 106 // ```elvish-transcript 107 // ~> repeat 0 lorem 108 // ~> repeat 4 NAN 109 // ▶ NAN 110 // ▶ NAN 111 // ▶ NAN 112 // ▶ NAN 113 // ``` 114 // 115 // Etymology: [Clojure](https://clojuredocs.org/clojure.core/repeat). 116 117 func repeat(fm *Frame, n int, v interface{}) error { 118 out := fm.ValueOutput() 119 for i := 0; i < n; i++ { 120 err := out.Put(v) 121 if err != nil { 122 return err 123 } 124 } 125 return nil 126 } 127 128 //elvdoc:fn read-upto 129 // 130 // ```elvish 131 // read-upto $terminator 132 // ``` 133 // 134 // Reads byte input until `$terminator` or end-of-file is encountered. It outputs the part of the 135 // input read as a string value. The output contains the trailing `$terminator`, unless `read-upto` 136 // terminated at end-of-file. 137 // 138 // The `$terminator` must be a single ASCII character such as `"\x00"` (NUL). 139 // 140 // Examples: 141 // 142 // ```elvish-transcript 143 // ~> echo "a,b,c" | read-upto "," 144 // ▶ 'a,' 145 // ~> echo "foo\nbar" | read-upto "\n" 146 // ▶ "foo\n" 147 // ~> echo "a.elv\x00b.elv" | read-upto "\x00" 148 // ▶ "a.elv\x00" 149 // ~> print "foobar" | read-upto "\n" 150 // ▶ foobar 151 // ``` 152 153 func readUpto(fm *Frame, terminator string) (string, error) { 154 if err := checkTerminator(terminator); err != nil { 155 return "", err 156 } 157 in := fm.InputFile() 158 var buf []byte 159 for { 160 var b [1]byte 161 _, err := in.Read(b[:]) 162 if err != nil { 163 if err == io.EOF { 164 break 165 } 166 return "", err 167 } 168 buf = append(buf, b[0]) 169 if b[0] == terminator[0] { 170 break 171 } 172 } 173 return string(buf), nil 174 } 175 176 func checkTerminator(s string) error { 177 if len(s) != 1 || s[0] > 127 { 178 return errs.BadValue{What: "terminator", 179 Valid: "a single ASCII character", Actual: parse.Quote(s)} 180 } 181 return nil 182 } 183 184 //elvdoc:fn read-line 185 // 186 // ```elvish 187 // read-line 188 // ``` 189 // 190 // Reads a single line from byte input, and writes the line to the value output, 191 // stripping the line ending. A line can end with `"\r\n"`, `"\n"`, or end of 192 // file. Examples: 193 // 194 // ```elvish-transcript 195 // ~> print line | read-line 196 // ▶ line 197 // ~> print "line\n" | read-line 198 // ▶ line 199 // ~> print "line\r\n" | read-line 200 // ▶ line 201 // ~> print "line-with-extra-cr\r\r\n" | read-line 202 // ▶ "line-with-extra-cr\r" 203 // ``` 204 205 func readLine(fm *Frame) (string, error) { 206 s, err := readUpto(fm, "\n") 207 if err != nil { 208 return "", err 209 } 210 return strutil.ChopLineEnding(s), nil 211 } 212 213 //elvdoc:fn print 214 // 215 // ```elvish 216 // print &sep=' ' $value... 217 // ``` 218 // 219 // Like `echo`, just without the newline. 220 // 221 // @cf echo 222 // 223 // Etymology: Various languages, in particular 224 // [Perl](https://perldoc.perl.org/functions/print.html) and 225 // [zsh](http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html), whose 226 // `print`s do not print a trailing newline. 227 228 type printOpts struct{ Sep string } 229 230 func (o *printOpts) SetDefaultOptions() { o.Sep = " " } 231 232 func print(fm *Frame, opts printOpts, args ...interface{}) error { 233 out := fm.ByteOutput() 234 for i, arg := range args { 235 if i > 0 { 236 _, err := out.WriteString(opts.Sep) 237 if err != nil { 238 return err 239 } 240 } 241 _, err := out.WriteString(vals.ToString(arg)) 242 if err != nil { 243 return err 244 } 245 } 246 return nil 247 } 248 249 //elvdoc:fn printf 250 // 251 // ```elvish 252 // printf $template $value... 253 // ``` 254 // 255 // Prints values to the byte stream according to a template. If you need to inject the output into 256 // the value stream use this pattern: `printf .... | slurp`. That ensures that any newlines in the 257 // output of `printf` do not cause its output to be broken into multiple values, thus eliminating 258 // the newlines, which will occur if you do `put (printf ....)`. 259 // 260 // Like [`print`](#print), this command does not add an implicit newline; include an explicit `"\n"` 261 // in the formatting template instead. For example, `printf "%.1f\n" (/ 10.0 3)`. 262 // 263 // See Go's [`fmt`](https://golang.org/pkg/fmt/#hdr-Printing) package for 264 // details about the formatting verbs and the various flags that modify the 265 // default behavior, such as padding and justification. 266 // 267 // Unlike Go, each formatting verb has a single associated internal type, and 268 // accepts any argument that can reasonably be converted to that type: 269 // 270 // - The verbs `%s`, `%q` and `%v` convert the corresponding argument to a 271 // string in different ways: 272 // 273 // - `%s` uses [to-string](#to-string) to convert a value to string. 274 // 275 // - `%q` uses [repr](#repr) to convert a value to string. 276 // 277 // - `%v` is equivalent to `%s`, and `%#v` is equivalent to `%q`. 278 // 279 // - The verb `%t` first convert the corresponding argument to a boolean using 280 // [bool](#bool), and then uses its Go counterpart to format the boolean. 281 // 282 // - The verbs `%b`, `%c`, `%d`, `%o`, `%O`, `%x`, `%X` and `%U` first convert 283 // the corresponding argument to an integer using an internal algorithm, and 284 // use their Go counterparts to format the integer. 285 // 286 // - The verbs `%e`, `%E`, `%f`, `%F`, `%g` and `%G` first convert the 287 // corresponding argument to a floating-point number using 288 // [float64](#float64), and then use their Go counterparts to format the 289 // number. 290 // 291 // The special verb `%%` prints a literal `%` and consumes no argument. 292 // 293 // Verbs not documented above are not supported. 294 // 295 // Examples: 296 // 297 // ```elvish-transcript 298 // ~> printf "%10s %.2f\n" Pi $math:pi 299 // Pi 3.14 300 // ~> printf "%-10s %.2f %s\n" Pi $math:pi $math:pi 301 // Pi 3.14 3.141592653589793 302 // ~> printf "%d\n" 0b11100111 303 // 231 304 // ~> printf "%08b\n" 231 305 // 11100111 306 // ~> printf "list is: %q\n" [foo bar 'foo bar'] 307 // list is: [foo bar 'foo bar'] 308 // ``` 309 // 310 // **Note**: Compared to the [POSIX `printf` 311 // command](https://pubs.opengroup.org/onlinepubs/007908799/xcu/printf.html) 312 // found in other shells, there are 3 key differences: 313 // 314 // - The behavior of the formatting verbs are based on Go's 315 // [`fmt`](https://golang.org/pkg/fmt/) package instead of the POSIX 316 // specification. 317 // 318 // - The number of arguments after the formatting template must match the number 319 // of formatting verbs. The POSIX command will repeat the template string to 320 // consume excess values; this command does not have that behavior. 321 // 322 // - This command does not interpret escape sequences such as `\n`; just use 323 // [double-quoted strings](language.html#double-quoted-string). 324 // 325 // @cf print echo pprint repr 326 327 func printf(fm *Frame, template string, args ...interface{}) error { 328 wrappedArgs := make([]interface{}, len(args)) 329 for i, arg := range args { 330 wrappedArgs[i] = formatter{arg} 331 } 332 333 _, err := fmt.Fprintf(fm.ByteOutput(), template, wrappedArgs...) 334 return err 335 } 336 337 type formatter struct { 338 wrapped interface{} 339 } 340 341 func (f formatter) Format(state fmt.State, r rune) { 342 wrapped := f.wrapped 343 switch r { 344 case 's': 345 writeFmt(state, 's', vals.ToString(wrapped)) 346 case 'q': 347 // TODO: Support using the precision flag to specify indentation. 348 writeFmt(state, 's', vals.ReprPlain(wrapped)) 349 case 'v': 350 var s string 351 if state.Flag('#') { 352 s = vals.ReprPlain(wrapped) 353 } else { 354 s = vals.ToString(wrapped) 355 } 356 writeFmt(state, 's', s) 357 case 't': 358 writeFmt(state, 't', vals.Bool(wrapped)) 359 case 'b', 'c', 'd', 'o', 'O', 'x', 'X', 'U': 360 var i int 361 if err := vals.ScanToGo(wrapped, &i); err != nil { 362 fmt.Fprintf(state, "%%!%c(%s)", r, err.Error()) 363 return 364 } 365 writeFmt(state, r, i) 366 case 'e', 'E', 'f', 'F', 'g', 'G': 367 var f float64 368 if err := vals.ScanToGo(wrapped, &f); err != nil { 369 fmt.Fprintf(state, "%%!%c(%s)", r, err.Error()) 370 return 371 } 372 writeFmt(state, r, f) 373 default: 374 fmt.Fprintf(state, "%%!%c(unsupported formatting verb)", r) 375 } 376 } 377 378 // Writes to State using the flag it stores, but with a potentially different 379 // verb and value. 380 func writeFmt(state fmt.State, v rune, val interface{}) { 381 // Reconstruct the verb string. 382 var sb strings.Builder 383 sb.WriteRune('%') 384 for _, f := range "+-# 0" { 385 if state.Flag(int(f)) { 386 sb.WriteRune(f) 387 } 388 } 389 if w, ok := state.Width(); ok { 390 sb.WriteString(strconv.Itoa(w)) 391 } 392 if p, ok := state.Precision(); ok { 393 sb.WriteRune('.') 394 sb.WriteString(strconv.Itoa(p)) 395 } 396 sb.WriteRune(v) 397 398 fmt.Fprintf(state, sb.String(), val) 399 } 400 401 //elvdoc:fn echo 402 // 403 // ```elvish 404 // echo &sep=' ' $value... 405 // ``` 406 // 407 // Print all arguments, joined by the `sep` option, and followed by a newline. 408 // 409 // Examples: 410 // 411 // ```elvish-transcript 412 // ~> echo Hello elvish 413 // Hello elvish 414 // ~> echo "Hello elvish" 415 // Hello elvish 416 // ~> echo &sep=, lorem ipsum 417 // lorem,ipsum 418 // ``` 419 // 420 // Notes: The `echo` builtin does not treat `-e` or `-n` specially. For instance, 421 // `echo -n` just prints `-n`. Use double-quoted strings to print special 422 // characters, and `print` to suppress the trailing newline. 423 // 424 // @cf print 425 // 426 // Etymology: Bourne sh. 427 428 func echo(fm *Frame, opts printOpts, args ...interface{}) error { 429 err := print(fm, opts, args...) 430 if err != nil { 431 return err 432 } 433 _, err = fm.ByteOutput().WriteString("\n") 434 return err 435 } 436 437 //elvdoc:fn pprint 438 // 439 // ```elvish 440 // pprint $value... 441 // ``` 442 // 443 // Pretty-print representations of Elvish values. Examples: 444 // 445 // ```elvish-transcript 446 // ~> pprint [foo bar] 447 // [ 448 // foo 449 // bar 450 // ] 451 // ~> pprint [&k1=v1 &k2=v2] 452 // [ 453 // &k2= 454 // v2 455 // &k1= 456 // v1 457 // ] 458 // ``` 459 // 460 // The output format is subject to change. 461 // 462 // @cf repr 463 464 func pprint(fm *Frame, args ...interface{}) error { 465 out := fm.ByteOutput() 466 for _, arg := range args { 467 _, err := out.WriteString(vals.Repr(arg, 0)) 468 if err != nil { 469 return err 470 } 471 _, err = out.WriteString("\n") 472 if err != nil { 473 return err 474 } 475 } 476 return nil 477 } 478 479 //elvdoc:fn repr 480 // 481 // ```elvish 482 // repr $value... 483 // ``` 484 // 485 // Writes representation of `$value`s, separated by space and followed by a 486 // newline. Example: 487 // 488 // ```elvish-transcript 489 // ~> repr [foo 'lorem ipsum'] "aha\n" 490 // [foo 'lorem ipsum'] "aha\n" 491 // ``` 492 // 493 // @cf pprint 494 // 495 // Etymology: [Python](https://docs.python.org/3/library/functions.html#repr). 496 497 func repr(fm *Frame, args ...interface{}) error { 498 out := fm.ByteOutput() 499 for i, arg := range args { 500 if i > 0 { 501 _, err := out.WriteString(" ") 502 if err != nil { 503 return err 504 } 505 } 506 _, err := out.WriteString(vals.ReprPlain(arg)) 507 if err != nil { 508 return err 509 } 510 } 511 _, err := out.WriteString("\n") 512 return err 513 } 514 515 //elvdoc:fn show 516 // 517 // ```elvish 518 // show $e 519 // ``` 520 // 521 // Shows the value to the output, which is assumed to be a VT-100-compatible 522 // terminal. 523 // 524 // Currently, the only type of value that can be showed is exceptions, but this 525 // will likely expand in future. 526 // 527 // Example: 528 // 529 // ```elvish-transcript 530 // ~> var e = ?(fail lorem-ipsum) 531 // ~> show $e 532 // Exception: lorem-ipsum 533 // [tty 3], line 1: var e = ?(fail lorem-ipsum) 534 // ``` 535 536 func show(fm *Frame, v diag.Shower) error { 537 out := fm.ByteOutput() 538 _, err := out.WriteString(v.Show("")) 539 if err != nil { 540 return err 541 } 542 _, err = out.WriteString("\n") 543 return err 544 } 545 546 //elvdoc:fn only-bytes 547 // 548 // ```elvish 549 // only-bytes 550 // ``` 551 // 552 // Passes byte input to output, and discards value inputs. 553 // 554 // Example: 555 // 556 // ```elvish-transcript 557 // ~> { put value; echo bytes } | only-bytes 558 // bytes 559 // ``` 560 561 func onlyBytes(fm *Frame) error { 562 // Discard values in a goroutine. 563 valuesDone := make(chan struct{}) 564 go func() { 565 for range fm.InputChan() { 566 } 567 close(valuesDone) 568 }() 569 // Make sure the goroutine has finished before returning. 570 defer func() { <-valuesDone }() 571 572 _, err := io.Copy(fm.ByteOutput(), fm.InputFile()) 573 return err 574 } 575 576 //elvdoc:fn only-values 577 // 578 // ```elvish 579 // only-values 580 // ``` 581 // 582 // Passes value input to output, and discards byte inputs. 583 // 584 // Example: 585 // 586 // ```elvish-transcript 587 // ~> { put value; echo bytes } | only-values 588 // ▶ value 589 // ``` 590 591 func onlyValues(fm *Frame) error { 592 // Discard bytes in a goroutine. 593 bytesDone := make(chan struct{}) 594 go func() { 595 // Ignore the error 596 _, _ = io.Copy(blackholeWriter{}, fm.InputFile()) 597 close(bytesDone) 598 }() 599 // Wait for the goroutine to finish before returning. 600 defer func() { <-bytesDone }() 601 602 // Forward values. 603 out := fm.ValueOutput() 604 for v := range fm.InputChan() { 605 err := out.Put(v) 606 if err != nil { 607 return err 608 } 609 } 610 return nil 611 } 612 613 type blackholeWriter struct{} 614 615 func (blackholeWriter) Write(p []byte) (int, error) { return len(p), nil } 616 617 //elvdoc:fn slurp 618 // 619 // ```elvish 620 // slurp 621 // ``` 622 // 623 // Reads bytes input into a single string, and put this string on structured 624 // stdout. 625 // 626 // Example: 627 // 628 // ```elvish-transcript 629 // ~> echo "a\nb" | slurp 630 // ▶ "a\nb\n" 631 // ``` 632 // 633 // Etymology: Perl, as 634 // [`File::Slurp`](http://search.cpan.org/~uri/File-Slurp-9999.19/lib/File/Slurp.pm). 635 636 func slurp(fm *Frame) (string, error) { 637 b, err := io.ReadAll(fm.InputFile()) 638 return string(b), err 639 } 640 641 //elvdoc:fn from-lines 642 // 643 // ```elvish 644 // from-lines 645 // ``` 646 // 647 // Splits byte input into lines, and writes them to the value output. Value 648 // input is ignored. 649 // 650 // ```elvish-transcript 651 // ~> { echo a; echo b } | from-lines 652 // ▶ a 653 // ▶ b 654 // ~> { echo a; put b } | from-lines 655 // ▶ a 656 // ``` 657 // 658 // @cf from-terminated read-upto to-lines 659 660 func fromLines(fm *Frame) error { 661 filein := bufio.NewReader(fm.InputFile()) 662 out := fm.ValueOutput() 663 for { 664 line, err := filein.ReadString('\n') 665 if line != "" { 666 err := out.Put(strutil.ChopLineEnding(line)) 667 if err != nil { 668 return err 669 } 670 } 671 if err != nil { 672 if err != io.EOF { 673 return err 674 } 675 return nil 676 } 677 } 678 } 679 680 //elvdoc:fn from-json 681 // 682 // ```elvish 683 // from-json 684 // ``` 685 // 686 // Takes bytes stdin, parses it as JSON and puts the result on structured stdout. 687 // The input can contain multiple JSONs, and whitespace between them are ignored. 688 // 689 // Note that JSON's only number type corresponds to Elvish's floating-point 690 // number type, and is always considered [inexact](language.html#exactness). 691 // It may be necessary to coerce JSON numbers to exact numbers using 692 // [exact-num](#exact-num). 693 // 694 // Examples: 695 // 696 // ```elvish-transcript 697 // ~> echo '"a"' | from-json 698 // ▶ a 699 // ~> echo '["lorem", "ipsum"]' | from-json 700 // ▶ [lorem ipsum] 701 // ~> echo '{"lorem": "ipsum"}' | from-json 702 // ▶ [&lorem=ipsum] 703 // ~> # multiple JSONs running together 704 // echo '"a""b"["x"]' | from-json 705 // ▶ a 706 // ▶ b 707 // ▶ [x] 708 // ~> # multiple JSONs separated by newlines 709 // echo '"a" 710 // {"k": "v"}' | from-json 711 // ▶ a 712 // ▶ [&k=v] 713 // ``` 714 // 715 // @cf to-json 716 717 func fromJSON(fm *Frame) error { 718 in := fm.InputFile() 719 out := fm.ValueOutput() 720 721 dec := json.NewDecoder(in) 722 for { 723 var v interface{} 724 err := dec.Decode(&v) 725 if err != nil { 726 if err == io.EOF { 727 return nil 728 } 729 return err 730 } 731 converted, err := fromJSONInterface(v) 732 if err != nil { 733 return err 734 } 735 err = out.Put(converted) 736 if err != nil { 737 return err 738 } 739 } 740 } 741 742 // Converts a interface{} that results from json.Unmarshal to an Elvish value. 743 func fromJSONInterface(v interface{}) (interface{}, error) { 744 switch v := v.(type) { 745 case nil, bool, string: 746 return v, nil 747 case float64: 748 return v, nil 749 case []interface{}: 750 vec := vals.EmptyList 751 for _, elem := range v { 752 converted, err := fromJSONInterface(elem) 753 if err != nil { 754 return nil, err 755 } 756 vec = vec.Conj(converted) 757 } 758 return vec, nil 759 case map[string]interface{}: 760 m := vals.EmptyMap 761 for key, val := range v { 762 convertedVal, err := fromJSONInterface(val) 763 if err != nil { 764 return nil, err 765 } 766 m = m.Assoc(key, convertedVal) 767 } 768 return m, nil 769 default: 770 return nil, fmt.Errorf("unexpected json type: %T", v) 771 } 772 } 773 774 //elvdoc:fn from-terminated 775 // 776 // ```elvish 777 // from-terminated $terminator 778 // ``` 779 // 780 // Splits byte input into lines at each `$terminator` character, and writes 781 // them to the value output. If the byte input ends with `$terminator`, it is 782 // dropped. Value input is ignored. 783 // 784 // The `$terminator` must be a single ASCII character such as `"\x00"` (NUL). 785 // 786 // ```elvish-transcript 787 // ~> { echo a; echo b } | from-terminated "\x00" 788 // ▶ "a\nb\n" 789 // ~> print "a\x00b" | from-terminated "\x00" 790 // ▶ a 791 // ▶ b 792 // ~> print "a\x00b\x00" | from-terminated "\x00" 793 // ▶ a 794 // ▶ b 795 // ``` 796 // 797 // @cf from-lines read-upto to-terminated 798 799 func fromTerminated(fm *Frame, terminator string) error { 800 if err := checkTerminator(terminator); err != nil { 801 return err 802 } 803 804 filein := bufio.NewReader(fm.InputFile()) 805 out := fm.ValueOutput() 806 for { 807 line, err := filein.ReadString(terminator[0]) 808 if line != "" { 809 err := out.Put(strutil.ChopTerminator(line, terminator[0])) 810 if err != nil { 811 return err 812 } 813 } 814 if err != nil { 815 if err != io.EOF { 816 logger.Println("error on reading:", err) 817 return err 818 } 819 return nil 820 } 821 } 822 } 823 824 //elvdoc:fn to-lines 825 // 826 // ```elvish 827 // to-lines $inputs? 828 // ``` 829 // 830 // Writes each [value input](#value-inputs) to a separate line in the byte 831 // output. Byte input is ignored. 832 // 833 // ```elvish-transcript 834 // ~> put a b | to-lines 835 // a 836 // b 837 // ~> to-lines [a b] 838 // a 839 // b 840 // ~> { put a; echo b } | to-lines 841 // b 842 // a 843 // ``` 844 // 845 // @cf from-lines to-terminated 846 847 func toLines(fm *Frame, inputs Inputs) error { 848 out := fm.ByteOutput() 849 var errOut error 850 851 inputs(func(v interface{}) { 852 if errOut != nil { 853 return 854 } 855 // TODO: Don't ignore the error. 856 _, errOut = fmt.Fprintln(out, vals.ToString(v)) 857 }) 858 return errOut 859 } 860 861 //elvdoc:fn to-terminated 862 // 863 // ```elvish 864 // to-terminated $terminator $inputs? 865 // ``` 866 // 867 // Writes each [value input](#value-inputs) to the byte output with the 868 // specified terminator character. Byte input is ignored. This behavior is 869 // useful, for example, when feeding output into a program that accepts NUL 870 // terminated lines to avoid ambiguities if the values contains newline 871 // characters. 872 // 873 // The `$terminator` must be a single ASCII character such as `"\x00"` (NUL). 874 // 875 // ```elvish-transcript 876 // ~> put a b | to-terminated "\x00" | slurp 877 // ▶ "a\x00b\x00" 878 // ~> to-terminated "\x00" [a b] | slurp 879 // ▶ "a\x00b\x00" 880 // ``` 881 // 882 // @cf from-terminated to-lines 883 884 func toTerminated(fm *Frame, terminator string, inputs Inputs) error { 885 if err := checkTerminator(terminator); err != nil { 886 return err 887 } 888 889 out := fm.ByteOutput() 890 var errOut error 891 inputs(func(v interface{}) { 892 if errOut != nil { 893 return 894 } 895 _, errOut = fmt.Fprint(out, vals.ToString(v), terminator) 896 }) 897 return errOut 898 } 899 900 //elvdoc:fn to-json 901 // 902 // ```elvish 903 // to-json 904 // ``` 905 // 906 // Takes structured stdin, convert it to JSON and puts the result on bytes stdout. 907 // 908 // ```elvish-transcript 909 // ~> put a | to-json 910 // "a" 911 // ~> put [lorem ipsum] | to-json 912 // ["lorem","ipsum"] 913 // ~> put [&lorem=ipsum] | to-json 914 // {"lorem":"ipsum"} 915 // ``` 916 // 917 // @cf from-json 918 919 func toJSON(fm *Frame, inputs Inputs) error { 920 encoder := json.NewEncoder(fm.ByteOutput()) 921 922 var errEncode error 923 inputs(func(v interface{}) { 924 if errEncode != nil { 925 return 926 } 927 errEncode = encoder.Encode(v) 928 }) 929 return errEncode 930 }