mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Added in-depth comments to the constructor. Added test cases for most edge cases (if not all). Updated the test to verify not only if an error occurs, but to also check whether it matches the one we expect. Added 3 more sanity checks that were missed.
726 lines
20 KiB
Go
726 lines
20 KiB
Go
/*
|
|
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
|
|
Copyright (C) ITsysCOM GmbH
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
*/
|
|
|
|
package utils
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math/rand"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cgrates/sipingo"
|
|
"github.com/nyaruka/phonenumbers"
|
|
)
|
|
|
|
// DataConverters groups together multiple converters,
|
|
// executing optimized conversions
|
|
type DataConverters []DataConverter
|
|
|
|
// ConvertString converts from and to string
|
|
func (dcs DataConverters) ConvertString(in string) (out string, err error) {
|
|
outIface := any(in)
|
|
for _, cnv := range dcs {
|
|
if outIface, err = cnv.Convert(outIface); err != nil {
|
|
return
|
|
}
|
|
}
|
|
return IfaceAsString(outIface), nil
|
|
}
|
|
|
|
// DataConverter represents functions which should convert input into output
|
|
type DataConverter interface {
|
|
Convert(any) (any, error)
|
|
}
|
|
|
|
// NewDataConverter is a factory of converters
|
|
func NewDataConverter(params string) (conv DataConverter, err error) {
|
|
switch {
|
|
case params == MetaDurationSeconds:
|
|
return NewDurationSecondsConverter(EmptyString)
|
|
case params == MetaDurationNanoseconds:
|
|
return NewDurationNanosecondsConverter(EmptyString)
|
|
case strings.HasPrefix(params, MetaRound):
|
|
if len(params) == len(MetaRound) { // no extra params, defaults implied
|
|
return NewRoundConverter(EmptyString)
|
|
}
|
|
return NewRoundConverter(params[len(MetaRound)+1:])
|
|
case strings.HasPrefix(params, MetaMultiply):
|
|
if len(params) == len(MetaMultiply) { // no extra params, defaults implied
|
|
return NewMultiplyConverter(EmptyString)
|
|
}
|
|
return NewMultiplyConverter(params[len(MetaMultiply)+1:])
|
|
case strings.HasPrefix(params, MetaDivide):
|
|
if len(params) == len(MetaDivide) { // no extra params, defaults implied
|
|
return NewDivideConverter(EmptyString)
|
|
}
|
|
return NewDivideConverter(params[len(MetaDivide)+1:])
|
|
case params == MetaJSON:
|
|
return new(JSONConverter), nil
|
|
case params == MetaDuration:
|
|
return NewDurationConverter(EmptyString)
|
|
case params == MetaIP2Hex:
|
|
return new(IP2HexConverter), nil
|
|
case params == MetaString2Hex:
|
|
return new(String2HexConverter), nil
|
|
case params == MetaSIPURIHost:
|
|
return new(SIPURIHostConverter), nil
|
|
case params == MetaSIPURIUser:
|
|
return new(SIPURIUserConverter), nil
|
|
case params == MetaSIPURIMethod:
|
|
return new(SIPURIMethodConverter), nil
|
|
case params == MetaUnixTime:
|
|
return new(UnixTimeConverter), nil
|
|
case params == MetaLen:
|
|
return new(LengthConverter), nil
|
|
case params == MetaSlice:
|
|
return new(SliceConverter), nil
|
|
case params == MetaFloat64:
|
|
return new(Float64Converter), nil
|
|
case params == E164DomainConverter:
|
|
return new(e164DomainConverter), nil
|
|
case params == E164Converter:
|
|
return new(e164Converter), nil
|
|
case strings.HasPrefix(params, MetaLibPhoneNumber):
|
|
if len(params) == len(MetaLibPhoneNumber) {
|
|
return NewPhoneNumberConverter(EmptyString)
|
|
}
|
|
return NewPhoneNumberConverter(params[len(MetaLibPhoneNumber)+1:])
|
|
case strings.HasPrefix(params, MetaTimeString):
|
|
layout := time.RFC3339
|
|
if len(params) > len(MetaTimeString) { // no extra params, defaults implied
|
|
layout = params[len(MetaTimeString)+1:]
|
|
}
|
|
return NewTimeStringConverter(layout), nil
|
|
case strings.HasPrefix(params, MetaRandom):
|
|
if len(params) == len(MetaRandom) { // no extra params, defaults implied
|
|
return NewRandomConverter(EmptyString)
|
|
}
|
|
return NewRandomConverter(params[len(MetaRandom)+1:])
|
|
case strings.HasPrefix(params, MetaStrip):
|
|
return NewStripConverter(params)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported converter definition: <%s>", params)
|
|
}
|
|
}
|
|
|
|
func NewDataConverterMustCompile(params string) (conv DataConverter) {
|
|
var err error
|
|
if conv, err = NewDataConverter(params); err != nil {
|
|
panic(fmt.Sprintf("parsing: <%s>, error: %s", params, err.Error()))
|
|
}
|
|
return
|
|
}
|
|
|
|
func NewDurationSecondsConverter(params string) (hdlr DataConverter, err error) {
|
|
return new(DurationSecondsConverter), nil
|
|
}
|
|
|
|
// DurationSecondsConverter converts duration into seconds encapsulated in float64
|
|
type DurationSecondsConverter struct{}
|
|
|
|
func (mS *DurationSecondsConverter) Convert(in any) (
|
|
out any, err error) {
|
|
var inDur time.Duration
|
|
if inDur, err = IfaceAsDuration(in); err != nil {
|
|
return nil, err
|
|
}
|
|
out = inDur.Seconds()
|
|
return
|
|
}
|
|
|
|
func NewDurationNanosecondsConverter(params string) (
|
|
hdlr DataConverter, err error) {
|
|
return new(DurationNanosecondsConverter), nil
|
|
}
|
|
|
|
// DurationNanosecondsConverter converts duration into nanoseconds encapsulated in int64
|
|
type DurationNanosecondsConverter struct{}
|
|
|
|
func (mS *DurationNanosecondsConverter) Convert(in any) (
|
|
out any, err error) {
|
|
var inDur time.Duration
|
|
if inDur, err = IfaceAsDuration(in); err != nil {
|
|
return nil, err
|
|
}
|
|
out = inDur.Nanoseconds()
|
|
return
|
|
}
|
|
|
|
func NewRoundConverter(params string) (hdlr DataConverter, err error) {
|
|
rc := new(RoundConverter)
|
|
var paramsSplt []string
|
|
if params != EmptyString {
|
|
paramsSplt = strings.Split(params, InInFieldSep)
|
|
}
|
|
switch len(paramsSplt) {
|
|
case 0:
|
|
rc.Method = MetaRoundingMiddle
|
|
case 1:
|
|
if rc.Decimals, err = strconv.Atoi(paramsSplt[0]); err != nil {
|
|
return nil, fmt.Errorf("%s converter needs integer as decimals, have: <%s>",
|
|
MetaRound, paramsSplt[0])
|
|
}
|
|
rc.Method = MetaRoundingMiddle
|
|
case 2:
|
|
rc.Method = paramsSplt[1]
|
|
if rc.Decimals, err = strconv.Atoi(paramsSplt[0]); err != nil {
|
|
return nil, fmt.Errorf("%s converter needs integer as decimals, have: <%s>",
|
|
MetaRound, paramsSplt[0])
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("unsupported %s converter parameters: <%s>",
|
|
MetaRound, params)
|
|
}
|
|
return rc, nil
|
|
}
|
|
|
|
// RoundConverter will round floats
|
|
type RoundConverter struct {
|
|
Decimals int
|
|
Method string
|
|
}
|
|
|
|
func (rnd *RoundConverter) Convert(in any) (out any, err error) {
|
|
var inFloat float64
|
|
if inFloat, err = IfaceAsFloat64(in); err != nil {
|
|
return
|
|
}
|
|
out = Round(inFloat, rnd.Decimals, rnd.Method)
|
|
return
|
|
}
|
|
|
|
func NewMultiplyConverter(constructParams string) (hdlr DataConverter, err error) {
|
|
if constructParams == EmptyString {
|
|
return nil, ErrMandatoryIeMissingNoCaps
|
|
}
|
|
var val float64
|
|
if val, err = strconv.ParseFloat(constructParams, 64); err != nil {
|
|
return
|
|
}
|
|
return &MultiplyConverter{Value: val}, nil
|
|
}
|
|
|
|
// MultiplyConverter multiplies input with value in params
|
|
// encapsulates the output as float64 value
|
|
type MultiplyConverter struct {
|
|
Value float64
|
|
}
|
|
|
|
func (m *MultiplyConverter) Convert(in any) (out any, err error) {
|
|
var inFloat64 float64
|
|
if inFloat64, err = IfaceAsFloat64(in); err != nil {
|
|
return nil, err
|
|
}
|
|
out = inFloat64 * m.Value
|
|
return
|
|
}
|
|
|
|
func NewDivideConverter(constructParams string) (hdlr DataConverter, err error) {
|
|
if constructParams == EmptyString {
|
|
return nil, ErrMandatoryIeMissingNoCaps
|
|
}
|
|
var val float64
|
|
if val, err = strconv.ParseFloat(constructParams, 64); err != nil {
|
|
return
|
|
}
|
|
return &DivideConverter{Value: val}, nil
|
|
}
|
|
|
|
// DivideConverter divides input with value in params
|
|
// encapsulates the output as float64 value
|
|
type DivideConverter struct {
|
|
Value float64
|
|
}
|
|
|
|
func (m *DivideConverter) Convert(in any) (out any, err error) {
|
|
var inFloat64 float64
|
|
if inFloat64, err = IfaceAsFloat64(in); err != nil {
|
|
return nil, err
|
|
}
|
|
out = inFloat64 / m.Value
|
|
return
|
|
}
|
|
|
|
func NewDurationConverter(params string) (hdlr DataConverter, err error) {
|
|
return new(DurationConverter), nil
|
|
}
|
|
|
|
// DurationConverter converts duration into seconds encapsulated in float64
|
|
type DurationConverter struct{}
|
|
|
|
func (mS *DurationConverter) Convert(in any) (
|
|
out any, err error) {
|
|
return IfaceAsDuration(in)
|
|
}
|
|
|
|
// NewPhoneNumberConverter create a new phoneNumber converter
|
|
// If the format isn't specify by default we use NATIONAL
|
|
// Possible fromats are : E164(0) , INTERNATIONAL(1) , NATIONAL(2) ,RFC3966(3)
|
|
// Also ContryCode needs to be specified
|
|
func NewPhoneNumberConverter(params string) (
|
|
pbDC DataConverter, err error) {
|
|
lc := new(PhoneNumberConverter)
|
|
var paramsSplt []string
|
|
if params != EmptyString {
|
|
paramsSplt = strings.Split(params, InInFieldSep)
|
|
}
|
|
switch len(paramsSplt) {
|
|
case 2:
|
|
lc.CountryCode = paramsSplt[0]
|
|
frm, err := strconv.Atoi(paramsSplt[1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
lc.Format = phonenumbers.PhoneNumberFormat(frm)
|
|
case 1:
|
|
lc.CountryCode = paramsSplt[0]
|
|
lc.Format = 2
|
|
default:
|
|
return nil, fmt.Errorf("unsupported %s converter parameters: <%s>",
|
|
MetaLibPhoneNumber, params)
|
|
}
|
|
return lc, nil
|
|
}
|
|
|
|
// PhoneNumberConverter converts
|
|
type PhoneNumberConverter struct {
|
|
CountryCode string
|
|
Format phonenumbers.PhoneNumberFormat
|
|
}
|
|
|
|
func (lc *PhoneNumberConverter) Convert(in any) (out any, err error) {
|
|
num, err := phonenumbers.Parse(IfaceAsString(in), lc.CountryCode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return phonenumbers.Format(num, lc.Format), nil
|
|
}
|
|
|
|
// IP2HexConverter will transform ip to hex
|
|
type IP2HexConverter struct{}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (*IP2HexConverter) Convert(in any) (out any, err error) {
|
|
var ip net.IP
|
|
switch val := in.(type) {
|
|
case string:
|
|
ip = net.ParseIP(val)
|
|
case net.IP:
|
|
ip = val
|
|
default:
|
|
src := IfaceAsString(in)
|
|
ip = net.ParseIP(src)
|
|
}
|
|
|
|
hx := hex.EncodeToString([]byte(ip))
|
|
if len(hx) < 8 {
|
|
return hx, nil
|
|
}
|
|
return "0x" + string([]byte(hx)[len(hx)-8:]), nil
|
|
}
|
|
|
|
// SIPURIHostConverter will return the
|
|
type SIPURIHostConverter struct{}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (*SIPURIHostConverter) Convert(in any) (out any, err error) {
|
|
return sipingo.HostFrom(IfaceAsString(in)), nil
|
|
}
|
|
|
|
// SIPURIUserConverter will return the
|
|
type SIPURIUserConverter struct{}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (*SIPURIUserConverter) Convert(in any) (out any, err error) {
|
|
return sipingo.UserFrom(IfaceAsString(in)), nil
|
|
}
|
|
|
|
// SIPURIMethodConverter will return the
|
|
type SIPURIMethodConverter struct{}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (*SIPURIMethodConverter) Convert(in any) (out any, err error) {
|
|
return sipingo.MethodFrom(IfaceAsString(in)), nil
|
|
}
|
|
|
|
func NewTimeStringConverter(params string) (hdlr DataConverter) {
|
|
return &TimeStringConverter{Layout: params}
|
|
}
|
|
|
|
type TimeStringConverter struct {
|
|
Layout string
|
|
}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (tS *TimeStringConverter) Convert(in any) (
|
|
out any, err error) {
|
|
tm, err := ParseTimeDetectLayout(in.(string), EmptyString)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return tm.Format(tS.Layout), nil
|
|
}
|
|
|
|
// String2HexConverter will transform the string to hex
|
|
type String2HexConverter struct{}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (*String2HexConverter) Convert(in any) (o any, err error) {
|
|
var out string
|
|
if out = hex.EncodeToString([]byte(IfaceAsString(in))); len(out) == 0 {
|
|
o = out
|
|
return
|
|
}
|
|
o = "0x" + out
|
|
return
|
|
}
|
|
|
|
// UnixTimeConverter converts the interface in the unix time
|
|
type UnixTimeConverter struct{}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (tS *UnixTimeConverter) Convert(in any) (
|
|
out any, err error) {
|
|
var tm time.Time
|
|
if tm, err = ParseTimeDetectLayout(in.(string), EmptyString); err != nil {
|
|
return
|
|
}
|
|
out = tm.Unix()
|
|
return
|
|
}
|
|
|
|
func NewRandomConverter(params string) (dflr DataConverter, err error) {
|
|
randConv := &RandomConverter{}
|
|
if params == EmptyString {
|
|
dflr = randConv
|
|
return
|
|
}
|
|
sls := strings.Split(params, InInFieldSep)
|
|
switch len(sls) {
|
|
case 2:
|
|
if sls[0] != EmptyString {
|
|
if randConv.begin, err = strconv.Atoi(sls[0]); err != nil {
|
|
return
|
|
}
|
|
}
|
|
if sls[1] != EmptyString {
|
|
if randConv.end, err = strconv.Atoi(sls[1]); err != nil {
|
|
return
|
|
}
|
|
}
|
|
case 1:
|
|
if randConv.begin, err = strconv.Atoi(sls[0]); err != nil {
|
|
return
|
|
}
|
|
}
|
|
dflr = randConv
|
|
return
|
|
}
|
|
|
|
type RandomConverter struct {
|
|
begin, end int
|
|
}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (rC *RandomConverter) Convert(in any) (
|
|
out any, err error) {
|
|
if rC.begin == 0 {
|
|
if rC.end == 0 {
|
|
return rand.Int(), nil
|
|
} else {
|
|
return rand.Intn(rC.end), nil
|
|
}
|
|
} else {
|
|
if rC.end == 0 {
|
|
return rand.Int() + rC.begin, nil
|
|
} else {
|
|
return int(RandomInteger(int64(rC.begin), int64(rC.end))), nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// LengthConverter returns the lenght of the slice
|
|
type LengthConverter struct{}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (LengthConverter) Convert(in any) (out any, err error) {
|
|
switch val := in.(type) {
|
|
case string:
|
|
return len(val), nil
|
|
case []string:
|
|
return len(val), nil
|
|
case []any:
|
|
return len(val), nil
|
|
case []bool:
|
|
return len(val), nil
|
|
case []int:
|
|
return len(val), nil
|
|
case []int8:
|
|
return len(val), nil
|
|
case []int16:
|
|
return len(val), nil
|
|
case []int32:
|
|
return len(val), nil
|
|
case []int64:
|
|
return len(val), nil
|
|
case []uint:
|
|
return len(val), nil
|
|
case []uint8:
|
|
return len(val), nil
|
|
case []uint16:
|
|
return len(val), nil
|
|
case []uint32:
|
|
return len(val), nil
|
|
case []uint64:
|
|
return len(val), nil
|
|
case []uintptr:
|
|
return len(val), nil
|
|
case []float32:
|
|
return len(val), nil
|
|
case []float64:
|
|
return len(val), nil
|
|
case []complex64:
|
|
return len(val), nil
|
|
case []complex128:
|
|
return len(val), nil
|
|
default:
|
|
return len(IfaceAsString(val)), nil
|
|
}
|
|
}
|
|
|
|
// SliceConverter converts the interface in the unix time
|
|
type SliceConverter struct{}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (SliceConverter) Convert(in any) (out any, err error) {
|
|
switch val := in.(type) {
|
|
case []string,
|
|
[]any,
|
|
[]bool,
|
|
[]int,
|
|
[]int8,
|
|
[]int16,
|
|
[]int32,
|
|
[]int64,
|
|
[]uint,
|
|
[]uint8,
|
|
[]uint16,
|
|
[]uint32,
|
|
[]uint64,
|
|
[]uintptr,
|
|
[]float32,
|
|
[]float64,
|
|
[]complex64,
|
|
[]complex128:
|
|
return val, nil
|
|
default:
|
|
src := IfaceAsString(in)
|
|
if strings.HasPrefix(src, IdxStart) &&
|
|
strings.HasSuffix(src, IdxEnd) { // it has a similar structure to a json marshaled slice
|
|
var slice []any
|
|
if err := json.Unmarshal([]byte(src), &slice); err == nil { // no error when unmarshal safe to asume that this is a slice
|
|
return slice, nil
|
|
}
|
|
}
|
|
return src, nil
|
|
}
|
|
}
|
|
|
|
type Float64Converter struct{}
|
|
|
|
// Convert implements DataConverter interface
|
|
func (Float64Converter) Convert(in any) (any, error) {
|
|
return IfaceAsFloat64(in)
|
|
}
|
|
|
|
// e164DomainConverter extracts the domain part out of a NAPTR name record
|
|
type e164DomainConverter struct{}
|
|
|
|
func (e164DomainConverter) Convert(in any) (any, error) {
|
|
name := IfaceAsString(in)
|
|
if i := strings.Index(name, ".e164."); i != -1 {
|
|
name = name[i:]
|
|
}
|
|
return strings.Trim(name, "."), nil
|
|
}
|
|
|
|
// e164Converter extracts the E164 address out of a NAPTR name record
|
|
type e164Converter struct{}
|
|
|
|
func (e164Converter) Convert(in any) (any, error) {
|
|
name := IfaceAsString(in)
|
|
i := strings.Index(name, ".e164.")
|
|
if i == -1 {
|
|
return nil, errors.New("unknown format")
|
|
}
|
|
return ReverseString(
|
|
strings.Replace(name[:i], ".", "", -1)), nil
|
|
}
|
|
|
|
// JSONConverter converts an object to json string
|
|
type JSONConverter struct{}
|
|
|
|
func (jsnC JSONConverter) Convert(in any) (any, error) {
|
|
b, err := json.Marshal(in)
|
|
if err != nil {
|
|
return EmptyString, err
|
|
}
|
|
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")
|
|
}
|
|
}
|