github.com/yoheimuta/protolint@v0.49.8-0.20240515023657-4ecaebb7575d/internal/cmd/protocgenprotolint/cmd.go (about) 1 package protocgenprotolint 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "google.golang.org/protobuf/types/pluginpb" 13 14 "github.com/yoheimuta/protolint/internal/cmd/subcmds" 15 16 "github.com/yoheimuta/protolint/internal/cmd/subcmds/lint" 17 18 "github.com/golang/protobuf/proto" 19 protogen "github.com/golang/protobuf/protoc-gen-go/plugin" 20 21 "github.com/yoheimuta/protolint/internal/osutil" 22 ) 23 24 var ( 25 version = "master" 26 revision = "latest" 27 ) 28 29 const ( 30 subCmdVersion = "version" 31 ) 32 33 // Do runs the command logic. 34 func Do( 35 args []string, 36 stdin io.Reader, 37 stdout io.Writer, 38 stderr io.Writer, 39 ) osutil.ExitCode { 40 if 0 < len(args) && args[0] == subCmdVersion { 41 return doVersion(stdout) 42 } 43 44 err := signalSupportProto3Optional() 45 if err != nil { 46 _, _ = fmt.Fprintln(stderr, err) 47 return osutil.ExitInternalFailure 48 } 49 50 subCmd, err := newSubCmd(stdin, stdout, stderr) 51 if err != nil { 52 _, _ = fmt.Fprintln(stderr, err) 53 return osutil.ExitInternalFailure 54 } 55 return subCmd.Run() 56 } 57 58 func signalSupportProto3Optional() error { 59 // supports proto3 field presence 60 // See https://github.com/protocolbuffers/protobuf/blob/cdc11c2d2d314ce0382fe0eaa715e5e0e1270438/docs/implementing_proto3_presence.md#signaling-that-your-code-generator-supports-proto3-optional 61 var supportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL) 62 data, err := proto.Marshal(&protogen.CodeGeneratorResponse{ 63 SupportedFeatures: &supportedFeatures, 64 }) 65 if err != nil { 66 return err 67 } 68 69 _, err = io.Copy(os.Stdout, bytes.NewReader(data)) 70 if err != nil { 71 return err 72 } 73 return nil 74 } 75 76 func newSubCmd( 77 stdin io.Reader, 78 stdout io.Writer, 79 stderr io.Writer, 80 ) (*lint.CmdLint, error) { 81 data, err := ioutil.ReadAll(stdin) 82 if err != nil { 83 return nil, err 84 } 85 86 var req protogen.CodeGeneratorRequest 87 err = proto.Unmarshal(data, &req) 88 if err != nil { 89 return nil, err 90 } 91 92 flags, err := newFlags(&req) 93 if err != nil { 94 return nil, err 95 } 96 97 subCmd, err := lint.NewCmdLint( 98 *flags, 99 stdout, 100 stderr, 101 ) 102 if err != nil { 103 return nil, err 104 } 105 return subCmd, nil 106 } 107 108 func newFlags( 109 req *protogen.CodeGeneratorRequest, 110 ) (*lint.Flags, error) { 111 flags, err := lint.NewFlags(req.FileToGenerate) 112 if err != nil { 113 return nil, err 114 } 115 116 var pf subcmds.PluginFlag 117 for _, p := range strings.Split(req.GetParameter(), ",") { 118 params := strings.SplitN(strings.TrimSpace(p), "=", 2) 119 switch params[0] { 120 case "": 121 continue 122 case "config_path": 123 if len(params) != 2 { 124 return nil, fmt.Errorf("config_path should be specified") 125 } 126 flags.ConfigDirPath = params[1] 127 case "config_dir_path": 128 if len(params) != 2 { 129 return nil, fmt.Errorf("config_dir_path should be specified") 130 } 131 flags.ConfigDirPath = params[1] 132 case "fix": 133 flags.FixMode = true 134 case "reporter": 135 if len(params) != 2 { 136 return nil, fmt.Errorf("reporter should be specified") 137 } 138 value := params[1] 139 r, err := lint.GetReporter(value) 140 if err != nil { 141 return nil, err 142 } 143 flags.Reporter = r 144 case "output_file": 145 if len(params) != 2 { 146 return nil, fmt.Errorf("output_file should be specified") 147 } 148 flags.OutputFilePath = params[1] 149 case "plugin": 150 if len(params) != 2 { 151 return nil, fmt.Errorf("plugin should be specified") 152 } 153 err = pf.Set(params[1]) 154 if err != nil { 155 return nil, err 156 } 157 case "v": 158 flags.Verbose = true 159 case "proto_root": 160 if len(params) != 2 { 161 return nil, fmt.Errorf("proto_root should be specified") 162 } 163 for i, f := range flags.FilePaths { 164 flags.FilePaths[i] = filepath.Join(params[1], f) 165 } 166 default: 167 return nil, fmt.Errorf("unmatched parameter: %s", p) 168 } 169 } 170 171 plugins, err := pf.BuildPlugins(flags.Verbose) 172 if err != nil { 173 return nil, err 174 } 175 flags.Plugins = plugins 176 177 return &flags, nil 178 } 179 180 func doVersion( 181 stdout io.Writer, 182 ) osutil.ExitCode { 183 _, _ = fmt.Fprintln(stdout, "protoc-gen-protolint version "+version+"("+revision+")") 184 return osutil.ExitSuccess 185 }