Implement *strip converter + tests

This commit is contained in:
ionutboangiu
2023-10-09 11:16:13 -04:00
committed by Dan Christian Bogos
parent efcb32741a
commit b09512a5c5
3 changed files with 457 additions and 0 deletions

View File

@@ -147,6 +147,9 @@ const (
MetaSingle = "*single"
MetaZero = "*zero"
MetaASAP = "*asap"
MetaNil = "*nil"
MetaSpace = "*space"
MetaChar = "*char"
CommentChar = '#'
CSVSep = ','
FallbackSep = ';'
@@ -658,6 +661,7 @@ const (
E164Converter = "*e164"
MetaJoin = "*join"
MetaSplit = "*split"
MetaStrip = "*strip"
MetaReload = "*reload"
MetaLoad = "*load"
MetaFloat64 = "*float64"
@@ -1051,6 +1055,7 @@ const (
MetaString = "*string"
MetaPrefix = "*prefix"
MetaSuffix = "*suffix"
MetaBoth = "*both"
MetaEmpty = "*empty"
MetaExists = "*exists"
MetaCronExp = "*cronexp"

View File

@@ -139,6 +139,8 @@ func NewDataConverter(params string) (conv DataConverter, err error) {
return splitConverter(InfieldSep), nil
}
return splitConverter(params[len(MetaSplit)+1:]), nil
case strings.HasPrefix(params, MetaStrip):
return NewStripConverter(params)
default:
return nil, fmt.Errorf("unsupported converter definition: <%s>", params)
}
@@ -686,3 +688,138 @@ func (jsnC JSONConverter) Convert(in any) (any, error) {
}
return string(b), nil
}
// StripConverter strips the prefix, the suffix or both from a string.
type StripConverter struct {
side string // side represents which part of the string to strip: prefix, suffix, or both.
substr string // substr represents the substring to be removed from the string.
amount int // amount represents the number of characters to be removed from the string.
}
// NewStripConverter initializes and returns a new StripConverter with configurations
// based on the provided parameters in the input string. Each parameter in the input
// string should be separated by ':'.
//
// The input string must follow one of the following formats:
// 1. "*strip:<side>:<amount>"
// 2. "*strip:<side>:<substring>[:<amount>]"
// 3. "*strip:<side>:*char:<substring>[:<amount>]"
//
// Explanation of placeholders:
// - <side>: Specifies which part of the string to strip. Must be one of "*prefix", "*suffix", or "*both".
// - <substring>: Identifies the substring to remove. It can be a specific string, "*nil" for null characters,
// "*space" for spaces, or any other character.
// - <amount> (optional): Determines the number of characters to remove. If omitted, all instances of <substring>
// are removed.
//
// Examples:
// - "*strip:*prefix:5": Removes the first 5 characters from the string's prefix.
// - "*strip:*suffix:*nil": Eliminates all trailing null characters in the string.
// - "*strip:*both:*space:2": Clears 2 spaces from both the prefix and suffix of the string.
// - "*strip:*suffix:*char:abc": Removes the substring "abc" from the suffix of the string.
// - "*strip:*prefix:*char:abc:2": Strips the substring "abc" from the prefix of the string, repeated 2 times.
func NewStripConverter(params string) (DataConverter, error) {
paramSlice := strings.Split(params, InInFieldSep)
paramCount := len(paramSlice)
if paramCount < 3 || paramCount > 5 {
return nil, errors.New("strip converter: invalid number of parameters (should have 3, 4 or 5)")
}
sc := StripConverter{
side: paramSlice[1],
substr: paramSlice[2],
amount: -1,
}
var err error
switch sc.substr {
case EmptyString:
return nil, errors.New("strip converter: substr parameter cannot be empty")
case MetaNil, MetaSpace:
if paramCount == 5 {
return nil, errors.New("strip converter: cannot have 5 params in *nil/*space case")
}
if sc.substr == MetaNil {
sc.substr = "\u0000"
} else {
sc.substr = " "
}
if paramCount == 4 {
sc.amount, err = strconv.Atoi(paramSlice[3])
if err != nil {
return nil, fmt.Errorf("strip converter: invalid amount parameter (%w)", err)
}
sc.substr = strings.Repeat(sc.substr, sc.amount)
}
case MetaChar:
if paramCount < 4 || paramSlice[3] == EmptyString {
return nil, errors.New("strip converter: usage of *char implies the need of 4 or 5 non-empty params")
}
sc.substr = paramSlice[3]
if paramCount == 5 {
sc.amount, err = strconv.Atoi(paramSlice[4])
if err != nil {
return nil, fmt.Errorf("strip converter: invalid amount parameter (%w)", err)
}
sc.substr = strings.Repeat(sc.substr, sc.amount)
}
default:
if paramCount > 3 {
return nil, errors.New("strip converter: just the amount specified, cannot have more than 3 params")
}
sc.amount, err = strconv.Atoi(paramSlice[2])
if err != nil {
return nil, fmt.Errorf("strip converter: invalid amount parameter (%w)", err)
}
sc.substr = ""
}
return sc, nil
}
// Convert trims the input string based on the StripConverter's configuration.
// It returns a CAST_FAILED error if the input is not a string.
func (sc StripConverter) Convert(in any) (any, error) {
str, ok := in.(string)
if !ok {
return nil, fmt.Errorf("strip converter: %w", ErrCastFailed)
}
if sc.amount <= 0 && sc.amount != -1 {
return str, nil
}
switch sc.side {
case MetaPrefix:
if sc.substr == EmptyString {
if sc.amount < len(str) {
return str[sc.amount:], nil
}
return EmptyString, nil
}
if sc.amount != -1 {
return strings.TrimPrefix(str, sc.substr), nil
}
return strings.TrimLeft(str, sc.substr), nil
case MetaSuffix:
if sc.substr == EmptyString {
if sc.amount < len(str) {
return str[:len(str)-sc.amount], nil
}
return EmptyString, nil
}
if sc.amount != -1 {
return strings.TrimSuffix(str, sc.substr), nil
}
return strings.TrimRight(str, sc.substr), nil
case MetaBoth:
if sc.substr == EmptyString {
if sc.amount*2 < len(str) {
return str[sc.amount : len(str)-sc.amount], nil
}
return EmptyString, nil
}
if sc.amount != -1 {
str = strings.TrimPrefix(str, sc.substr)
return strings.TrimSuffix(str, sc.substr), nil
}
return strings.Trim(str, sc.substr), nil
default:
return EmptyString, errors.New("strip converter: invalid side parameter")
}
}

View File

@@ -1480,3 +1480,318 @@ func TestDataConverterConvertJSONOK(t *testing.T) {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", exp, rcv)
}
}
func TestStripConverter(t *testing.T) {
tests := []struct {
name string
params string
input string
expected string
constructorErr bool
convertErr bool
}{
{
name: "Strip 5 leading characters",
params: "*strip:*prefix:5",
input: "12345TEST12345",
expected: "TEST12345",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 5 trailing characters",
params: "*strip:*suffix:5",
input: "12345TEST12345",
expected: "12345TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 5 characters from both sides",
params: "*strip:*both:5",
input: "12345TEST12345",
expected: "TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip all trailing nils",
params: "*strip:*suffix:*nil",
input: "TEST\u0000\u0000\u0000\u0000",
expected: "TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip all leading nils",
params: "*strip:*prefix:*nil",
input: "\u0000\u0000\u0000\u0000TEST",
expected: "TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip all nils from both sides",
params: "*strip:*both:*nil",
input: "\u0000\u0000TEST\u0000\u0000",
expected: "TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 2 trailing nils",
params: "*strip:*suffix:*nil:2",
input: "TEST\u0000\u0000\u0000\u0000",
expected: "TEST\u0000\u0000",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 2 leading nils",
params: "*strip:*prefix:*nil:2",
input: "\u0000\u0000\u0000\u0000TEST",
expected: "\u0000\u0000TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 1 nil from both sides",
params: "*strip:*both:*nil:1",
input: "\u0000\u0000TEST\u0000\u0000",
expected: "\u0000TEST\u0000",
constructorErr: false,
convertErr: false,
},
{
name: "Strip all trailing spaces",
params: "*strip:*suffix:*space",
input: "TEST ",
expected: "TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip all leading spaces",
params: "*strip:*prefix:*space",
input: " TEST",
expected: "TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip all spaces from both sides",
params: "*strip:*both:*space",
input: " TEST ",
expected: "TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 2 trailing spaces",
params: "*strip:*suffix:*space:2",
input: "TEST ",
expected: "TEST ",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 2 leading spaces",
params: "*strip:*prefix:*space:2",
input: " TEST",
expected: " TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 1 space from both sides",
params: "*strip:*both:*space:1",
input: " TEST ",
expected: " TEST ",
constructorErr: false,
convertErr: false,
},
{
name: "Strip all trailing 'abcd' char groups",
params: "*strip:*suffix:*char:abcd",
input: "TESTabcdabcdabcdabcd",
expected: "TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip all leading 'abcd' char groups",
params: "*strip:*prefix:*char:abcd",
input: "abcdabcdabcdabcdTEST",
expected: "TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip all 'abcd' char groups from both sides",
params: "*strip:*both:*char:abcd",
input: "abcdabcdTESTabcdabcd",
expected: "TEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 2 trailing 'abcd' char groups",
params: "*strip:*suffix:*char:abcd:2",
input: "TESTabcdabcdabcdabcd",
expected: "TESTabcdabcd",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 2 leading 'abcd' char groups",
params: "*strip:*prefix:*char:abcd:2",
input: "abcdabcdabcdabcdTEST",
expected: "abcdabcdTEST",
constructorErr: false,
convertErr: false,
},
{
name: "Strip 1 'abcd' char group from both sides",
params: "*strip:*both:*char:abcd:1",
input: "abcdabcdTESTabcdabcd",
expected: "abcdTESTabcd",
constructorErr: false,
convertErr: false,
},
{
name: "Empty third parameter",
params: "*strip:*prefix:",
input: "TEST",
expected: "strip converter: substr parameter cannot be empty",
constructorErr: true,
convertErr: false,
},
{
name: "Invalid side parameter",
params: "*strip:*invalid:*nil",
input: "TEST",
expected: "strip converter: invalid side parameter",
constructorErr: false,
convertErr: true,
},
{
name: "Invalid nr. of params *char",
params: "*strip:*prefix:*char:*nil:abc:3",
input: "TEST",
expected: "strip converter: invalid number of parameters (should have 3, 4 or 5)",
constructorErr: true,
convertErr: false,
},
{
name: "Invalid amount parameter",
params: "*strip:*prefix:*char:0:three",
input: "000TEST",
expected: "strip converter: invalid amount parameter (strconv.Atoi: parsing \"three\": invalid syntax)",
constructorErr: true,
convertErr: false,
},
{
name: "Strip a prefix longer than the value",
params: "*strip:*prefix:5",
input: "TEST",
expected: "",
constructorErr: false,
convertErr: false,
},
{
name: "Strip a suffix longer than the value",
params: "*strip:*suffix:5",
input: "TEST",
expected: "",
constructorErr: false,
convertErr: false,
},
{
name: "Strip from both ends an amount of characters longer than the value",
params: "*strip:*both:3",
input: "TEST",
expected: "",
constructorErr: false,
convertErr: false,
},
{
name: "*char missing substring case 1",
params: "*strip:*prefix:*char",
input: "12345TEST",
expected: "strip converter: usage of *char implies the need of 4 or 5 non-empty params",
constructorErr: true,
convertErr: false,
},
{
name: "*char missing substring case 2",
params: "*strip:*prefix:*char::2",
input: "12345TEST",
expected: "strip converter: usage of *char implies the need of 4 or 5 non-empty params",
constructorErr: true,
convertErr: false,
},
{
name: "*char missing substring case 3",
params: "*strip:*prefix:*char:",
input: "12345TEST",
expected: "strip converter: usage of *char implies the need of 4 or 5 non-empty params",
constructorErr: true,
convertErr: false,
},
{
name: "*nil/*space too many parameters",
params: "*strip:*prefix:*nil:5:12345",
input: "12345TEST",
expected: "strip converter: cannot have 5 params in *nil/*space case",
constructorErr: true,
convertErr: false,
},
{
name: "third param numeric with too many params case 1",
params: "*strip:*prefix:1:1",
input: "12345TEST",
expected: "strip converter: just the amount specified, cannot have more than 3 params",
constructorErr: true,
convertErr: false,
},
{
name: "third param numeric with too many params case 2",
params: "*strip:*prefix:1:12345:1",
input: "12345TEST",
expected: "strip converter: just the amount specified, cannot have more than 3 params",
constructorErr: true,
convertErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sc, err := NewDataConverter(tt.params)
if (err != nil) != tt.constructorErr {
t.Errorf("NewStripConverter() error = %v, constructorErr %v", err, tt.constructorErr)
return
}
if tt.constructorErr {
if err.Error() != tt.expected {
t.Errorf("expected error message: %v, received: %v", tt.expected, err.Error())
}
return
}
rcv, err := sc.Convert(tt.input)
if (err != nil) != tt.convertErr {
t.Errorf("Convert() error = %v, convertErr %v", err, tt.convertErr)
return
}
if tt.convertErr {
if err.Error() != tt.expected {
t.Errorf("expected error message: %s, received: %s", tt.expected, err.Error())
}
return
}
if rcv != tt.expected {
t.Errorf("expected: %q, received: %q", tt.expected, rcv)
}
})
}
}