Skip to content

Commit

Permalink
Merge pull request #3 from claudioluciano/feature/standard_return_type
Browse files Browse the repository at this point in the history
feature: apply standard for time.Time return type
  • Loading branch information
rsfreitas authored May 17, 2023
2 parents d5d64c0 + 732b081 commit 1a8aa46
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 57 deletions.
20 changes: 2 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ The output format of the `Parse` function depends on the input type:

- Slice or Array: the output is a slice of the same length as the input slice or array. Each element of the output slice is the corresponding type of the input slice or array elements, represented as strings.

- Pointer to Struct, Slice, or Map: the output is a pointer to the corresponding JSON schema-like representation.

- Other types: the output is the type name as a string.
- Primitive types: the output is the type name as a string.

### Examples

Expand Down Expand Up @@ -89,22 +87,8 @@ parsed := Parse(v)
// "int",
// }

// Pointer to Struct
v := &struct {
Age int
Name string
}{
Age: 30,
Name: "John",
}

parsed := Parse(v)
// parsed = *map[string]interface{}{
// "Age": "int",
// "Name": "string",
// }

// Other types
// Primitive types
v := true

parsed := Parse(v)
Expand Down
52 changes: 22 additions & 30 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,22 @@ import (

const timeStringKind = "time.Time"

/*
Parse receives anything and tries to parse it to a JSON schema-like representation
that has the same type as the input.
For example, if the input is a struct, the output will be a map with the same fields
and nested types as the input struct.
If the input is a slice or array, the output will be a slice of the same length
containing the JSON schema-like representation of each element in the input.
If the input is a map, the output will be a map with the same keys and nested types
as the input map.
If the input is a pointer to a struct, slice, or map, the output will be a pointer to
the corresponding JSON schema-like representation.
For other types (e.g. bool, string, int), the output will be the type name as a string.
The function returns an interface{} value that can be asserted to the appropriate type
after the function call.
*/
// Parse receives anything and tries to parse it to a JSON schema-like representation
// that has the same type as the input.
//
// For example, if the input is a struct, the output will be a map with the same fields
// and nested types as the input struct.
//
// If the input is a slice or array, the output will be a slice of the same length
// containing the JSON schema-like representation of each element in the input.
//
// If the input is a map, the output will be a map with the same keys and nested types
// as the input map.
//
// For primitive types (e.g. bool, string, int), the output will be the type name as a string.
//
// The function returns an interface{} value that can be asserted to the appropriate type
// after the function call.
func Parse(in interface{}) interface{} {
v := reflect.ValueOf(in)

Expand All @@ -38,7 +33,7 @@ func parse(v reflect.Value) interface{} {
switch v.Kind() {
case reflect.Struct:
if ok := isTime(v); ok {
return "DateTime"
return "date-time"
}

return parseStruct(v)
Expand All @@ -57,7 +52,7 @@ func parse(v reflect.Value) interface{} {

func parseNil(v reflect.Value) interface{} {
if ok := isTime(v); ok {
return "DateTime"
return "date-time"
}

return v.Type().String()
Expand All @@ -67,14 +62,13 @@ func parseStruct(v reflect.Value) map[string]interface{} {
m := make(map[string]interface{})

for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
structfield := v.Type().Field(i)
field := v.Type().Field(i)

if !structfield.IsExported() {
if !field.IsExported() {
continue
}

m[structfield.Name] = parse(field)
m[field.Name] = parse(v.Field(i))
}

return m
Expand All @@ -99,7 +93,6 @@ func parseMap(v reflect.Value) interface{} {

for _, key := range v.MapKeys() {
value := v.MapIndex(key)

m[key.String()] = parse(value)
}

Expand All @@ -118,11 +111,10 @@ func parseSlice(v reflect.Value) interface{} {
return fmt.Sprintf("[%v]", elType)
}

m := []interface{}{}
var m []interface{}
for i := 0; i < v.Len(); i++ {
field := v.Index(i)
t := parse(field)

m = append(m, t)
}

Expand Down
20 changes: 11 additions & 9 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ type User struct {
}

type Event struct {
ID int
Title string
Date time.Time
Guests []Person
Details map[string]string
ID int
Title string
Date time.Time
Guests []Person
Details map[string]string
PtrDate *time.Time
unexported string
}

func TestParse(t *testing.T) {
Expand Down Expand Up @@ -56,15 +58,15 @@ func TestParse(t *testing.T) {
expected: `{"Age":"int","Name":"string"}`,
},
{
name: "struct with time.Time field",
name: "struct with one time.Time field filled and the other nil",
input: Event{
ID: 1,
Title: "Party",
Date: time.Now(),
Guests: []Person{{Name: "Alice", Age: 30}, {Name: "Bob", Age: 40}},
Details: map[string]string{"location": "New York", "host": "John"},
},
expected: `{"Date":"DateTime","Details": {"location": "string", "host": "string"},"Guests":"[{"Age":"int","Name":"string"}, {"Age":"int","Name":"string"}]","ID":"int","Title":"string"}`,
expected: `{"Date":"date-time","Details": {"location": "string", "host": "string"},"Guests":"[{"Age":"int","Name":"string"}, {"Age":"int","Name":"string"}]","ID":"int","Title":"string","PtrDate":"date-time"}`,
},
{
name: "slice",
Expand Down Expand Up @@ -113,11 +115,11 @@ func TestParse(t *testing.T) {

expectedMap := make(map[string]interface{})
expectedBytes, _ := json.Marshal(actual)
json.Unmarshal(expectedBytes, &expectedMap)
_ = json.Unmarshal(expectedBytes, &expectedMap)

actualMap := make(map[string]interface{})
actualBytes, _ := json.Marshal(actual)
json.Unmarshal(actualBytes, &actualMap)
_ = json.Unmarshal(actualBytes, &actualMap)

if !reflect.DeepEqual(actualMap, expectedMap) {
t.Errorf("Expected %v, but got %v", tt.expected, actual)
Expand Down

0 comments on commit 1a8aa46

Please sign in to comment.