mirror of
https://github.com/therootcompany/golib.git
synced 2025-10-12 20:18:16 +00:00
feat(transform): maintenance fork of github.com/tidwall/transform
This commit is contained in:
parent
f3828d2037
commit
aaa7942bef
15
3p/transform/LICENSE
Normal file
15
3p/transform/LICENSE
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
|
Copyright (c) 2017, Joshua J Baker
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
|
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
226
3p/transform/README.md
Normal file
226
3p/transform/README.md
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
# Transform
|
||||||
|
|
||||||
|
[](https://pkg.go.dev/github.com/therootcompany/golib/3p/transform)
|
||||||
|
|
||||||
|
Transform is a Go package that provides a simple pattern for performing [chainable](#chaining) data transformations on streams of bytes. It conforms to the [io.Reader](https://golang.org/pkg/io/#Reader) interface and is useful for operations such as converting data formats, audio/video resampling, image transforms, log filters, regex line matching, etc.
|
||||||
|
|
||||||
|
The [transutil package](#transutil-package) provides few examples that work with JSON such as `JSONToMsgPack`, `MsgPackToJSON`, `JSONToPrettyJSON`, `JSONToUglyJSON`, `JSONToProtoBuf`, and `ProtoBufToJSON`. It also includes a handy `Gzipper` and `Gunzipper`.
|
||||||
|
|
||||||
|
# Getting Started
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
To start using Transform, install Go and run `go get`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get -u github.com/therootcompany/golib/3p/transform
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using
|
||||||
|
|
||||||
|
Below are a few very simple examples of custom transformers.
|
||||||
|
|
||||||
|
### ToUpper
|
||||||
|
|
||||||
|
Convert a string to uppper case. Unicode aware. In this example
|
||||||
|
we only process one rune at a time.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func ToUpper(r io.Reader) io.Reader {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
return transform.NewTransformer(func() ([]byte, error) {
|
||||||
|
c, _, err := br.ReadRune()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(strings.ToUpper(string([]rune{c}))), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
msg := "Hello World"
|
||||||
|
data, err := ioutil.ReadAll(ToUpper(bytes.NewBufferString(msg)))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(data))
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
HELLO WORLD
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rot13
|
||||||
|
|
||||||
|
The [Rot13](https://en.wikipedia.org/wiki/ROT13) cipher.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Rot13(r io.Reader) io.Reader {
|
||||||
|
buf := make([]byte, 256)
|
||||||
|
return transform.NewTransformer(func() ([]byte, error) {
|
||||||
|
n, err := r.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if buf[i] >= 'a' && buf[i] <= 'z' {
|
||||||
|
buf[i] = ((buf[i] - 'a' + 13) % 26) + 'a'
|
||||||
|
} else if buf[i] >= 'A' && buf[i] <= 'Z' {
|
||||||
|
buf[i] = ((buf[i] - 'A' + 13) % 26) + 'A'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf[:n], nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
msg := "Hello World"
|
||||||
|
data, err := ioutil.ReadAll(Rot13(bytes.NewBufferString(msg)))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(data))
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Uryyb Jbeyq
|
||||||
|
```
|
||||||
|
|
||||||
|
### RegExp Line Matcher
|
||||||
|
|
||||||
|
A line reader that filters lines that match on a RegExp pattern.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func LineMatch(r io.Reader, pattern string) io.Reader {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
return NewTransformer(func() ([]byte, error) {
|
||||||
|
for {
|
||||||
|
line, err := br.ReadBytes('\n')
|
||||||
|
matched, _ := regexp.Match(pattern, line)
|
||||||
|
if matched {
|
||||||
|
return line, err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
logs := `
|
||||||
|
23 Apr 17:32:23.604 [INFO] DB loaded in 0.551 seconds
|
||||||
|
23 Apr 17:32:23.605 [WARN] Disk space is low
|
||||||
|
23 Apr 17:32:23.054 [INFO] Server started on port 7812
|
||||||
|
23 Apr 17:32:23.141 [INFO] Ready for connections
|
||||||
|
`
|
||||||
|
data, err := ioutil.ReadAll(LineMatch(bytes.NewBufferString(logs), "WARN"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(data))
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
23 Apr 17:32:23.605 [WARN] Disk space is low
|
||||||
|
```
|
||||||
|
|
||||||
|
### LineTrimSpace
|
||||||
|
|
||||||
|
A line reader that trims the spaces from all lines.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func LineTrimSpace(r io.Reader, pattern string) io.Reader {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
return transform.NewTransformer(func() ([]byte, error) {
|
||||||
|
for {
|
||||||
|
line, err := br.ReadBytes('\n')
|
||||||
|
if len(line) > 0 {
|
||||||
|
line = append(bytes.TrimSpace(line), '\n')
|
||||||
|
}
|
||||||
|
return line, err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
phrases := " lacy timber \n"
|
||||||
|
phrases += "\t\thybrid gossiping\t\n"
|
||||||
|
phrases += " coy radioactivity\n"
|
||||||
|
phrases += "rocky arrow \n"
|
||||||
|
out, err := ioutil.ReadAll(LineTrimSpace(bytes.NewBufferString(phrases)))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("%s\n", out)
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
lacy timber
|
||||||
|
hybrid gossiping
|
||||||
|
coy radioactivity
|
||||||
|
rocky arrow
|
||||||
|
```
|
||||||
|
|
||||||
|
### Chaining
|
||||||
|
|
||||||
|
A reader that matches lines on the letter 'o', trims the
|
||||||
|
space from the lines, and transforms everything to upper case.
|
||||||
|
|
||||||
|
```go
|
||||||
|
phrases := " lacy timber \n"
|
||||||
|
phrases += "\t\thybrid gossiping\t\n"
|
||||||
|
phrases += " coy radioactivity\n"
|
||||||
|
phrases += "rocky arrow \n"
|
||||||
|
|
||||||
|
r := ToUpper(LineTrimSpace(LineMatch(bytes.NewBufferString(phrases), "o")))
|
||||||
|
|
||||||
|
// Pass the string though the transformer.
|
||||||
|
out, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", out)
|
||||||
|
```
|
||||||
|
|
||||||
|
Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
HYBRID GOSSIPING
|
||||||
|
COY RADIOACTIVITY
|
||||||
|
ROCKY ARROW
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transutil package
|
||||||
|
|
||||||
|
[](https://pkg.go.dev/github.com/tidwall/transform/transutil)
|
||||||
|
|
||||||
|
The `github.com/tidwall/transform/transutil` package includes additional examples.
|
||||||
|
|
||||||
|
```go
|
||||||
|
func Gunzipper(r io.Reader) io.Reader
|
||||||
|
func Gzipper(r io.Reader) io.Reader
|
||||||
|
func JSONToMsgPack(r io.Reader) io.Reader
|
||||||
|
func JSONToPrettyJSON(r io.Reader) io.Reader
|
||||||
|
func JSONToProtoBuf(r io.Reader, pb proto.Message, multimessage bool) io.Reader
|
||||||
|
func JSONToUglyJSON(r io.Reader) io.Reader
|
||||||
|
func MsgPackToJSON(r io.Reader) io.Reader
|
||||||
|
func ProtoBufToJSON(r io.Reader, pb proto.Message, multimessage bool) io.Reader
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This is a maintenance fork of <https://github.com/tidwall/transform>.
|
54
3p/transform/transform.go
Normal file
54
3p/transform/transform.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Package transform provides a convenient utility for transforming one data
|
||||||
|
// format to another.
|
||||||
|
package transform
|
||||||
|
|
||||||
|
// Transformer represents a transform reader.
|
||||||
|
type Transformer struct {
|
||||||
|
tfn func() ([]byte, error) // user-defined transform function
|
||||||
|
buf []byte // read buffer
|
||||||
|
idx int // read buffer index
|
||||||
|
err error // last error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTransformer returns an object that can be used for transforming one
|
||||||
|
// data formant to another. The param is a function that performs the
|
||||||
|
// conversion and returns the transformed data in chunks/messages.
|
||||||
|
func NewTransformer(fn func() ([]byte, error)) *Transformer {
|
||||||
|
return &Transformer{tfn: fn}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadMessage allows for reading a one transformed message at a time.
|
||||||
|
func (r *Transformer) ReadMessage() ([]byte, error) {
|
||||||
|
return r.tfn()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read conforms to io.Reader
|
||||||
|
func (r *Transformer) Read(p []byte) (n int, err error) {
|
||||||
|
if len(r.buf)-r.idx > 0 {
|
||||||
|
// There's data in the read buffer, return it prior to returning errors
|
||||||
|
// or reading more messages.
|
||||||
|
if len(r.buf)-r.idx > len(p) {
|
||||||
|
// The input slice is smaller than the read buffer, copy a subslice
|
||||||
|
// of the read buffer and increase the read index.
|
||||||
|
copy(p, r.buf[r.idx:r.idx+len(p)])
|
||||||
|
r.idx += len(p)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
// Copy the entire read buffer to the input slice.
|
||||||
|
n = len(r.buf) - r.idx
|
||||||
|
copy(p[:n], r.buf[r.idx:])
|
||||||
|
r.buf = r.buf[:0] // reset the read buffer, keeping it's capacity
|
||||||
|
r.idx = 0 // rewind the read buffer index
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
if r.err != nil {
|
||||||
|
return 0, r.err
|
||||||
|
}
|
||||||
|
var msg []byte
|
||||||
|
msg, r.err = r.ReadMessage()
|
||||||
|
// We should immediately append the incoming message to the read
|
||||||
|
// buffer to allow for the implemented transformer to repurpose
|
||||||
|
// it's own message space if needed.
|
||||||
|
r.buf = append(r.buf, msg...)
|
||||||
|
return r.Read(p)
|
||||||
|
}
|
250
3p/transform/transform_test.go
Normal file
250
3p/transform/transform_test.go
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
package transform
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Rot13(r io.Reader) *Transformer {
|
||||||
|
buf := make([]byte, rand.Int()%256+1) // used to test varying slice sizes
|
||||||
|
//buf := make([]byte, 256)
|
||||||
|
return NewTransformer(func() ([]byte, error) {
|
||||||
|
n, err := r.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if buf[i] >= 'a' && buf[i] <= 'z' {
|
||||||
|
buf[i] = ((buf[i] - 'a' + 13) % 26) + 'a'
|
||||||
|
} else if buf[i] >= 'A' && buf[i] <= 'Z' {
|
||||||
|
buf[i] = ((buf[i] - 'A' + 13) % 26) + 'A'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf[:n], nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTransformer(t *testing.T) {
|
||||||
|
// simple
|
||||||
|
msg := "Hello\n13th Floor"
|
||||||
|
data, err := ioutil.ReadAll(Rot13(Rot13(bytes.NewBufferString(msg))))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if msg != string(data) {
|
||||||
|
t.Fatalf("expected '%v', got '%v'\n", msg, string(data))
|
||||||
|
}
|
||||||
|
// random
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
buf := make([]byte, 10000)
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
_, err := rand.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadAll(Rot13(Rot13(bytes.NewBuffer(buf))))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if string(buf) != string(data) {
|
||||||
|
t.Fatalf("expected '%v', got '%v'\n", string(buf), string(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleTransformer_rot13() {
|
||||||
|
// Rot13 transformation
|
||||||
|
rot13 := func(r io.Reader) *Transformer {
|
||||||
|
buf := make([]byte, 256)
|
||||||
|
return NewTransformer(func() ([]byte, error) {
|
||||||
|
n, err := r.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
if buf[i] >= 'a' && buf[i] <= 'z' {
|
||||||
|
buf[i] = ((buf[i] - 'a' + 13) % 26) + 'a'
|
||||||
|
} else if buf[i] >= 'A' && buf[i] <= 'Z' {
|
||||||
|
buf[i] = ((buf[i] - 'A' + 13) % 26) + 'A'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf[:n], nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Pass the string though the transformer.
|
||||||
|
out, err := ioutil.ReadAll(rot13(bytes.NewBufferString("Hello World")))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", out)
|
||||||
|
// Output:
|
||||||
|
// Uryyb Jbeyq
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleTransformer_toUpper() {
|
||||||
|
// Convert a string to uppper case. Unicode aware.
|
||||||
|
toUpper := func(r io.Reader) *Transformer {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
return NewTransformer(func() ([]byte, error) {
|
||||||
|
c, _, err := br.ReadRune()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(strings.ToUpper(string([]rune{c}))), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Pass the string though the transformer.
|
||||||
|
out, err := ioutil.ReadAll(toUpper(bytes.NewBufferString("Hello World")))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", out)
|
||||||
|
// Output:
|
||||||
|
// HELLO WORLD
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleTransformer_lineMatcherRegExp() {
|
||||||
|
// Filter lines matching a pattern
|
||||||
|
matcher := func(r io.Reader, pattern string) *Transformer {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
return NewTransformer(func() ([]byte, error) {
|
||||||
|
for {
|
||||||
|
line, err := br.ReadBytes('\n')
|
||||||
|
matched, _ := regexp.Match(pattern, line)
|
||||||
|
if matched {
|
||||||
|
return line, err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
logs := `
|
||||||
|
23 Apr 17:32:23.604 [INFO] DB loaded in 0.551 seconds
|
||||||
|
23 Apr 17:32:23.605 [WARN] Disk space is low
|
||||||
|
23 Apr 17:32:23.054 [INFO] Server started on port 7812
|
||||||
|
23 Apr 17:32:23.141 [INFO] Ready for connections
|
||||||
|
`
|
||||||
|
// Pass the string though the transformer.
|
||||||
|
out, err := ioutil.ReadAll(matcher(bytes.NewBufferString(logs), "WARN"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", out)
|
||||||
|
// Output:
|
||||||
|
// 23 Apr 17:32:23.605 [WARN] Disk space is low
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleTransformer_trimmer() {
|
||||||
|
// Trim space from all lines
|
||||||
|
trimmer := func(r io.Reader) *Transformer {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
return NewTransformer(func() ([]byte, error) {
|
||||||
|
for {
|
||||||
|
line, err := br.ReadBytes('\n')
|
||||||
|
if len(line) > 0 {
|
||||||
|
line = append(bytes.TrimSpace(line), '\n')
|
||||||
|
}
|
||||||
|
return line, err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
phrases := " lacy timber \n"
|
||||||
|
phrases += "\t\thybrid gossiping\t\n"
|
||||||
|
phrases += " coy radioactivity\n"
|
||||||
|
phrases += "rocky arrow \n"
|
||||||
|
// Pass the string though the transformer.
|
||||||
|
out, err := ioutil.ReadAll(trimmer(bytes.NewBufferString(phrases)))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", out)
|
||||||
|
// Output:
|
||||||
|
// lacy timber
|
||||||
|
// hybrid gossiping
|
||||||
|
// coy radioactivity
|
||||||
|
// rocky arrow
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleTransformer_pipeline() {
|
||||||
|
// Filter lines matching a pattern
|
||||||
|
matcher := func(r io.Reader, pattern string) *Transformer {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
return NewTransformer(func() ([]byte, error) {
|
||||||
|
for {
|
||||||
|
line, err := br.ReadBytes('\n')
|
||||||
|
matched, _ := regexp.Match(pattern, line)
|
||||||
|
if matched {
|
||||||
|
return line, err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim space from all lines
|
||||||
|
trimmer := func(r io.Reader) *Transformer {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
return NewTransformer(func() ([]byte, error) {
|
||||||
|
for {
|
||||||
|
line, err := br.ReadBytes('\n')
|
||||||
|
if len(line) > 0 {
|
||||||
|
line = append(bytes.TrimSpace(line), '\n')
|
||||||
|
}
|
||||||
|
return line, err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a string to uppper case. Unicode aware. In this example
|
||||||
|
// we only process one rune at a time. It works but it's not ideal
|
||||||
|
// for production.
|
||||||
|
toUpper := func(r io.Reader) *Transformer {
|
||||||
|
br := bufio.NewReader(r)
|
||||||
|
return NewTransformer(func() ([]byte, error) {
|
||||||
|
c, _, err := br.ReadRune()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(strings.ToUpper(string([]rune{c}))), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
phrases := " lacy timber \n"
|
||||||
|
phrases += "\t\thybrid gossiping\t\n"
|
||||||
|
phrases += " coy radioactivity\n"
|
||||||
|
phrases += "rocky arrow \n"
|
||||||
|
|
||||||
|
// create a transformer that matches lines on the letter 'o', trims the
|
||||||
|
// space from the lines, and transforms to upper case.
|
||||||
|
r := toUpper(trimmer(matcher(bytes.NewBufferString(phrases), "o")))
|
||||||
|
|
||||||
|
// Pass the string though the transformer.
|
||||||
|
out, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s\n", out)
|
||||||
|
// Output:
|
||||||
|
// HYBRID GOSSIPING
|
||||||
|
// COY RADIOACTIVITY
|
||||||
|
// ROCKY ARROW
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user