项目作者: maxim2266

项目描述 :
Package stout (STream OUTput): writing byte streams in a type-safe and extensible way.
高级语言: Go
项目地址: git://github.com/maxim2266/stout.git
创建时间: 2019-11-17T21:05:25Z
项目社区:https://github.com/maxim2266/stout

开源协议:BSD 3-Clause "New" or "Revised" License

下载


stout

GoDoc
Go Report Card
License: BSD 3 Clause

Package stout (STream OUTput): writing byte streams in a type-safe and extensible way.

Motivation

In Go, writing files or other byte streams (pipes, sockets, etc.) is where all that error-checking
boilerplate code pops up, obscuring program logic and making the source code harder to read.
This project is an attempt to improve the situation. The main goals of the project are:

  • Reduce the amount of error-checking boilerplate code;
  • Make writing multi-part byte streams look more like a declarative composition, as much as possible with Go syntax;
  • Develop an API that can be easily extended for any custom type or operation;
  • Provide some generally useful functions for real-life applications.

The core ideas behind the implementation of the package are described in this blog post.

Examples

“Hello, user” application:
  1. func main() {
  2. _, err := stout.WriterBufferedStream(os.Stdout).Write(
  3. stout.String("Hello, "),
  4. stout.String(os.Getenv("USER")),
  5. stout.String("!\n"),
  6. )
  7. if err != nil {
  8. println("ERROR:", err.Error())
  9. os.Exit(1)
  10. }
  11. }
Simplified implementation of cat command:
  1. func main() {
  2. files := make([]stout.Chunk, 0, len(os.Args)-1)
  3. for _, arg := range os.Args[1:] {
  4. files = append(files, stout.File(arg))
  5. }
  6. _, err := stout.WriterBufferedStream(os.Stdout).Write(files...)
  7. if err != nil {
  8. println("ERROR:", err.Error())
  9. os.Exit(1)
  10. }
  11. }
Report generator

A complete application that generates a simple HTML page from ps command output:

  1. func main() {
  2. // run "ps" command
  3. lines, err := ps()
  4. if err != nil {
  5. die(err.Error())
  6. }
  7. // compose HTML
  8. // note: in a real-life application we would be writing to a socket instead of stdout
  9. _, err = stout.WriterBufferedStream(os.Stdout).Write(
  10. stout.String(hdr),
  11. stout.Repeat(rowsFrom(lines)),
  12. stout.String("</table></body></html>"),
  13. )
  14. if err != nil {
  15. die(err.Error())
  16. }
  17. }
  18. func rowsFrom(lines [][]byte) func(int, *stout.Writer) (int64, error) {
  19. return func(i int, w *stout.Writer) (int64, error) {
  20. // iteration stop
  21. if i >= len(lines) {
  22. return 0, io.EOF
  23. }
  24. // get the i-th record
  25. fields := bytes.Fields(lines[i])
  26. if len(fields) < 4 {
  27. return 0, io.EOF
  28. }
  29. // compose and write one row
  30. return w.WriteChunks([]stout.Chunk{
  31. stout.String("<tr><td>"),
  32. stout.Join("</td><td>",
  33. stout.ByteSlice(fields[0]), // pid
  34. stout.ByteSlice(fields[1]), // %cpu
  35. stout.ByteSlice(fields[2]), // %mem
  36. stout.String(html.EscapeString(string(fields[3]))), // cmd
  37. ),
  38. stout.String("</td></tr>"),
  39. })
  40. }
  41. }
  42. func ps() (lines [][]byte, err error) {
  43. var buff bytes.Buffer
  44. // get the output of the command into the buffer
  45. _, err = stout.ByteBufferStream(&buff).Write(
  46. stout.Command("ps", "--no-headers", "-Ao", "pid,%cpu,%mem,cmd"),
  47. )
  48. if err == nil {
  49. lines = bytes.Split(buff.Bytes(), []byte{'\n'})
  50. }
  51. return
  52. }
  53. func die(msg string) {
  54. println("error:", msg)
  55. os.Exit(1)
  56. }
  57. const hdr = `<!DOCTYPE html>
  58. <html><head><meta charset="UTF-8"></head>
  59. <body><table>
  60. <tr><th>pid</th><th>cpu</th><th>mem</th><th>cmd</th>`

Status

Tested on Linux Mint 20.2 with Go version 1.16.6.