Skip to content

Commit f8d6180

Browse files
committed
added transformation functionality
1 parent aecb53e commit f8d6180

File tree

3 files changed

+217
-4
lines changed

3 files changed

+217
-4
lines changed

validator/transformers.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package validator
2+
3+
import (
4+
"strconv"
5+
"strings"
6+
7+
"golang.org/x/text/cases"
8+
"golang.org/x/text/language"
9+
)
10+
11+
// RemoveSpecialChars removes special characters from a string.
12+
func RemoveSpecialChars(value interface{}) interface{} {
13+
if str, ok := value.(string); ok {
14+
var result strings.Builder
15+
for _, char := range str {
16+
if (char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9') || char == ' ' {
17+
result.WriteRune(char)
18+
}
19+
}
20+
return result.String()
21+
}
22+
return value
23+
}
24+
25+
// ToTitleCase converts a string to title case.
26+
func ToTitleCase(value interface{}) interface{} {
27+
if str, ok := value.(string); ok {
28+
// Create a title caser for the English language
29+
caser := cases.Title(language.French)
30+
return caser.String(strings.ToLower(str))
31+
}
32+
return value
33+
}
34+
35+
// ToInt converts a string or float to an integer.
36+
func ToInt(value interface{}) interface{} {
37+
switch v := value.(type) {
38+
case string:
39+
if i, err := strconv.Atoi(v); err == nil {
40+
return i
41+
}
42+
case float64:
43+
return int(v)
44+
}
45+
return value
46+
}
47+
48+
// ToFloat converts a string or integer to a float.
49+
func ToFloat(value interface{}) interface{} {
50+
switch v := value.(type) {
51+
case string:
52+
if f, err := strconv.ParseFloat(v, 64); err == nil {
53+
return f
54+
}
55+
case int:
56+
return float64(v)
57+
}
58+
return value
59+
}
60+
61+
// Truncate truncates a string to a specified maximum length.
62+
func Truncate(maxLength int) Transformer {
63+
return func(value interface{}) interface{} {
64+
if str, ok := value.(string); ok {
65+
if len(str) > maxLength {
66+
return str[:maxLength]
67+
}
68+
}
69+
return value
70+
}
71+
}
72+
73+
// Replace replaces occurrences of a substring with another string.
74+
func Replace(old, new string) Transformer {
75+
return func(value interface{}) interface{} {
76+
if str, ok := value.(string); ok {
77+
return strings.ReplaceAll(str, old, new)
78+
}
79+
return value
80+
}
81+
}

validator/transformers_test.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package validator
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestRemoveSpecialChars(t *testing.T) {
8+
tests := []struct {
9+
input interface{}
10+
expected interface{}
11+
}{
12+
{"Hello!@# World!", "Hello World"},
13+
{"123#456", "123456"},
14+
{123, 123}, // Non-string input
15+
}
16+
17+
for _, test := range tests {
18+
result := RemoveSpecialChars(test.input)
19+
if result != test.expected {
20+
t.Errorf("RemoveSpecialChars(%v) = %v, expected %v", test.input, result, test.expected)
21+
}
22+
}
23+
}
24+
25+
func TestToTitleCase(t *testing.T) {
26+
tests := []struct {
27+
input interface{}
28+
expected interface{}
29+
}{
30+
{"hello world", "Hello World"},
31+
{"HELLO WORLD", "Hello World"},
32+
{"çimant", "Çimant"},
33+
{"hello, world!", "Hello, World!"}, // Handles punctuation correctly
34+
{"123", "123"}, // Non-alphabetic input
35+
{123, 123}, // Non-string input
36+
}
37+
38+
for _, test := range tests {
39+
result := ToTitleCase(test.input)
40+
if result != test.expected {
41+
t.Errorf("ToTitleCase(%v) = %v, expected %v", test.input, result, test.expected)
42+
}
43+
}
44+
}
45+
46+
func TestToInt(t *testing.T) {
47+
tests := []struct {
48+
input interface{}
49+
expected interface{}
50+
}{
51+
{"123", 123},
52+
{123.45, 123},
53+
{"abc", "abc"}, // Invalid input
54+
}
55+
56+
for _, test := range tests {
57+
result := ToInt(test.input)
58+
if result != test.expected {
59+
t.Errorf("ToInt(%v) = %v, expected %v", test.input, result, test.expected)
60+
}
61+
}
62+
}
63+
64+
func TestToFloat(t *testing.T) {
65+
tests := []struct {
66+
input interface{}
67+
expected interface{}
68+
}{
69+
{"123.45", 123.45},
70+
{123, 123.0},
71+
{"abc", "abc"}, // Invalid input
72+
}
73+
74+
for _, test := range tests {
75+
result := ToFloat(test.input)
76+
if result != test.expected {
77+
t.Errorf("ToFloat(%v) = %v, expected %v", test.input, result, test.expected)
78+
}
79+
}
80+
}
81+
82+
func TestTruncate(t *testing.T) {
83+
transformer := Truncate(5)
84+
85+
tests := []struct {
86+
input interface{}
87+
expected interface{}
88+
}{
89+
{"hello world", "hello"},
90+
{"hi", "hi"},
91+
{123, 123}, // Non-string input
92+
}
93+
94+
for _, test := range tests {
95+
result := transformer(test.input)
96+
if result != test.expected {
97+
t.Errorf("Truncate(%v) = %v, expected %v", test.input, result, test.expected)
98+
}
99+
}
100+
}
101+
102+
func TestReplace(t *testing.T) {
103+
transformer := Replace("foo", "bar")
104+
105+
tests := []struct {
106+
input interface{}
107+
expected interface{}
108+
}{
109+
{"foo bar", "bar bar"},
110+
{"hello world", "hello world"},
111+
{123, 123}, // Non-string input
112+
}
113+
114+
for _, test := range tests {
115+
result := transformer(test.input)
116+
if result != test.expected {
117+
t.Errorf("Replace(%v) = %v, expected %v", test.input, result, test.expected)
118+
}
119+
}
120+
}

validator/validator.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import "fmt"
55
// ValidatorFunc is a function that validates a field and returns an error if validation fails.
66
type ValidatorFunc func(value interface{}) error
77

8+
// TransformerFunc is a function that transforms a value.
9+
type Transformer func(value interface{}) interface{}
10+
811
// Validator defines a validator function and its error message.
912
type Validator struct {
1013
Func ValidatorFunc
@@ -13,10 +16,11 @@ type Validator struct {
1316

1417
// ValidationOption defines the validation rules for a specific field.
1518
type ValidationOption struct {
16-
Key string // Field name in the request body
17-
IsOptional bool // Whether the field is optional
18-
Validators []Validator // List of validators for the field
19-
Nested []ValidationOption // Validation options for nested objects
19+
Key string // Field name in the request body
20+
IsOptional bool // Whether the field is optional
21+
Validators []Validator // List of validators for the field
22+
Transformers []Transformer // List of transformers for the field
23+
Nested []ValidationOption // Validation options for nested objects
2024
}
2125

2226
// Validate checks the request body against the validation options and returns the first error.
@@ -29,6 +33,14 @@ func Validate(body map[string]interface{}, options []ValidationOption) error {
2933
continue
3034
}
3135

36+
// Apply transformations
37+
if exists {
38+
for _, transformer := range option.Transformers {
39+
value = transformer(value)
40+
}
41+
body[option.Key] = value // Update the body with the transformed value
42+
}
43+
3244
// Run all validators for the field (if it exists)
3345
for _, validator := range option.Validators {
3446
if err := validator.Func(value); err != nil {

0 commit comments

Comments
 (0)