@@ -12,11 +12,6 @@ import (
1212 "unicode"
1313)
1414
15- const (
16- plainColumnSep = ":"
17- smartColumnSep = "@"
18- )
19-
2015var (
2116 noExistsClauseFlag = flag .Bool ("no-exists-clause" , false , "Omit IF NOT EXISTS in CREATE TABLE statements" )
2217 idColumnFlag = flag .String ("id-column" , "id" , "Name of the column that identifies a row" )
@@ -25,41 +20,84 @@ var (
2520 onlyFlag = flag .String ("only" , "" , "Limit output to 'schema' or 'queries'" )
2621)
2722
23+ const (
24+ plainColumnSep = ":"
25+ smartColumnSep = "@"
26+ )
27+
28+ const (
29+ exitCodeBadArgument = 1
30+ exitCodeInternalError = 2
31+ )
32+
2833var (
2934 errBadArgument = errors .New ("bad argument" )
3035 errInvalidSmartColumn = fmt .Errorf ("%w: invalid <smart-column>" , errBadArgument )
3136)
3237
33- func main () {
34- flag .CommandLine .SetOutput (os .Stdout )
35- flag .Usage = printUsage
36- flag .Parse ()
38+ // usage contains the inline documentation for sqlcup.
39+ //go:embed usage.txt
40+ var usage string
3741
38- if err := run (); err != nil {
39- _ , _ = fmt .Fprintf (os .Stderr , "%s: %s\n " , os .Args [0 ], err )
40- if errors .Is (err , errBadArgument ) {
41- flag .Usage ()
42+ func main () {
43+ // Suppress error logs from flag package while parsing flags.
44+ flag .CommandLine .SetOutput (io .Discard )
45+ // With flag.ContinueOnError we prevent Parse from calling os.Exit on error and instead show our own error message.
46+ flag .CommandLine .Init (os .Args [0 ], flag .ContinueOnError )
47+ if err := flag .CommandLine .Parse (os .Args [1 :]); err != nil {
48+ if err == flag .ErrHelp {
49+ printHelp ()
50+ os .Exit (0 )
4251 }
43- os .Exit (1 )
52+ fatalUsageError (err )
53+ }
54+
55+ sca , err := parseScaffoldCommandArgs (flag .CommandLine .Args ())
56+ if err != nil {
57+ exitWithError (err )
58+ }
59+
60+ err = scaffoldCommand (sca )
61+ if err != nil {
62+ exitWithError (err )
4463 }
4564}
4665
47- //go:embed usage.txt
48- var usage string
66+ // fatalUsageError writes the inline help to os.Stdout and the err to os.Stderr, then calls os.Exit(1).
67+ //goland:noinspection GoUnhandledErrorResult
68+ func fatalUsageError (err error ) {
69+ printHelp ()
70+
71+ // Write error message to stderr.
72+ fmt .Fprintf (os .Stderr , "%s: %s\n " , os .Args [0 ], err )
73+
74+ // Exit process with non-zero status code to indicate failure to the calling process.
75+ os .Exit (exitCodeBadArgument )
76+ }
4977
5078//goland:noinspection GoUnhandledErrorResult
51- func printUsage () {
52- w := flag .CommandLine .Output ()
53- fmt .Fprintln (w , usage )
54- flag .PrintDefaults ()
79+ func printHelp () {
80+ // Write usage documentation for sqlcup to stdout.
81+ fmt .Fprintln (os .Stdout , usage )
82+
83+ // Write flag documentation and defaults to stdout.
84+ flag .CommandLine .SetOutput (os .Stdout )
85+ flag .CommandLine .PrintDefaults ()
86+ flag .CommandLine .SetOutput (io .Discard )
87+ fmt .Fprintln (os .Stdout )
5588}
5689
57- func run () error {
58- sca , err := parseScaffoldCommandArgs (flag .Args ())
59- if err != nil {
60- return err
90+ // exitWithError prints err to os.Stderr and calls os.Exit.
91+ // If err is (or wraps) errBadArgument, inline documentation is written to os.Stdout.
92+ //goland:noinspection GoUnhandledErrorResult
93+ func exitWithError (err error ) {
94+ if errors .Is (err , errBadArgument ) {
95+ fatalUsageError (err )
96+ } else {
97+ // This is not a user error, so we don't write inline help.
98+ fmt .Fprintf (os .Stderr , "%s: %s\n " , os .Args [0 ], err )
99+ os .Exit (exitCodeInternalError )
61100 }
62- return scaffoldCommand (sca )
63101}
64102
65103type column struct {
@@ -152,7 +190,7 @@ func parseSmartColumnDefinition(s string) (column, error) {
152190 case "blob" :
153191 colType = "BLOB"
154192 default :
155- return column {}, fmt .Errorf ("%w: '%s': unknown <tag> #%s" , errInvalidSmartColumn , s , tag )
193+ return column {}, fmt .Errorf ("%w: '%s', unknown <tag> #%s" , errInvalidSmartColumn , s , tag )
156194 }
157195 }
158196 if id {
@@ -176,7 +214,7 @@ func parseSmartColumnDefinition(s string) (column, error) {
176214 }
177215
178216 if colType == "" {
179- return column {}, fmt .Errorf ("%w: '%s' missing column type" , errInvalidSmartColumn , s )
217+ return column {}, fmt .Errorf ("%w: '%s', missing column type" , errInvalidSmartColumn , s )
180218 }
181219 constraint := ""
182220 if ! null {
@@ -195,8 +233,8 @@ func parseSmartColumnDefinition(s string) (column, error) {
195233
196234func parsePlainColumnDefinition (s string ) (column , error ) {
197235 parts := strings .Split (s , ":" )
198- if len (parts ) < 2 || len (parts ) > 3 {
199- return column {}, fmt .Errorf ("%w: invalid <plain-column>: '%s', expected '<name>:<type>' or '<name>:<type>:< constraint>'" , errBadArgument , s )
236+ if len (parts ) < 2 || len (parts ) > 3 || parts [ 0 ] == "" {
237+ return column {}, fmt .Errorf ("%w: invalid <plain-column>: '%s', expected '<name>:<type>[:< constraint>] '" , errBadArgument , s )
200238 }
201239 col := column {
202240 ID : strings .ToLower (parts [0 ]) == * idColumnFlag ,
@@ -331,14 +369,14 @@ func writeSchema(w io.Writer, args *scaffoldCommandArgs) {
331369 fmt .Fprintf (w , ");" )
332370}
333371
334- //goland:noinspection GoUnhandledErrorResult
372+ //goland:noinspection GoUnhandledErrorResult,SqlNoDataSourceInspection
335373func writeGetQuery (w io.Writer , args * scaffoldCommandArgs ) {
336374 fmt .Fprintf (w , "-- name: Get%s :one\n " , args .SingularEntity )
337375 fmt .Fprintf (w , "SELECT * FROM %s\n " , args .Table )
338376 fmt .Fprintf (w , "WHERE %s = ? LIMIT 1;" , args .IDColumn .Name )
339377}
340378
341- //goland:noinspection GoUnhandledErrorResult
379+ //goland:noinspection GoUnhandledErrorResult,SqlNoDataSourceInspection
342380func writeListQuery (w io.Writer , args * scaffoldCommandArgs ) {
343381 fmt .Fprintf (w , "-- name: List%s :many\n " , args .PluralEntity )
344382 fmt .Fprintf (w , "SELECT * FROM %s" , args .Table )
@@ -349,7 +387,7 @@ func writeListQuery(w io.Writer, args *scaffoldCommandArgs) {
349387 }
350388}
351389
352- //goland:noinspection GoUnhandledErrorResult
390+ //goland:noinspection GoUnhandledErrorResult,SqlNoDataSourceInspection
353391func writeCreateQuery (w io.Writer , args * scaffoldCommandArgs ) {
354392 fmt .Fprintf (w , "-- name: Create%s :one\n " , args .SingularEntity )
355393 fmt .Fprintf (w , "INSERT INTO %s (\n " , args .Table )
@@ -375,7 +413,7 @@ func writeCreateQuery(w io.Writer, args *scaffoldCommandArgs) {
375413 fmt .Fprintf (w , "RETURNING *;" )
376414}
377415
378- //goland:noinspection GoUnhandledErrorResult
416+ //goland:noinspection GoUnhandledErrorResult,SqlNoDataSourceInspection
379417func writeDeleteQuery (w io.Writer , args * scaffoldCommandArgs ) {
380418 fmt .Fprintf (w , "-- name: Delete%s :exec\n " , args .SingularEntity )
381419 fmt .Fprintf (w , "DELETE FROM %s\n " , args .Table )
0 commit comments