@@ -15,14 +15,16 @@ import (
1515
1616// App represents the full configuration for a fetch invocation.
1717type App struct {
18- URL * url.URL
18+ URL * url.URL
19+ ExtraArgs []string
1920
2021 Cfg config.Config
2122
2223 AWSSigv4 * aws.Config
2324 Basic * core.KeyVal
2425 Bearer string
2526 BuildInfo bool
27+ Complete string
2628 ConfigPath string
2729 Data io.Reader
2830 DryRun bool
@@ -44,12 +46,24 @@ func (a *App) PrintHelp(p *core.Printer) {
4446}
4547
4648func (a * App ) CLI () * CLI {
49+ var extraArgs bool
4750 return & CLI {
4851 Description : "fetch is a modern HTTP(S) client for the command line" ,
4952 Args : []Arguments {
5053 {Name : "URL" , Description : "The URL to make a request to" },
5154 },
5255 ArgFn : func (s string ) error {
56+ // Append extra args, if necessary.
57+ if extraArgs {
58+ a .ExtraArgs = append (a .ExtraArgs , s )
59+ return nil
60+ }
61+ if s == "--" {
62+ extraArgs = true
63+ return nil
64+ }
65+
66+ // Otherwise, parse the provided URL.
5367 if a .URL != nil {
5468 return fmt .Errorf ("unexpected argument: %q" , s )
5569 }
@@ -186,14 +200,46 @@ func (a *App) CLI() *CLI {
186200 Description : "Enable/disable color" ,
187201 Default : "" ,
188202 Aliases : []string {"colour" },
189- Values : []string {"auto" , "off" , "on" },
203+ Values : []core.KeyVal {
204+ {
205+ Key : "auto" ,
206+ Val : "Automatically determine color" ,
207+ },
208+ {
209+ Key : "off" ,
210+ Val : "Disable color output" ,
211+ },
212+ {
213+ Key : "on" ,
214+ Val : "Enable color output" ,
215+ },
216+ },
190217 IsSet : func () bool {
191218 return a .Cfg .Color != core .ColorUnknown
192219 },
193220 Fn : func (value string ) error {
194221 return a .Cfg .ParseColor (value )
195222 },
196223 },
224+ {
225+ Short : "" ,
226+ Long : "complete" ,
227+ Args : "SHELL" ,
228+ Description : "Output shell completion" ,
229+ Default : "" ,
230+ Values : []core.KeyVal {
231+ {Key : "fish" },
232+ {Key : "zsh" },
233+ },
234+ HideValues : true ,
235+ IsSet : func () bool {
236+ return a .Complete != ""
237+ },
238+ Fn : func (value string ) error {
239+ a .Complete = value
240+ return nil
241+ },
242+ },
197243 {
198244 Short : "c" ,
199245 Long : "config" ,
@@ -288,7 +334,20 @@ func (a *App) CLI() *CLI {
288334 Args : "OPTION" ,
289335 Description : "Enable/disable formatting" ,
290336 Default : "" ,
291- Values : []string {"auto" , "off" , "on" },
337+ Values : []core.KeyVal {
338+ {
339+ Key : "auto" ,
340+ Val : "Automatically determine whether to format" ,
341+ },
342+ {
343+ Key : "off" ,
344+ Val : "Disable output formatting" ,
345+ },
346+ {
347+ Key : "on" ,
348+ Val : "Enable output formatting" ,
349+ },
350+ },
292351 IsSet : func () bool {
293352 return a .Cfg .Format != core .FormatUnknown
294353 },
@@ -329,7 +388,16 @@ func (a *App) CLI() *CLI {
329388 Args : "VERSION" ,
330389 Description : "Highest allowed HTTP version" ,
331390 Default : "" ,
332- Values : []string {"1" , "2" },
391+ Values : []core.KeyVal {
392+ {
393+ Key : "1" ,
394+ Val : "HTTP/1.1" ,
395+ },
396+ {
397+ Key : "2" ,
398+ Val : "HTTP/2.0" ,
399+ },
400+ },
333401 IsSet : func () bool {
334402 return a .Cfg .HTTP != core .HTTPDefault
335403 },
@@ -358,6 +426,21 @@ func (a *App) CLI() *CLI {
358426 Args : "OPTION" ,
359427 Description : "Enable/disable image rendering" ,
360428 Default : "" ,
429+ Values : []core.KeyVal {
430+ {
431+ Key : "auto" ,
432+ Val : "Automatically decide image display" ,
433+ },
434+ {
435+ Key : "native" ,
436+ Val : "Only use builtin decoders" ,
437+ },
438+ {
439+ Key : "off" ,
440+ Val : "Disable image display" ,
441+ },
442+ },
443+ HideValues : true ,
361444 IsSet : func () bool {
362445 return a .Cfg .Image != core .ImageUnknown
363446 },
@@ -425,15 +508,28 @@ func (a *App) CLI() *CLI {
425508 Fn : func (value string ) error {
426509 key , val , _ := cut (value , "=" )
427510 if strings .HasPrefix (val , "@" ) {
428- stats , err := os .Stat (val [1 :])
511+ path := val [1 :]
512+
513+ // Expand '~' to the home directory.
514+ if len (path ) >= 2 && path [0 ] == '~' && path [1 ] == os .PathSeparator {
515+ home , err := os .UserHomeDir ()
516+ if err != nil {
517+ return err
518+ }
519+ path = home + path [1 :]
520+ val = "@" + path
521+ }
522+
523+ // Ensure the file exists.
524+ stats , err := os .Stat (path )
429525 if err != nil {
430526 if os .IsNotExist (err ) {
431- return fmt .Errorf ("file does not exist: '%s'" , val [ 1 :] )
527+ return fmt .Errorf ("file does not exist: '%s'" , path )
432528 }
433529 return err
434530 }
435531 if stats .IsDir () {
436- return fmt .Errorf ("file is a directory: '%s'" , val [ 1 :] )
532+ return fmt .Errorf ("file is a directory: '%s'" , path )
437533 }
438534 }
439535 a .Multipart = append (a .Multipart , core.KeyVal {Key : key , Val : val })
@@ -588,7 +684,24 @@ func (a *App) CLI() *CLI {
588684 Args : "VERSION" ,
589685 Description : "Minimum TLS version" ,
590686 Default : "" ,
591- Values : []string {"1.0" , "1.1" , "1.2" , "1.3" },
687+ Values : []core.KeyVal {
688+ {
689+ Key : "1.0" ,
690+ Val : "TLS v1.0" ,
691+ },
692+ {
693+ Key : "1.1" ,
694+ Val : "TLS v1.1" ,
695+ },
696+ {
697+ Key : "1.2" ,
698+ Val : "TLS v1.2" ,
699+ },
700+ {
701+ Key : "1.3" ,
702+ Val : "TLS v1.3" ,
703+ },
704+ },
592705 IsSet : func () bool {
593706 return a .Cfg .TLS != nil
594707 },
@@ -671,7 +784,16 @@ func requestBody(value string) (io.Reader, error) {
671784 case value == "@-" :
672785 return os .Stdin , nil
673786 default :
674- f , err := os .Open (value [1 :])
787+ path := value [1 :]
788+ // Expand '~' to the home directory.
789+ if len (path ) >= 2 && path [0 ] == '~' && path [1 ] == os .PathSeparator {
790+ home , err := os .UserHomeDir ()
791+ if err != nil {
792+ return nil , err
793+ }
794+ path = home + path [1 :]
795+ }
796+ f , err := os .Open (path )
675797 if err != nil {
676798 if os .IsNotExist (err ) {
677799 return nil , fileNotExistsError (value [1 :])
0 commit comments