From cc7a272f6527b5f9fabe8c59eee85b20d8659a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20K=C3=B6ken?= Date: Sun, 26 Sep 2021 23:41:34 +0200 Subject: [PATCH 1/3] Add Marker support to process markers and generate code Initialized the marker processor Fixes #1 --- cmd/accessor/common.go | 49 ++++++++++++++++++++++ cmd/accessor/constants.go | 21 ++++++++++ cmd/accessor/generate.go | 88 +++++++++++++++++++++++++++++++++++++++ cmd/accessor/main.go | 23 ++++++++++ cmd/accessor/processor.go | 32 ++++++++++++++ cmd/accessor/root.go | 31 ++++++++++++++ cmd/accessor/validate.go | 78 ++++++++++++++++++++++++++++++++++ cmd/accessor/version.go | 33 +++++++++++++++ go.mod | 5 +++ 9 files changed, 360 insertions(+) create mode 100644 cmd/accessor/common.go create mode 100644 cmd/accessor/constants.go create mode 100644 cmd/accessor/generate.go create mode 100644 cmd/accessor/main.go create mode 100644 cmd/accessor/processor.go create mode 100644 cmd/accessor/root.go create mode 100644 cmd/accessor/validate.go create mode 100644 cmd/accessor/version.go diff --git a/cmd/accessor/common.go b/cmd/accessor/common.go new file mode 100644 index 0000000..3c697dd --- /dev/null +++ b/cmd/accessor/common.go @@ -0,0 +1,49 @@ +/* +Copyright © 2021 Accessor Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "github.com/procyon-projects/marker" + "log" +) + +// printErrors prints error(s) if any error exists after processing markers. +func printErrors(errorList marker.ErrorList) { + if errorList == nil || len(errorList) == 0 { + return + } + + for _, err := range errorList { + switch typedErr := err.(type) { + case marker.ParserError: + pos := typedErr.Position + log.Errorf("%s (%d:%d) : %s\n", typedErr.FileName, pos.Line, pos.Column, typedErr.Error()) + case marker.ErrorList: + printErrors(typedErr) + } + } +} + +// validateMarkers visits all files and returns errors +func validateMarkers(collector *marker.Collector, pkgs []*marker.Package) error { + marker.EachFile(collector, pkgs, func(file *marker.File, fileErrors error) { + if fileErrors != nil { + validationErrors = append(validationErrors, fileErrors) + } + }) + + return marker.NewErrorList(validationErrors) +} diff --git a/cmd/accessor/constants.go b/cmd/accessor/constants.go new file mode 100644 index 0000000..c2db263 --- /dev/null +++ b/cmd/accessor/constants.go @@ -0,0 +1,21 @@ +/* +Copyright © 2021 Accessor Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +const ( + AppName = "accessor" + AppVersion = "1.0.0" +) diff --git a/cmd/accessor/generate.go b/cmd/accessor/generate.go new file mode 100644 index 0000000..3714f6d --- /dev/null +++ b/cmd/accessor/generate.go @@ -0,0 +1,88 @@ +/* +Copyright © 2021 Accessor Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "github.com/procyon-projects/marker" + "github.com/spf13/cobra" + "log" +) + +var paths []string +var outputPath string +var options []string + +var generateCmd = &cobra.Command{ + Use: "generate", + Short: "Generate Go files by processing markers", + Long: `The generate command helps your code generation process by processing markers`, + Run: func(cmd *cobra.Command, args []string) { + packages, err := marker.LoadPackages(paths...) + + if err != nil { + log.Errorln(err) + return + } + + registry := marker.NewRegistry() + err = RegisterDefinitions(registry) + + if err != nil { + log.Errorln(err) + return + } + + collector := marker.NewCollector(registry) + err = validateMarkers(collector, packages) + + if err != nil { + + switch typedErr := err.(type) { + case marker.ErrorList: + printErrors(typedErr) + return + } + + log.Errorln(err) + return + } + + err = ProcessMarkers(collector, packages) + if err != nil { + log.Errorln(err) + } + }, +} + +func init() { + rootCmd.AddCommand(generateCmd) + + generateCmd.Flags().StringSliceVarP(&paths, "path", "p", paths, "path(s) separated by comma") + err := generateCmd.MarkFlagRequired("path") + + if err != nil { + panic(err) + } + + generateCmd.Flags().StringVarP(&outputPath, "output", "o", "", "output path") + err = generateCmd.MarkFlagRequired("output") + + if err != nil { + panic(err) + } + + generateCmd.Flags().StringSliceVarP(&options, "args", "a", options, "extra arguments for marker processors (key-value separated by comma)") +} diff --git a/cmd/accessor/main.go b/cmd/accessor/main.go new file mode 100644 index 0000000..51bf5aa --- /dev/null +++ b/cmd/accessor/main.go @@ -0,0 +1,23 @@ +/* +Copyright © 2021 Accessor Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import "log" + +func main() { + log.SetFlags(0) + Execute() +} diff --git a/cmd/accessor/processor.go b/cmd/accessor/processor.go new file mode 100644 index 0000000..4451f26 --- /dev/null +++ b/cmd/accessor/processor.go @@ -0,0 +1,32 @@ +/* +Copyright © 2021 Accessor Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "github.com/procyon-projects/marker" +) + +// Register your marker definitions. +func RegisterDefinitions(registry *marker.Registry) error { + /* your code goes here */ + return nil +} + +// Process your markers. +func ProcessMarkers(collector *marker.Collector, pkgs []*marker.Package) error { + /* your code goes here */ + return nil +} diff --git a/cmd/accessor/root.go b/cmd/accessor/root.go new file mode 100644 index 0000000..40bcbd2 --- /dev/null +++ b/cmd/accessor/root.go @@ -0,0 +1,31 @@ +/* +Copyright © 2021 Accessor Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "fmt" + "github.com/spf13/cobra" +) + +var rootCmd = &cobra.Command{ + Use: fmt.Sprintf(AppName), + Short: "Marker Processor", + Long: `Marker Processor`, +} + +func Execute() { + cobra.CheckErr(rootCmd.Execute()) +} diff --git a/cmd/accessor/validate.go b/cmd/accessor/validate.go new file mode 100644 index 0000000..1ae615d --- /dev/null +++ b/cmd/accessor/validate.go @@ -0,0 +1,78 @@ +/* +Copyright © 2021 Accessor Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "github.com/procyon-projects/marker" + "github.com/spf13/cobra" + "log" +) + +var ( + validatePaths []string + validateArgs []string + validationErrors []error +) + +var validateCmd = &cobra.Command{ + Use: "validate", + Short: "Validate markers' syntax and arguments", + Long: `The validate command helps you validate markers' syntax and arguments'`, + Run: func(cmd *cobra.Command, args []string) { + packages, err := marker.LoadPackages(validatePaths...) + + if err != nil { + log.Errorln(err) + return + } + + registry := marker.NewRegistry() + err = RegisterDefinitions(registry) + + if err != nil { + log.Errorln(err) + return + } + + collector := marker.NewCollector(registry) + err = validateMarkers(collector, packages) + + if err != nil { + + switch typedErr := err.(type) { + case marker.ErrorList: + printErrors(typedErr) + return + } + + log.Errorln(err) + return + } + }, +} + +func init() { + rootCmd.AddCommand(validateCmd) + + validateCmd.Flags().StringSliceVarP(&validatePaths, "path", "p", validatePaths, "path(s) separated by comma") + err := validateCmd.MarkFlagRequired("path") + + if err != nil { + panic(err) + } + + validateCmd.Flags().StringSliceVarP(&validateArgs, "args", "a", validateArgs, "extra arguments for marker processors (key-value separated by comma)") +} diff --git a/cmd/accessor/version.go b/cmd/accessor/version.go new file mode 100644 index 0000000..9197a26 --- /dev/null +++ b/cmd/accessor/version.go @@ -0,0 +1,33 @@ +/* +Copyright © 2021 Accessor Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package main + +import ( + "fmt" + "github.com/spf13/cobra" +) + +var versionCmd = &cobra.Command{ + Use: "version", + Short: fmt.Sprintf("Print %s version", AppName), + Run: func(cmd *cobra.Command, args []string) { + fmt.Printf("%s version %s\n", AppName, AppVersion) + }, +} + +func init() { + rootCmd.AddCommand(versionCmd) +} diff --git a/go.mod b/go.mod index 86ffc6a..b229551 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,8 @@ module github.com/procyon-projects/accessor go 1.13 + +require ( + github.com/procyon-projects/marker v0.0.9-dev + github.com/spf13/cobra v1.2.1 +) From b9d7aab0c6ef005f186b2f8c81051f09197aa735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20K=C3=B6ken?= Date: Mon, 27 Sep 2021 01:05:08 +0200 Subject: [PATCH 2/3] Add accessor and accessor:mapping markers --- cmd/accessor/constants.go | 1 + cmd/accessor/markers.go | 59 +++++++++++++++++++++++++++++++++++++++ cmd/accessor/processor.go | 23 +++++++++++++-- test/package1/accessor.go | 16 +++++++++++ 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 cmd/accessor/markers.go create mode 100644 test/package1/accessor.go diff --git a/cmd/accessor/constants.go b/cmd/accessor/constants.go index c2db263..dc7c061 100644 --- a/cmd/accessor/constants.go +++ b/cmd/accessor/constants.go @@ -18,4 +18,5 @@ package main const ( AppName = "accessor" AppVersion = "1.0.0" + PkgId = "github.com/procyon-projects/accessor" ) diff --git a/cmd/accessor/markers.go b/cmd/accessor/markers.go new file mode 100644 index 0000000..6e0198e --- /dev/null +++ b/cmd/accessor/markers.go @@ -0,0 +1,59 @@ +package main + +import ( + "errors" + "fmt" + "strings" +) + +const ( + MarkerAccessor = "accessor" + MarkerAccessorMapping = "accessor:mapping" +) + +type AccessorMarker struct { + Value string `marker:"Value,useValueSyntax"` + Url string `marker:"Url"` +} + +func (a AccessorMarker) Validate() error { + if strings.TrimSpace(a.Value) == "" { + return errors.New("'Value' cannot be empty or nil") + } + + if strings.TrimSpace(a.Url) == "" { + return errors.New("'Url' cannot be empty or nil") + } + + return nil +} + +type AccessorMappingMarker struct { + Value string `marker:"Value,useValueSyntax"` + Method string `marker:"Method"` +} + +func (a AccessorMappingMarker) Validate() error { + if strings.TrimSpace(a.Value) == "" { + return errors.New("'Value' cannot be empty or nil") + } + + if strings.TrimSpace(a.Method) == "" { + return errors.New("'Url' cannot be empty or nil") + } + + matched := false + methods := []string{"GET", "POST", "HEAD", "OPTIONS", "PUT", "PATCH", "DELETE", "TRACE"} + + for _, method := range methods { + if strings.TrimSpace(a.Method) == method { + matched = true + } + } + + if !matched { + return fmt.Errorf("invalid 'Method' argument value. Here is the list of valid values %s", strings.Join(methods, ", ")) + } + + return nil +} diff --git a/cmd/accessor/processor.go b/cmd/accessor/processor.go index 4451f26..1257354 100644 --- a/cmd/accessor/processor.go +++ b/cmd/accessor/processor.go @@ -21,12 +21,31 @@ import ( // Register your marker definitions. func RegisterDefinitions(registry *marker.Registry) error { - /* your code goes here */ + markers := []struct { + Name string + Level marker.TargetLevel + Output interface{} + }{ + {Name: MarkerAccessor, Level: marker.InterfaceTypeLevel, Output: &AccessorMarker{}}, + {Name: MarkerAccessorMapping, Level: marker.InterfaceMethodLevel, Output: &AccessorMappingMarker{}}, + } + + for _, m := range markers { + err := registry.Register(m.Name, PkgId, m.Level, m.Output) + if err != nil { + return err + } + } + return nil } // Process your markers. func ProcessMarkers(collector *marker.Collector, pkgs []*marker.Package) error { - /* your code goes here */ + marker.EachFile(collector, pkgs, func(file *marker.File, err error) { + if err != nil { + + } + }) return nil } diff --git a/test/package1/accessor.go b/test/package1/accessor.go new file mode 100644 index 0000000..8892081 --- /dev/null +++ b/test/package1/accessor.go @@ -0,0 +1,16 @@ +package package1 + +// +import=accessor, Pkg="github.com/procyon-projects/accessor" +import "context" + +// +accessor=MovieAccessor, Url="https://localhost:8090/api/v1" +type MovieAccessor interface { + // +accessor:mapping="/movie/{id}", Method=GET + GetMovie(ctx context.Context, id string) + // +accessor:mapping="/movie", Method=POST + SaveMovie(ctx context.Context) + // +accessor:mapping="/movie/{id}", Method=PATCH + UpdateMovie(ctx context.Context) + // +accessor:mapping="/movie/{id}/reviews", Method=GET + GetMovieReviews(ctx context.Context, id string) +} From e2ada73cd829e0fdd7c5b9d1e499dfff2396cc30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20K=C3=B6ken?= Date: Mon, 27 Sep 2021 01:32:24 +0200 Subject: [PATCH 3/3] Add findAccessorInterfaces method that finds interfaces marked as accessor. --- cmd/accessor/processor.go | 32 ++++++++++++++++++++++--- test/package1/{accessor.go => movie.go} | 0 2 files changed, 29 insertions(+), 3 deletions(-) rename test/package1/{accessor.go => movie.go} (100%) diff --git a/cmd/accessor/processor.go b/cmd/accessor/processor.go index 1257354..7042444 100644 --- a/cmd/accessor/processor.go +++ b/cmd/accessor/processor.go @@ -19,6 +19,10 @@ import ( "github.com/procyon-projects/marker" ) +var ( + accessorInterfaces []marker.InterfaceType +) + // Register your marker definitions. func RegisterDefinitions(registry *marker.Registry) error { markers := []struct { @@ -43,9 +47,31 @@ func RegisterDefinitions(registry *marker.Registry) error { // Process your markers. func ProcessMarkers(collector *marker.Collector, pkgs []*marker.Package) error { marker.EachFile(collector, pkgs, func(file *marker.File, err error) { - if err != nil { - - } + findAccessorInterfaces(file.InterfaceTypes) }) return nil } + +func findAccessorInterfaces(interfaceTypes []marker.InterfaceType) { + for _, interfaceType := range interfaceTypes { + markerValues := interfaceType.Markers + + if markerValues == nil { + return + } + + markers, ok := markerValues[MarkerAccessor] + + if !ok { + return + } + + for _, candidateMarker := range markers { + switch candidateMarker.(type) { + case AccessorMarker: + accessorInterfaces = append(accessorInterfaces, interfaceType) + } + } + + } +} diff --git a/test/package1/accessor.go b/test/package1/movie.go similarity index 100% rename from test/package1/accessor.go rename to test/package1/movie.go