1. I did observe that there's an experimental "Transformer" interface, in
, but I haven't had time to dig around in it.
2. Something like this would would permit you to wrap a reader, such as from a fs.FileReader, packaged with a map of byte->rewriter-rule, and implementing the basic "io.Reader" interface. You would be able to then pass a configured instance to a CSV reader. There are some shortcuts, such as not permitting you to construct a new slice of byte, push any necessary escape characters, the triggering byte, and then the remaining bytes. This approach permits a rule to request an arbitrary look-forward chunk, but with no scan-more functionality. It should be noted that even if it doesn't look like I scraped it together for funsies, and I'm pretty confident the approach is alright, you may very well be able to make some major improvements.
https://go.dev/play/p/uGwWSMmm9hOconst CHUNKSIZE int = 32
type RewriteFunc func([]byte)[]byte
type RewriteRule struct {
RewriteFunc
Size int
}
func (rule RewriteRule) IsOk() bool {
return rule.Size > 0 && rule.RewriteFunc != nil
}
func (rule RewriteRule) Transform(p []byte) ([]byte, bool) {
if rule.Size < len(p) {
return []byte{}, false
}
return rule.RewriteFunc(p), true
}
type SmartCSV struct {
buffer []byte
ruleset map[byte]RewriteRule
err error
src io.Reader
}
func (scv *SmartCSV) feed(n int) int {
if scv.err != nil {
return 0, scv.err
}
chunk := make([]byte, n)
n, scv.err = scv.src.Read(chunk)
if n > 0 {
scv.buffer = append(buffer, chunk...)
}
return n
}
func (scv *SmartCSV) peek(n int) (byte, bool) {
if scv.err != nil {
return 0, scv.err
}
if n < len(scv.buffer) {
return scv.buffer[n], true
} else if scv.err != nil {
return 0, false
}
scv.feed(CHUNKSIZE)
return scv.peek(n)
}
func (scv *SmartCSV) pop() (b byte, ok bool) {
if 0 < len(scv.buffer) {
b = scv.buffer[0]
if rule, ok := scv.ruleset[b]; ok && rule.IsOk() {
_, ok = scv.peek(rule.Size)
if !ok {
scv.buffer = scv.buffer[1:]
return b, false
}
p, ok := rule.Transform(scv.buffer)
if !ok {
scv.buffer = scv.buffer[1:]
return b, false
}
b, scv.buffer = p[0], p[1:]
} else {
scv.buffer = scv.buffer[1:]
}
} else if scv.err != nil {
return 0, false
} else {
b, ok = scv.peek(1)
}
return b, ok
}