Package stout (STream OUTput): writing byte streams in a type-safe and extensible way.
Package stout
(STream OUTput): writing byte streams in a type-safe and extensible way.
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:
The core ideas behind the implementation of the package are described in this blog post.
func main() {
_, err := stout.WriterBufferedStream(os.Stdout).Write(
stout.String("Hello, "),
stout.String(os.Getenv("USER")),
stout.String("!\n"),
)
if err != nil {
println("ERROR:", err.Error())
os.Exit(1)
}
}
cat
command:
func main() {
files := make([]stout.Chunk, 0, len(os.Args)-1)
for _, arg := range os.Args[1:] {
files = append(files, stout.File(arg))
}
_, err := stout.WriterBufferedStream(os.Stdout).Write(files...)
if err != nil {
println("ERROR:", err.Error())
os.Exit(1)
}
}
A complete application that generates a simple HTML page from ps
command output:
func main() {
// run "ps" command
lines, err := ps()
if err != nil {
die(err.Error())
}
// compose HTML
// note: in a real-life application we would be writing to a socket instead of stdout
_, err = stout.WriterBufferedStream(os.Stdout).Write(
stout.String(hdr),
stout.Repeat(rowsFrom(lines)),
stout.String("</table></body></html>"),
)
if err != nil {
die(err.Error())
}
}
func rowsFrom(lines [][]byte) func(int, *stout.Writer) (int64, error) {
return func(i int, w *stout.Writer) (int64, error) {
// iteration stop
if i >= len(lines) {
return 0, io.EOF
}
// get the i-th record
fields := bytes.Fields(lines[i])
if len(fields) < 4 {
return 0, io.EOF
}
// compose and write one row
return w.WriteChunks([]stout.Chunk{
stout.String("<tr><td>"),
stout.Join("</td><td>",
stout.ByteSlice(fields[0]), // pid
stout.ByteSlice(fields[1]), // %cpu
stout.ByteSlice(fields[2]), // %mem
stout.String(html.EscapeString(string(fields[3]))), // cmd
),
stout.String("</td></tr>"),
})
}
}
func ps() (lines [][]byte, err error) {
var buff bytes.Buffer
// get the output of the command into the buffer
_, err = stout.ByteBufferStream(&buff).Write(
stout.Command("ps", "--no-headers", "-Ao", "pid,%cpu,%mem,cmd"),
)
if err == nil {
lines = bytes.Split(buff.Bytes(), []byte{'\n'})
}
return
}
func die(msg string) {
println("error:", msg)
os.Exit(1)
}
const hdr = `<!DOCTYPE html>
<html><head><meta charset="UTF-8"></head>
<body><table>
<tr><th>pid</th><th>cpu</th><th>mem</th><th>cmd</th>`
Tested on Linux Mint 20.2 with Go version 1.16.6.