mkideal / cli

CLI - A package for building command line app with go
MIT License
730 stars 43 forks source link

File options #19

Closed suntong closed 7 years ago

suntong commented 8 years ago
  1. The clix.File allows to read content from file or stdin, does something equivalent exist for write to file or stdout as well?
  2. If there will be, for file options, better provide a function to expose the file-handle (io.Reader & io.Writer) for users to read/write files themselves.

Thanks

mkideal commented 8 years ago

Reader and Writer are added, see commit 60d614b and expamles 028, 029

suntong commented 8 years ago

Works like a charm, as always, and super fast fix as well. :-) :+1:

suntong commented 8 years ago

Oh, I need a function/field that exposes the file-handle (io.Reader & io.Writer) for me to read/write files myself. Thanks.

mkideal commented 8 years ago

It's a io.Reader/io.Writer in itself. clix.Reader implements io.Reader

// Read implementes io.Reader
func (r Reader) Read(data []byte) (n int, err error)

And, clix.Writer implments io.Writer

// Write implementes io.Writer interface
func (w *Writer) Write(data []byte) (n int, err error)
suntong commented 8 years ago

oh, let me look at it... Thanks!

suntong commented 8 years ago

Hmm... my function is expecting a w io.Writer parameter, can I pass a clix.Writer variable e.g., argv.Writer on https://github.com/mkideal/cli/blob/master/_examples/029-writer/main.go#L18, to the function? My Go knowledge on this part is blurred.

mkideal commented 8 years ago

Of course. See io.Writer:

// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
// Write must not modify the slice data, even temporarily.
//
// Implementations must not retain p.
type Writer interface {
    Write(p []byte) (n int, err error)
}
suntong commented 8 years ago

Oh, thanks for going the whole nine yards explaining it. My Go knowledge is really blurred on this part, your confirmation really helps.

mkideal commented 8 years ago

😄 ,I just copy the go standard source code from go1.6/src/io/io.go

suntong commented 8 years ago

Can you add a function Name() to give the file name please, for both Reader and Writer. Thx.

Oh, and better an indicator for the user to know whether they are rw to stdio or files as well please.

Hmm... on second thought, maybe when Name() return nil it means dealing with stdio?

suntong commented 8 years ago

hmm... sorry to bother you with some of my own code:

This is the code I'm porting from another go cli option package to cli:

    fileo := argv.Fileo
    if fileo == nil {
        var err error
        fileo, err = os.Create(
            strings.Replace(argv.Filei.Name(), ".extold", ".extnew", 1))
        check(err)
    }

Basically, the code takes a mandatory input file, argv.Filei, but the output file argv.Fileo is optional, if it is not provided, build it from the name of input file.

Both the argv.Filei and argv.Fileo were of type *os.File before, but now, I'm getting:

cannot convert nil to type ext.Writer
argv.Filei.Name undefined (type ext.Reader has no field or method Name)
cannot assign *os.File to fileo (type ext.Writer) in multiple assignment
cannot use fileo (type ext.Writer) as type io.Writer in argument to myFunc
        ext.Writer does not implement io.Writer (Write method has pointer receiver)

What would you think the best way to fix it? Sorry for not being able to give clearer requirement prevously -- My Go knowledge is really blurred on this part.

Thx!

mkideal commented 8 years ago

ext.Writer is just a io.Writer, not*os.File`.

fileo, err := os.Creat(strings.Replace(argv.Filei.Name(), ".extold", ".extnew", 1))
check(err)
argv.Fileo.SetWriter(fileo)
suntong commented 8 years ago

OMG!!! It's 2:30am your time right?

OK. that part is fixed. One more to go -- my function is expecting a w io.Writer parameter, but I cannot pass a clix.Writer variable:

cannot use fileo (type ext.Writer) as type io.Writer in argument to myFunc
        ext.Writer does not implement io.Writer (Write method has pointer receiver)

BTW, I've just prepared a small demo file. https://gist.github.com/suntong/cb2bf3d3ada7d7941aac8ef338a815a1#file-dump-go

But don't look at it now. Have some sleep instead. No hurry.

mkideal commented 8 years ago

OMG!!! It's 2:30am your time right? - Yes.

Ah, cannot use fileo (type ext.Writer) as type io.Writer in argument to myFunc ext.Writer does not implement io.Writer (Write method has pointer receiver),It's a problem!

suntong commented 8 years ago

Have some rest now. Deal with it tomorrow.

Remember you can start with https://gist.github.com/suntong/cb2bf3d3ada7d7941aac8ef338a815a1#file-dump-go.

Good night.

mkideal commented 8 years ago
Filei clix.Reader `cli:"*i,input" usage:"The file to dump from (mandatory)"`
Fileo clix.Writer `cli:"o,output" usage:"The dump output (default: .dump file of input)"`

should be replaced with

Filei *clix.Reader `cli:"*i,input" usage:"The file to dump from (mandatory)"`
Fileo *clix.Writer `cli:"o,output" usage:"The dump output (default: .dump file of input)"`

BTW, Global of root command must be true, otherwise ctx.RootArgv() is nil in subcommand!

suntong commented 8 years ago

My God. Alright it works. Now go to bed. :-)

suntong commented 8 years ago

Sleeping? Alright.

Let me whisper my question --

For Writer, if the parameter is optional, how to tell whether it is issued or not?

I've corrected my code according to all your above points. Now I have a code like this:

    argv := ctx.Argv().(*dumpT)

    if argv.Fileo == nil {
        fileo, err := os.Create(
            strings.Replace(argv.Filei.Name(), ".extold", ".extnew", 1))
        check(err)
        argv.Fileo.SetWriter(fileo)
    }
    fileo := argv.Fileo

Then the fileo is passed to my function which is expecting a w io.Writer parameter. All are good.

The problem is that when the --output option is not specified, the output was written to stdout instead of my created file. I.e., need to tell whether it is issued or not somehow.

mkideal commented 8 years ago

if argv.Fileo == nil should be if argv.Fileo.IsStdout(). I my CLI, Writer holds os.Stdout if not specific

suntong commented 8 years ago

Wow, wasn't expecting you reply so soon.

Please rethink your architect, because the above sounds like you cannot tell whether the --output option is not specified, or specified but without a following file parameter.

mkideal commented 8 years ago

ctx.IsSet("--output") determins whether the --output option is specified.

suntong commented 8 years ago

Perfect! Thx!

suntong commented 7 years ago

ctx.IsSet("--output") determins whether the --output option is specified.

mkideal, that ctx.IsSet is interfering with the file reading -- take a look at the latest code at https://gist.github.com/suntong/6658063cc0cf752f5d7ad5cc299f30bd

    if ctx.IsSet("--Filei") { // --Filei option is specified
        data, _ := ioutil.ReadAll(argv.Filei)
        argv.Filei.Close()
        //print(data)
        dd = string(data)
        print(dd)
    }

The print won't print anything, regardless whether get from pipe or from file. However, if removing the enclosing if ctx.IsSet("--Filei") call, the program works.

mkideal commented 7 years ago
      if ctx.IsSet("-i") { // --Filei option is specified
    data, _ := ioutil.ReadAll(argv.Filei)
    argv.Filei.Close()
    //print(data)
    dd = string(data)
    print(dd)
}

Replace --Filei with -i or --input

suntong commented 7 years ago

Duh. Sorry!