Skip to content

Commit

Permalink
feat: Added ability to parse toml files
Browse files Browse the repository at this point in the history
In yoheimuta#338 the distribution of python wheels is requested.

Python relies according to PEP 518 on pyproject.toml
allowing to add a [tools] table with all config values.

The pyproject.toml can now be added as a valid project
configuration source.
  • Loading branch information
carstencodes committed Dec 18, 2023
1 parent c72c4ac commit f22f0a7
Show file tree
Hide file tree
Showing 40 changed files with 386 additions and 94 deletions.
6 changes: 6 additions & 0 deletions _testdata/py_project/project_with_yaml/protolint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
lint:
rules_option:
indent:
style: tab
newline: "\n"
7 changes: 7 additions & 0 deletions _testdata/py_project/project_with_yaml/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
name = "protolint_user"
version = "0.1.0"

[tools.protolint.rules_option.indent]
style = "\t"
newline = "\r\n"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
name = "protolint_user"
version = "0.1.0"

[tools.protolint.rules_option.indent]
style = "\t"
newline = "\r\n"
6 changes: 6 additions & 0 deletions _testdata/py_project/project_with_yaml_parent/protolint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
lint:
rules_option:
indent:
style: tab
newline: "\n"
3 changes: 3 additions & 0 deletions _testdata/py_project/pyproject/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[tools.protolint.rules_option.indent]
style = "\t"
newline = "\n"
6 changes: 6 additions & 0 deletions _testdata/py_project/pyproject_no_protolint/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[project]
name = "protolint_user"
version = "0.1.0"

[tools]
y = "x"
3 changes: 3 additions & 0 deletions _testdata/py_project/pyproject_no_tools/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[project]
name = "protolint_user"
version = "0.1.0"
7 changes: 7 additions & 0 deletions _testdata/py_project/with_pyproject/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
name = "protolint_user"
version = "0.1.0"

[tools.protolint.rules_option.indent]
style = "\t"
newline = "\n"
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module github.com/yoheimuta/protolint

require (
github.com/BurntSushi/toml v1.3.2
github.com/chavacava/garif v0.0.0-20230608123814-4bd63c2919ab
github.com/gertd/go-pluralize v0.2.0
github.com/golang/protobuf v1.5.2
github.com/hashicorp/go-hclog v1.2.0
Expand All @@ -12,7 +14,6 @@ require (
)

require (
github.com/chavacava/garif v0.0.0-20230608123814-4bd63c2919ab // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
Expand Down
8 changes: 2 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 h1:W9o46d2kbNL06lq7UNDPV0zYLzkrde/bjIqO02eoll0=
github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo=
github.com/chavacava/garif v0.0.0-20230608123814-4bd63c2919ab h1:5JxePczlyGAtj6R1MUEFZ/UFud6FfsOejq7xLC2ZIb0=
github.com/chavacava/garif v0.0.0-20230608123814-4bd63c2919ab/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
Expand Down Expand Up @@ -87,12 +87,9 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/yoheimuta/go-protoparser/v4 v4.7.0 h1:80LGfVM25sCoNDD08hv9O0ShQMjoTrIE76j5ON+gq3U=
Expand Down Expand Up @@ -177,7 +174,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Expand Down
2 changes: 1 addition & 1 deletion internal/linter/config/customizableSeverityOption.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "github.com/yoheimuta/protolint/linter/rule"
// CustomizableSeverityOption represents an option where the
// severity of a rule can be configured via yaml.
type CustomizableSeverityOption struct {
severity *rule.Severity `yaml:"severity"`
severity *rule.Severity `yaml:"severity" toml:"severity"`
}

// Severity returns the configured severity. If no severity
Expand Down
2 changes: 1 addition & 1 deletion internal/linter/config/directories.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

// Directories represents the target directories.
type Directories struct {
Exclude []string `yaml:"exclude"`
Exclude []string `yaml:"exclude" json:"exclude" toml:"exclude"`
}

func (d Directories) shouldSkipRule(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package config
// EnumFieldNamesZeroValueEndWithOption represents the option for the ENUM_FIELD_NAMES_ZERO_VALUE_END_WITH rule.
type EnumFieldNamesZeroValueEndWithOption struct {
CustomizableSeverityOption
Suffix string `yaml:"suffix"`
Suffix string `yaml:"suffix" json:"suffix" toml:"suffix"`
}
2 changes: 1 addition & 1 deletion internal/linter/config/enumFieldsHaveCommentOption.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package config
// EnumFieldsHaveCommentOption represents the option for the ENUM_FIELDS_HAVE_COMMENT rule.
type EnumFieldsHaveCommentOption struct {
CustomizableSeverityOption
ShouldFollowGolangStyle bool `yaml:"should_follow_golang_style"`
ShouldFollowGolangStyle bool `yaml:"should_follow_golang_style" json:"should_follow_golang_style" toml:"should_follow_golang_style"`
}
2 changes: 1 addition & 1 deletion internal/linter/config/enumsHaveCommentOption.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package config
// EnumsHaveCommentOption represents the option for the ENUMS_HAVE_COMMENT rule.
type EnumsHaveCommentOption struct {
CustomizableSeverityOption
ShouldFollowGolangStyle bool `yaml:"should_follow_golang_style"`
ShouldFollowGolangStyle bool `yaml:"should_follow_golang_style" json:"should_follow_golang_style" toml:"should_follow_golang_style"`
}
16 changes: 1 addition & 15 deletions internal/linter/config/externalConfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@ type Lint struct {
Files Files
Directories Directories
Rules Rules
RulesOption RulesOption `yaml:"rules_option" json:"rules_option"`
}

type embeddedConfig struct {
Protolint *Lint `json:"protolint"`
RulesOption RulesOption `yaml:"rules_option" json:"rules_option" toml:"rules_option"`
}

// ExternalConfig represents the external configuration.
Expand All @@ -31,13 +27,3 @@ func (c ExternalConfig) ShouldSkipRule(
lint.Directories.shouldSkipRule(displayPath) ||
lint.Rules.shouldSkipRule(ruleID, defaultRuleIDs)
}

func (p embeddedConfig) toExternalConfig() *ExternalConfig {
if p.Protolint == nil {
return nil
}

return &ExternalConfig{
Lint: *p.Protolint,
}
}
16 changes: 16 additions & 0 deletions internal/linter/config/externalConfigProvider.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ func getLoaderFromExtension(filePath string) (configLoader, error) {
if strings.HasSuffix(filePath, packageJsonFileNameForJsExtension) {
return jsonConfigLoader{filePath: filePath}, nil
}
if strings.HasSuffix(filePath, pyProjectTomlFileNameForPyExtension) {
return tomlConfigLoader{filePath: filePath}, nil
}

return nil, fmt.Errorf("%s is not a valid support file extension", filePath)
}
Expand Down Expand Up @@ -115,5 +118,18 @@ func getExternalConfigLoader(
return jsonConfigLoader{filePath: filePath}, nil
}

// after checking for protolint yaml and npm.json files, go for pyproject.toml of python
for _, dir := range dirPaths {
filePath := filepath.Join(dir, pyProjectTomlFileNameForPy)
checkedPaths = append(checkedPaths, filePath)
if _, err := os.Stat(filePath); err != nil {
if os.IsNotExist(err) {
continue
}
return nil, err
}
return tomlConfigLoader{filePath: filePath}, nil
}

return nil, fmt.Errorf("not found config file by searching `%s`", strings.Join(checkedPaths, ","))
}
63 changes: 59 additions & 4 deletions internal/linter/config/externalConfigProvider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ func TestGetExternalConfig(t *testing.T) {
},
},
Rules: struct {
NoDefault bool `yaml:"no_default"`
AllDefault bool `yaml:"all_default"`
Add []string `yaml:"add"`
Remove []string `yaml:"remove"`
NoDefault bool `yaml:"no_default" json:"no_default" toml:"no_default"`
AllDefault bool `yaml:"all_default" json:"all_default" toml:"all_default"`
Add []string `yaml:"add" json:"add" toml:"add"`
Remove []string `yaml:"remove" json:"remove" toml:"remove"`
}{
NoDefault: true,
Add: []string{
Expand Down Expand Up @@ -226,6 +226,16 @@ func TestGetExternalConfig(t *testing.T) {
cwdPath: setting_test.TestDataPath("js_config", "package_no_protolint"),
wantExternalConfig: nil,
},
{
name: "found a pyproject.toml without 'tools.protolint' in it",
cwdPath: setting_test.TestDataPath("py_project", "pyproject_no_protolint"),
wantExternalConfig: nil,
},
{
name: "found a pyproject.toml without 'tools' in it",
cwdPath: setting_test.TestDataPath("py_project", "pyproject_no_tools"),
wantExternalConfig: nil,
},
{
name: "found a package.json with 'protolint' and other stuff in it",
cwdPath: setting_test.TestDataPath("js_config", "non_pure_package"),
Expand All @@ -241,6 +251,21 @@ func TestGetExternalConfig(t *testing.T) {
},
},
},
{
name: "found a pyproject.toml with 'tools.protolint' and other stuff in it",
cwdPath: setting_test.TestDataPath("py_project", "with_pyproject"),
wantExternalConfig: &config.ExternalConfig{
SourcePath: "pyproject.toml",
Lint: config.Lint{
RulesOption: config.RulesOption{
Indent: config.IndentOption{
Style: "\t",
Newline: "\n",
},
},
},
},
},
{
name: "found a package.json with 'protolint' and other stuff in it, but superseded by sibling protolint.yaml",
cwdPath: setting_test.TestDataPath("js_config", "package_with_yaml"),
Expand All @@ -256,6 +281,21 @@ func TestGetExternalConfig(t *testing.T) {
},
},
},
{
name: "found a pyproject.toml with 'toolsprotolint' and other stuff in it, but superseded by sibling protolint.yaml",
cwdPath: setting_test.TestDataPath("py_project", "project_with_yaml"),
wantExternalConfig: &config.ExternalConfig{
SourcePath: "protolint.yaml",
Lint: config.Lint{
RulesOption: config.RulesOption{
Indent: config.IndentOption{
Style: "\t",
Newline: "\n",
},
},
},
},
},
{
name: "found a package.json with 'protolint' and other stuff in it, but superseded by parent protolint.yaml",
cwdPath: setting_test.TestDataPath("js_config", "package_with_yaml_parent", "child"),
Expand All @@ -271,6 +311,21 @@ func TestGetExternalConfig(t *testing.T) {
},
},
},
{
name: "found a pyproject.toml with 'toolsprotolint' and other stuff in it, but superseded by parent protolint.yaml",
cwdPath: setting_test.TestDataPath("py_project", "project_with_yaml_parent", "child"),
wantExternalConfig: &config.ExternalConfig{
SourcePath: setting_test.TestDataPath("py_project", "project_with_yaml_parent", "protolint.yaml"),
Lint: config.Lint{
RulesOption: config.RulesOption{
Indent: config.IndentOption{
Style: "\t",
Newline: "\n",
},
},
},
},
},
} {
test := test
t.Run(test.name, func(t *testing.T) {
Expand Down
16 changes: 8 additions & 8 deletions internal/linter/config/externalConfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ func TestExternalConfig_ShouldSkipRule(t *testing.T) {
},
},
Rules: struct {
NoDefault bool `yaml:"no_default"`
AllDefault bool `yaml:"all_default"`
Add []string `yaml:"add"`
Remove []string `yaml:"remove"`
NoDefault bool `yaml:"no_default" json:"no_default" toml:"no_default"`
AllDefault bool `yaml:"all_default" json:"all_default" toml:"all_default"`
Add []string `yaml:"add" json:"add" toml:"add"`
Remove []string `yaml:"remove" json:"remove" toml:"remove"`
}{
NoDefault: true,
Add: []string{
Expand Down Expand Up @@ -81,10 +81,10 @@ func TestExternalConfig_ShouldSkipRule(t *testing.T) {
},
},
Rules: struct {
NoDefault bool `yaml:"no_default"`
AllDefault bool `yaml:"all_default"`
Add []string `yaml:"add"`
Remove []string `yaml:"remove"`
NoDefault bool `yaml:"no_default" json:"no_default" toml:"no_default"`
AllDefault bool `yaml:"all_default" json:"all_default" toml:"all_default"`
Add []string `yaml:"add" json:"add" toml:"add"`
Remove []string `yaml:"remove" json:"remove" toml:"remove"`
}{
NoDefault: false,
Add: []string{
Expand Down
4 changes: 2 additions & 2 deletions internal/linter/config/fieldNamesExcludePrepositionsOption.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ package config
// FieldNamesExcludePrepositionsOption represents the option for the FIELD_NAMES_EXCLUDE_PREPOSITIONS rule.
type FieldNamesExcludePrepositionsOption struct {
CustomizableSeverityOption
Prepositions []string `yaml:"prepositions"`
Excludes []string `yaml:"excludes"`
Prepositions []string `yaml:"prepositions" json:"prepositions" toml:"prepositions"`
Excludes []string `yaml:"excludes" json:"excludes" toml:"excludes"`
}
2 changes: 1 addition & 1 deletion internal/linter/config/fieldsHaveCommentOption.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package config
// FieldsHaveCommentOption represents the option for the FIELDS_HAVE_COMMENT rule.
type FieldsHaveCommentOption struct {
CustomizableSeverityOption
ShouldFollowGolangStyle bool `yaml:"should_follow_golang_style"`
ShouldFollowGolangStyle bool `yaml:"should_follow_golang_style" json:"should_follow_golang_style" toml:"should_follow_golang_style"`
}
2 changes: 1 addition & 1 deletion internal/linter/config/fileNamesLowerSnakeCaseOption.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package config
// FileNamesLowerSnakeCaseOption represents the option for the FILE_NAMES_LOWER_SNAKE_CASE rule.
type FileNamesLowerSnakeCaseOption struct {
CustomizableSeverityOption
Excludes []string `yaml:"excludes"`
Excludes []string `yaml:"excludes" json:"excludes" toml:"excludes"`
}
2 changes: 1 addition & 1 deletion internal/linter/config/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "github.com/yoheimuta/protolint/internal/stringsutil"

// Files represents the target files.
type Files struct {
Exclude []string `yaml:"exclude"`
Exclude []string `yaml:"exclude" json:"exclude" toml:"exclude"`
}

func (d Files) shouldSkipRule(
Expand Down
4 changes: 2 additions & 2 deletions internal/linter/config/ignore.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import "github.com/yoheimuta/protolint/internal/stringsutil"

// Ignore represents files ignoring the specific rule.
type Ignore struct {
ID string `yaml:"id"`
Files []string `yaml:"files"`
ID string `yaml:"id" json:"id" toml:"id"`
Files []string `yaml:"files" json:"files" toml:"files"`
}

func (i Ignore) shouldSkipRule(
Expand Down
18 changes: 18 additions & 0 deletions internal/linter/config/importsSortedOption.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,21 @@ func (i *ImportsSortedOption) UnmarshalYAML(unmarshal func(interface{}) error) e
}
return nil
}

// UnmarshalTOML implements toml Unmarshaler interface.
func (i *ImportsSortedOption) UnmarshalTOML(data interface{}) error {
optionsMap := map[string]interface{}{}
for k, v := range data.(map[string]interface{}) {
optionsMap[k] = v.(string)
}

if newline, ok := optionsMap["newline"]; ok {
switch newline.(string) {
case "\n", "\r", "\r\n", "":
i.Newline = newline.(string)
default:
return fmt.Errorf(`%s is an invalid newline option. valid option is \n, \r or \r\n`, newline)
}
}
return nil
}
Loading

0 comments on commit f22f0a7

Please sign in to comment.