Repository: fatih/gomodifytags
Pull request #68 (2020-10-02 07:35:50)

Adding feature modify tags by struct field name
Code change for issue https://github.com/fatih/gomodifytags/issues/67

Notes:
- Adding new feature modify tags by struct field name
- How to use:
`gomodifytags -file demo.go -struct Server -field Name -add-tags validate:gt=1`
- Adding `-field` parameter without `-struct` will return error


Patch:
diff --git a/README.md b/README.md
index 7561b75..8cc6b67 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,8 @@ to change:
 * `-struct`: This accepts the struct name. i.e: `-struct Server`. The name
   should be a valid type name. The `-struct` flag selects the whole struct, and
   thus it will operate on all fields.
+* `-field`: This accepts a field name. i.e: `-field Address`. Useful to select
+  a certain field. The name should be a valid field name. The `-struct` flag is required.
 * `-offset`: This accepts a byte offset of the file. Useful for editors to pass
   the position under the cursor. i.e: `-offset 548`. The offset has to be
   inside a valid struct. The `-offset` selects the whole struct. If you need
diff --git a/main.go b/main.go
index 821a55b..4585c31 100644
--- a/main.go
+++ b/main.go
@@ -49,6 +49,7 @@ type config struct {
 
 	offset     int
 	structName string
+	fieldName  string
 	line       string
 	start, end int
 	all        bool
@@ -93,6 +94,7 @@ func realMain() error {
 		flagLine = flag.String("line", "",
 			"Line number of the field or a range of line. i.e: 4 or 4,8")
 		flagStruct = flag.String("struct", "", "Struct name to be processed")
+		flagField  = flag.String("field", "", "Field name to be processed")
 		flagAll    = flag.Bool("all", false, "Select all structs to be processed")
 
 		// tag flags
@@ -135,6 +137,7 @@ func realMain() error {
 		file:                 *flagFile,
 		line:                 *flagLine,
 		structName:           *flagStruct,
+		fieldName:            *flagField,
 		offset:               *flagOffset,
 		all:                  *flagAll,
 		output:               *flagOutput,
@@ -576,13 +579,38 @@ func (c *config) structSelection(file ast.Node) (int, int, error) {
 		return 0, 0, errors.New("struct name does not exist")
 	}
 
-	// struct selects all lines inside a struct
+	// if field name has been specified as well, only select the given field
+	if c.fieldName != "" {
+		return c.fieldSelection(encStruct)
+	}
+
 	start := c.fset.Position(encStruct.Pos()).Line
 	end := c.fset.Position(encStruct.End()).Line
 
 	return start, end, nil
 }
 
+func (c *config) fieldSelection(st *ast.StructType) (int, int, error) {
+	var encField *ast.Field
+	for _, f := range st.Fields.List {
+		for _, field := range f.Names {
+			if field.Name == c.fieldName {
+				encField = f
+			}
+		}
+	}
+
+	if encField == nil {
+		return 0, 0, errors.New(fmt.Sprintf("struct %q doesn't have field name %q",
+			c.structName, c.fieldName))
+	}
+
+	start := c.fset.Position(encField.Pos()).Line
+	end := c.fset.Position(encField.End()).Line
+
+	return start, end, nil
+}
+
 func (c *config) offsetSelection(file ast.Node) (int, int, error) {
 	structs := collectStructs(file)
 
@@ -727,6 +755,10 @@ func (c *config) validate() error {
 			" should be defined")
 	}
 
+	if c.fieldName != "" && c.structName == "" {
+		return errors.New("-field is requiring -struct")
+	}
+
 	return nil
 }
 


Test patch:
diff --git a/main_test.go b/main_test.go
index 452645a..827ec20 100644
--- a/main_test.go
+++ b/main_test.go
@@ -410,6 +410,63 @@ func TestRewrite(t *testing.T) {
 				transform: "snakecase",
 			},
 		},
+		{
+			file: "field_add",
+			cfg: &config{
+				add:        []string{"json"},
+				output:     "source",
+				structName: "foo",
+				fieldName:  "bar",
+				transform:  "snakecase",
+			},
+		},
+		{
+			file: "field_add_same_line",
+			cfg: &config{
+				add:        []string{"json"},
+				output:     "source",
+				structName: "foo",
+				fieldName:  "qux",
+				transform:  "snakecase",
+			},
+		},
+		{
+			file: "field_add_existing",
+			cfg: &config{
+				add:        []string{"json"},
+				output:     "source",
+				structName: "foo",
+				fieldName:  "bar",
+				transform:  "snakecase",
+			},
+		},
+		{
+			file: "field_clear_tags",
+			cfg: &config{
+				clear:      true,
+				output:     "source",
+				structName: "foo",
+				fieldName:  "bar",
+			},
+		},
+		{
+			file: "field_clear_options",
+			cfg: &config{
+				clearOption: true,
+				output:      "source",
+				structName:  "foo",
+				fieldName:   "bar",
+			},
+		},
+		{
+			file: "field_remove",
+			cfg: &config{
+				remove:     []string{"json"},
+				output:     "source",
+				structName: "foo",
+				fieldName:  "bar",
+			},
+		},
 	}
 
 	for _, ts := range test {
diff --git a/test-fixtures/field_add.golden b/test-fixtures/field_add.golden
new file mode 100644
index 0000000..f94ccad
--- /dev/null
+++ b/test-fixtures/field_add.golden
@@ -0,0 +1,7 @@
+package foo
+
+type foo struct {
+	bar       string `json:"bar"`
+	qaz, qux  string
+	timestamp time.Time
+}
diff --git a/test-fixtures/field_add.input b/test-fixtures/field_add.input
new file mode 100644
index 0000000..9dcca2f
--- /dev/null
+++ b/test-fixtures/field_add.input
@@ -0,0 +1,7 @@
+package foo
+
+type foo struct {
+	bar       string
+	qaz, qux  string
+	timestamp time.Time
+}
diff --git a/test-fixtures/field_add_existing.golden b/test-fixtures/field_add_existing.golden
new file mode 100644
index 0000000..075e228
--- /dev/null
+++ b/test-fixtures/field_add_existing.golden
@@ -0,0 +1,7 @@
+package foo
+
+type foo struct {
+	bar       string `json:"bar"`
+	baz       string
+	timestamp time.Time `json:"@timestamp"`
+}
diff --git a/test-fixtures/field_add_existing.input b/test-fixtures/field_add_existing.input
new file mode 100644
index 0000000..31afaaa
--- /dev/null
+++ b/test-fixtures/field_add_existing.input
@@ -0,0 +1,7 @@
+package foo
+
+type foo struct {
+	bar       string
+	baz       string
+	timestamp time.Time `json:"@timestamp"`
+}
diff --git a/test-fixtures/field_add_same_line.golden b/test-fixtures/field_add_same_line.golden
new file mode 100644
index 0000000..daeccc0
--- /dev/null
+++ b/test-fixtures/field_add_same_line.golden
@@ -0,0 +1,7 @@
+package foo
+
+type foo struct {
+	bar       string
+	qaz, qux  string `json:"qaz"`
+	timestamp time.Time
+}
diff --git a/test-fixtures/field_add_same_line.input b/test-fixtures/field_add_same_line.input
new file mode 100644
index 0000000..9dcca2f
--- /dev/null
+++ b/test-fixtures/field_add_same_line.input
@@ -0,0 +1,7 @@
+package foo
+
+type foo struct {
+	bar       string
+	qaz, qux  string
+	timestamp time.Time
+}
diff --git a/test-fixtures/field_clear_options.golden b/test-fixtures/field_clear_options.golden
new file mode 100644
index 0000000..4cb197b
--- /dev/null
+++ b/test-fixtures/field_clear_options.golden
@@ -0,0 +1,9 @@
+package foo
+
+type foo struct {
+	bar string `json:"bar" hcl:"bar"`
+	t   bool   `hcl:"t"`
+	t   bool   `hcl:"t,omitempty"`
+	qux string `json:"qux,omitempty" hcl:"qux,squash,keys"`
+	yoo string `json:"yoo"`
+}
diff --git a/test-fixtures/field_clear_options.input b/test-fixtures/field_clear_options.input
new file mode 100644
index 0000000..5d03cfd
--- /dev/null
+++ b/test-fixtures/field_clear_options.input
@@ -0,0 +1,9 @@
+package foo
+
+type foo struct {
+	bar string `json:"bar,omitempty" hcl:"bar,omitnested"`
+	t   bool   `hcl:"t"`
+	t   bool   `hcl:"t,omitempty"`
+	qux string `json:"qux,omitempty" hcl:"qux,squash,keys"`
+	yoo string `json:"yoo"`
+}
diff --git a/test-fixtures/field_clear_tags.golden b/test-fixtures/field_clear_tags.golden
new file mode 100644
index 0000000..c128ef8
--- /dev/null
+++ b/test-fixtures/field_clear_tags.golden
@@ -0,0 +1,9 @@
+package foo
+
+type foo struct {
+	bar string 
+	t   bool   `hcl:"t"`
+	t   bool   `hcl:"t,omitempty"`
+	qux string `json:"qux,omitempty" hcl:"qux,squash,keys"`
+	yoo string `json:"yoo"`
+}
diff --git a/test-fixtures/field_clear_tags.input b/test-fixtures/field_clear_tags.input
new file mode 100644
index 0000000..5d03cfd
--- /dev/null
+++ b/test-fixtures/field_clear_tags.input
@@ -0,0 +1,9 @@
+package foo
+
+type foo struct {
+	bar string `json:"bar,omitempty" hcl:"bar,omitnested"`
+	t   bool   `hcl:"t"`
+	t   bool   `hcl:"t,omitempty"`
+	qux string `json:"qux,omitempty" hcl:"qux,squash,keys"`
+	yoo string `json:"yoo"`
+}
diff --git a/test-fixtures/field_remove.golden b/test-fixtures/field_remove.golden
new file mode 100644
index 0000000..6d47e9c
--- /dev/null
+++ b/test-fixtures/field_remove.golden
@@ -0,0 +1,7 @@
+package foo
+
+type foo struct {
+	bar string 
+	baz string `json:"baz"`
+	t   bool   `hcl:"t"`
+}
diff --git a/test-fixtures/field_remove.input b/test-fixtures/field_remove.input
new file mode 100644
index 0000000..2652c96
--- /dev/null
+++ b/test-fixtures/field_remove.input
@@ -0,0 +1,7 @@
+package foo
+
+type foo struct {
+	bar string `json:"bar"`
+	baz string `json:"baz"`
+	t   bool   `hcl:"t"`
+}