-4

I have the following code that uses a package to draw a progress bar

type tmpStruct struct {
}

func (t *tmpStruct) Write(p []byte) (n int, err error) {
    fmt.Fprintf(os.Stdout, "%s", string(p))
    return len(p), nil
}

func demoLoadingBarCount(maximumInt int) {
    buf := tmpStruct{}
    if nBuf, ok := interface{}(&buf).(io.Writer); ok {
        bar := progressbar.NewOptions(
            maximumInt,
            progressbar.OptionSetTheme(progressbar.Theme{Saucer: "█", SaucerPadding: "-", BarStart: ">", BarEnd: "<"}),
            progressbar.OptionSetWidth(100),
            progressbar.OptionSetWriter(nBuf),
        )
        for i := 0; i < maximumInt; i++ {
            bar.Add(1)
            time.Sleep(10 * time.Millisecond)
        }
    }
}

All works, except there is no new line at the end as you can see here enter image description here

I can't add a new line character in the Write function as that will cause it to new line after every byte pushed to the writer. Is there a neat way can I do this?

EDIT: I want the new line after the progress bar and before the next line prints out

amlwwalker
  • 3,161
  • 4
  • 26
  • 47
  • I don't understand. Where, and why, do you want a newline? – Jonathan Hall Jun 25 '19 at 09:42
  • What on earth is this?!?!? `if nBuf, ok := interface{}(&buf).(io.Writer); ok {` – Volker Jun 25 '19 at 09:53
  • 1
    @Volker: My guess: The an ill-conceived response to `tmpStruct does not implement io.Writer (Write method has pointer receiver)` – Jonathan Hall Jun 25 '19 at 09:56
  • Also, why are you using `tmpStruct` at all? Why not just use `os.Stdout`? – Jonathan Hall Jun 25 '19 at 09:57
  • @Flimzy as you can see in the image [client logger] is appending to the same line. Hence wish for a new line. I am using tmpStruct because the above is a PoC as I want to push the progress bar to a different writer eventually, not just os.Stdout. Volker - yes, its as a result of SO suggesting that so that I can type assert that buf is of Writer type, what is the correct approach? I got it from here https://stackoverflow.com/a/23172457/1414721 Why the downvotes, `if nBuf, ok := interface{}(&buf).(io.Writer); ok` maybe incorrect but the question follows standard question asking convention – amlwwalker Jun 25 '19 at 10:13
  • 2
    So you want a new line after the status bar? Why don't you print a newline, then, after the status bar? – Jonathan Hall Jun 25 '19 at 10:16
  • `nBuf := &tmpStruct{}` and drop the if statement. Then nBuf is of type *tmpStruct which implements io.Writer. – Peter Jun 25 '19 at 10:17
  • As for the type asserting: There's no need to type assert at all. Interfaces are automatic. Just declare your variable properly: `buf := &tmpStruct{}` – Jonathan Hall Jun 25 '19 at 10:17
  • Well I could, but I would have to do that outside of the progress bar, and not as part of the progress bar writer... that would be very repetitive – amlwwalker Jun 25 '19 at 10:17
  • 1
    Repetitive is what coders are best at. Write a function to handle it for you. I don't see any problem. – Jonathan Hall Jun 25 '19 at 10:18
  • Thanks @Peter, I'm not sure why the aggression Flimzy - `buf := &tmpStruct{}` is all you needed to say. Re the function, that is literally my question "is there a neat way to do this". I consider a seperate helper function the "not neat" way to do this. Surely the writer can no when its finished being written to and add a new line – amlwwalker Jun 25 '19 at 10:20
  • 2
    I downvoted because the question is very unclear. I don't see a clear problem statement. I see a teeny tiny graphic which I guess demonstrates an unspecified incorrect behavior (after comments I think I understand the intention). – Jonathan Hall Jun 25 '19 at 10:20
  • 2
    It's not really a minimal reproduction case, either. In fact, it's more code than necessary (given the unnecessary `tmpStruct` type, and the unnecessary type assertion), and simultaneously not enough code, since there's not a complete, executable example there to demonstrate the failure mode. – Jonathan Hall Jun 25 '19 at 10:21
  • Eeeuh, does `BarEnd: "<\n"` work? – RickyA Jun 25 '19 at 10:50
  • lol, I like your thinking @RickyA no it prints `BarEnd` every time it updates the progress bar, so that causes it to print over lots and lots of lines – amlwwalker Jun 25 '19 at 10:55
  • Ah, then I would just wrap this lib and the buffer in a custom struct and expose two functions ; `add` and `close` – RickyA Jun 25 '19 at 10:58
  • Yes that's the approach I'm now taking. Thanks. I prefer the explicit `close()` as it leaves room for other cleaning up if necessary. – amlwwalker Jun 25 '19 at 11:01

1 Answers1

4

The simple answer to the question you've asked is simply to print an additional newline after the progress bar is complete:

func demoLoadingBarCount(maximumInt int) {
    buf := &tmpStruct{}
    bar := progressbar.NewOptions(
        maximumInt,
        progressbar.OptionSetTheme(progressbar.Theme{Saucer: "█", SaucerPadding: "-", BarStart: ">", BarEnd: "<"}),
        progressbar.OptionSetWidth(100),
        progressbar.OptionSetWriter(buf),
    )
    for i := 0; i < maximumInt; i++ {
        bar.Add(1)
        time.Sleep(10 * time.Millisecond)
    }
    fmt.Fprintf(buf, "\n") // <---- Add this
}

Although your comments indicate that this is problematic, but you haven't explained how. If you update your question to explain why this is a problem, perhaps a better solution can follow.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
  • Yes, this is a solution, and does work. I was hoping that this could be handled inside the Write function of the custom io.Writer so that the writer could be passed around and the coder wouldn't have to either know, or realise they needed to do this when they want to use a progress bar. Ultimately I want to pass the progress bar around and call it/use it whenever I need one, and not have to worry about the new line each time. – amlwwalker Jun 25 '19 at 10:30
  • If you switch to an `io.WriteCloser`, *and* you know that the progress bar will close the writer when it's done, you could then make your `Close()` function write one last "\n". But that sounds a lot more intrusive than just printing a "\n". There are no magic solutions. – Jonathan Hall Jun 25 '19 at 10:32
  • Doesn't each use of the progress bar needs its own instantiation anyway? Since it's tracking different state? I don't see how you'd be able to pass a single one around anyway. – Jonathan Hall Jun 25 '19 at 10:32
  • That could be interesting for my use case. Thank you, I shall look into that. Incidentally, thanks for a more thorough discussion. I felt your approach was "help at a cost". If you like, take it as feedback. I see that I could have been more descriptive with my question. Noted. Ultimately I plan to wrap a pointer to the progressBar (and I can reassign it) and the io.writer in the tmpStruct, which I planned to pass around. So yes, a specific PB can't be reused but a pointer could and the writer can stay the same. – amlwwalker Jun 25 '19 at 10:36
  • I think what confused me re the `&tmpStruct{}` was that the function takes an io.Writer, not a `*io.Writer`, so didn't think that I should pass the address of a `tmpStruct{}` rather than a `tmpStruct{}` – amlwwalker Jun 25 '19 at 10:40