all repos

json2go @ 1609c23952004c5a160a5737c4ac8e9fac80292b

convert json to go type annotations
2 files changed, 43 insertions(+), 9 deletions(-)
feat: validate provided struct name
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2025-11-26 20:05:07 +0200
Authored at: 2025-11-26 19:50:22 +0200
Change ID: nxznoqyxnmxwnuxnkzruyrvvptxluuwn
Parent: 7a527f3
M json2go.go
···
        4
        4
         	"encoding/json"

      
        5
        5
         	"errors"

      
        6
        6
         	"fmt"

      
        
        7
        +	"regexp"

      
        7
        8
         	"sort"

      
        8
        9
         	"strings"

      
        9
        10
         )

      
        10
        11
         

      
        11
        
        -var ErrInvalidJSON = errors.New("invalid json")

      
        
        12
        +var identRe = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`)

      
        
        13
        +var (

      
        
        14
        +	ErrInvalidJSON       = errors.New("invalid json")

      
        
        15
        +	ErrInvalidStructName = errors.New("invalid struct name")

      
        
        16
        +)

      
        12
        17
         

      
        13
        18
         type Transformer struct {

      
        14
        19
         	structName string

      ···
        21
        26
         // Transform ...

      
        22
        27
         // todo: take io.Reader as input?

      
        23
        28
         // todo: output as io.Writer?

      
        24
        
        -// todo: validate provided structName

      
        25
        29
         func (t *Transformer) Transform(structName, jsonStr string) (string, error) {

      
        
        30
        +	if !identRe.MatchString(structName) {

      
        
        31
        +		return "", ErrInvalidStructName

      
        
        32
        +	}

      
        
        33
        +

      
        26
        34
         	t.structName = structName

      
        27
        35
         

      
        28
        36
         	var input any

      ···
        65
        73
         	}

      
        66
        74
         }

      
        67
        75
         

      
        68
        
        -// todo: input shouldn't be map, to preserve it's order

      
        69
        76
         func (t *Transformer) buildStruct(input map[string]any) string {

      
        70
        77
         	var fields strings.Builder

      
        71
        78
         	for _, f := range mapToStructInput(input) {

      
M json2go_test.go
···
        22
        22
         

      
        23
        23
         func TestTransformer_Transform(t *testing.T) {

      
        24
        24
         	tests := map[string]struct {

      
        25
        
        -		input  string

      
        26
        
        -		output string

      
        27
        
        -		err    error

      
        
        25
        +		input      string

      
        
        26
        +		output     string

      
        
        27
        +		structName string

      
        
        28
        +		err        error

      
        28
        29
         	}{

      
        29
        30
         		"simple object": {

      
        30
        31
         			input: `{"name": "Olex", "active": true, "age": 420}`,

      ···
        37
        38
         		"invalid json": {

      
        38
        39
         			err:   ErrInvalidJSON,

      
        39
        40
         			input: `{"invalid":json}`,

      
        
        41
        +		},

      
        
        42
        +		"invalid struct name, starts with number": {

      
        
        43
        +			err:        ErrInvalidStructName,

      
        
        44
        +			structName: "1Name",

      
        
        45
        +		},

      
        
        46
        +		"invalid struct name, has space": {

      
        
        47
        +			err:        ErrInvalidStructName,

      
        
        48
        +			structName: "Name Name2",

      
        
        49
        +		},

      
        
        50
        +		"invalid struct name, has non letter/number": {

      
        
        51
        +			err:        ErrInvalidStructName,

      
        
        52
        +			structName: "Name$",

      
        40
        53
         		},

      
        41
        54
         		"snake_case to CamelCase": {

      
        42
        55
         			input: `{"first_name": "Bob", "last_name": "Bobberson"}`,

      ···
        93
        106
         	trans := NewTransformer()

      
        94
        107
         	for tname, tt := range tests {

      
        95
        108
         		t.Run(tname, func(t *testing.T) {

      
        96
        
        -			result, err := trans.Transform("Out", tt.input)

      
        
        109
        +			sn := "Out"

      
        
        110
        +			if tt.structName != "" {

      
        
        111
        +				sn = tt.structName

      
        
        112
        +			}

      
        
        113
        +

      
        
        114
        +			result, err := trans.Transform(sn, tt.input)

      
        97
        115
         			assertEqualErr(t, tt.err, err)

      
        98
        116
         			assertEqual(t, tt.output, result)

      
        99
        117
         		})

      ···
        102
        120
         

      
        103
        121
         func assertEqualErr(t *testing.T, expected, actual error) {

      
        104
        122
         	t.Helper()

      
        105
        
        -	if (expected != nil || actual != nil) && errors.Is(expected, actual) {

      
        106
        
        -		t.Errorf("expected: %v, got: %v\n", expected, actual)

      
        
        123
        +	if expected == nil && actual == nil {

      
        
        124
        +		return

      
        
        125
        +	}

      
        
        126
        +

      
        
        127
        +	if expected == nil || actual == nil {

      
        
        128
        +		t.Errorf("expected: %v, got: %v", expected, actual)

      
        
        129
        +		return

      
        
        130
        +	}

      
        
        131
        +

      
        
        132
        +	if !errors.Is(actual, expected) {

      
        
        133
        +		t.Errorf("expected error: %v, got: %v", expected, actual)

      
        107
        134
         	}

      
        108
        135
         }

      
        109
        136