add csv processor

This commit is contained in:
2026-01-02 22:42:02 +03:00
parent e60c0e25d5
commit d3418959d1
2 changed files with 177 additions and 0 deletions

123
processor/csv/processor.go Normal file
View File

@@ -0,0 +1,123 @@
package csv
import (
"bytes"
"context"
"encoding/csv"
"fmt"
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/param"
"gitoa.ru/go-4devs/config/value"
)
type pkey int
const (
paramDelimiter pkey = iota + 1
paramParse
)
const defaultDelimiter = ','
func WithDelimiter(in rune) param.Option {
return func(p param.Params) param.Params {
return param.With(p, paramDelimiter, in)
}
}
func WithInt(p param.Params) param.Params {
return param.With(p, paramParse, func(data []string) (config.Value, error) {
return value.ParseSlice(data, value.Atoi)
})
}
func WithInt64(p param.Params) param.Params {
return param.With(p, paramParse, func(data []string) (config.Value, error) {
return value.ParseSlice(data, value.ParseInt64)
})
}
func WithFloat(p param.Params) param.Params {
return param.With(p, paramParse, func(data []string) (config.Value, error) {
return value.ParseSlice(data, value.ParseFloat)
})
}
func WithBool(p param.Params) param.Params {
return param.With(p, paramParse, func(data []string) (config.Value, error) {
return value.ParseSlice(data, value.ParseBool)
})
}
func WithUint(p param.Params) param.Params {
return param.With(p, paramParse, func(data []string) (config.Value, error) {
return value.ParseSlice(data, value.ParseUint)
})
}
func WithUint64(p param.Params) param.Params {
return param.With(p, paramParse, func(data []string) (config.Value, error) {
return value.ParseSlice(data, value.ParseUint64)
})
}
func WithDuration(p param.Params) param.Params {
return param.With(p, paramParse, func(data []string) (config.Value, error) {
return value.ParseSlice(data, value.ParseDuration)
})
}
func WithTime(p param.Params) param.Params {
return param.With(p, paramParse, func(data []string) (config.Value, error) {
return value.ParseSlice(data, value.ParseTime)
})
}
func WithParse(fn func(data []string) config.Value) param.Option {
return func(p param.Params) param.Params {
return param.With(p, paramParse, fn)
}
}
func Csv(_ context.Context, in config.Value, opts ...param.Option) (config.Value, error) {
sval, serr := in.ParseString()
if serr != nil {
return in, nil //nolint:nilerr
}
params := param.New(opts...)
reader := csv.NewReader(bytes.NewBufferString(sval))
reader.Comma = getDelimiter(params)
data, rerr := reader.Read()
if rerr != nil {
return nil, fmt.Errorf("read csv:%w", rerr)
}
return csvValue(params, data)
}
func csvValue(params param.Params, data []string) (config.Value, error) {
fn, ok := params.Param(paramParse)
if !ok {
return stringsValue(data)
}
parse, _ := fn.(func([]string) (config.Value, error))
return parse(data)
}
func stringsValue(data []string) (config.Value, error) {
return value.New(data), nil
}
func getDelimiter(params param.Params) rune {
if name, ok := param.Rune(params, paramDelimiter); ok {
return name
}
return defaultDelimiter
}

View File

@@ -0,0 +1,54 @@
package csv_test
import (
"context"
"testing"
"gitoa.ru/go-4devs/config"
"gitoa.ru/go-4devs/config/processor/csv"
"gitoa.ru/go-4devs/config/test/require"
"gitoa.ru/go-4devs/config/value"
)
func TestCsv(t *testing.T) {
t.Parallel()
ctx := context.Background()
val := value.String("test2,test3,other")
data, derr := csv.Csv(ctx, val)
require.NoError(t, derr)
var resString []string
require.NoError(t, data.Unmarshal(&resString))
require.Equal(t, []string{"test2", "test3", "other"}, resString)
}
func TestCsv_int(t *testing.T) {
t.Parallel()
ctx := context.Background()
val := value.String("42,0,1")
data, derr := csv.Csv(ctx, val, csv.WithInt)
require.NoError(t, derr)
var resInt []int
require.NoError(t, data.Unmarshal(&resInt))
require.Equal(t, []int{42, 0, 1}, resInt)
}
func TestCsv_invalidValue(t *testing.T) {
t.Parallel()
ctx := context.Background()
val := value.String("42,0.1,1")
data, derr := csv.Csv(ctx, val, csv.WithInt)
require.ErrorIs(t, derr, config.ErrInvalidValue)
require.Equal(t, nil, data)
}