Skip to content

Conversation

@cardil
Copy link

@cardil cardil commented Feb 28, 2023

Fixes #65
Require mattn/go-isatty#81

Comment on lines +7 to +8
// FIXME: remove, after merge of https://github.com/mattn/go-isatty/pull/81
replace github.com/mattn/go-isatty => github.com/cardil/mattn-isatty v0.0.0-20230228121058-48002435730c
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this before merge

@dolmen
Copy link
Contributor

dolmen commented Mar 22, 2023

  1. This implementation breaks the existing API. go-colorable is a widely used module
  2. The proposed changes could be implemented in a package on top of go-colorable or as a simple function:
func setColorable(w io.Writer) io.Writer {
    if f, isFile := w.(*os.File); isFile {
        return colorable.NewColorable(f)
    }
    return w
}

So 👎

@cardil
Copy link
Author

cardil commented Apr 1, 2023

  1. This implementation breaks the existing API. go-colorable is a widely used module

Can you explain how it breaks the API? I'm changing here the parameter type from *os.File to io.Writer. The *os.File implements the io.Writer, so every instance of *os.File can be passed to the function that accepts io.Writer, so this widens the API, not breaks it.

@abhinav
Copy link

abhinav commented Apr 16, 2023

Hey @cardil, thanks for the PR and @dolmen, thanks for reviewing.
I'm a lurker who's interested in this PR, hoping to help with this discussion.

First, to answer your question @cardil: changing the function signature is a breaking change.
As an example, this will break all function references. For example, if I was previously doing this:

someFunc(colorable.NewColorable)

// where

func someFunc(newColorable func(*os.File) io.Writer) {
  // ...
}

That code will now stop compiling because newColorable's type won't match. (It's not uncommon to use function references like this to provide stubs from tests.)

@dolmen, unfortunately, that alternative won't suffice because it still requires a specific implementation (*os.File) and doesn't allow for file-like objects that expose their file descriptor, but aren't *os.Files. For example, if I wrote a wrapper around an os.File that modifies some of its reading/writing behavior (e.g. tracks total number of bytes written), I would be unable to use it with go-colorable. And on the cosmetic end, it's nicer to be able to write loosely coupled code: function foo takes an io.Writer and adds special behavior only if the underlying file descriptor (if any) happens to meet these conditions, and isn't required to be an os.File.


To do this in a backwards compatible manner, this needs to be a new function. As a suggestion, consider:

func NewColorableWriter(w io.Writer) io.Writer {
  // ...
}

This could be made easier by putting the existing NewColorable function into a regular file (not tagged with a platform-specific build tag), that just calls the individual platform-specific NewColorableWriter function. Roughly:

// colorable.go
func NewColorable(f *os.File) io.Writer {
  return NewColorableWriter(f)
}

// colorable_windows.go and colorable_others.go
func NewColorableWriter(w io.Writer) io.Writer {
  // ...
}

This change would be completely backwards-compatible and support this functionality on io.Writers.

What do you think, @cardil @dolmen?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow passing io.Writer instead of *os.File to the NewColorable func

3 participants