1package termenv
2
3import (
4 "io"
5 "os"
6 "sync"
7)
8
9// output is the default global output.
10var output = NewOutput(os.Stdout)
11
12// File represents a file descriptor.
13//
14// Deprecated: Use *os.File instead.
15type File interface {
16 io.ReadWriter
17 Fd() uintptr
18}
19
20// OutputOption sets an option on Output.
21type OutputOption = func(*Output)
22
23// Output is a terminal output.
24type Output struct {
25 Profile
26 w io.Writer
27 environ Environ
28
29 assumeTTY bool
30 unsafe bool
31 cache bool
32 fgSync *sync.Once
33 fgColor Color
34 bgSync *sync.Once
35 bgColor Color
36}
37
38// Environ is an interface for getting environment variables.
39type Environ interface {
40 Environ() []string
41 Getenv(string) string
42}
43
44type osEnviron struct{}
45
46func (oe *osEnviron) Environ() []string {
47 return os.Environ()
48}
49
50func (oe *osEnviron) Getenv(key string) string {
51 return os.Getenv(key)
52}
53
54// DefaultOutput returns the default global output.
55func DefaultOutput() *Output {
56 return output
57}
58
59// SetDefaultOutput sets the default global output.
60func SetDefaultOutput(o *Output) {
61 output = o
62}
63
64// NewOutput returns a new Output for the given writer.
65func NewOutput(w io.Writer, opts ...OutputOption) *Output {
66 o := &Output{
67 w: w,
68 environ: &osEnviron{},
69 Profile: -1,
70 fgSync: &sync.Once{},
71 fgColor: NoColor{},
72 bgSync: &sync.Once{},
73 bgColor: NoColor{},
74 }
75
76 if o.w == nil {
77 o.w = os.Stdout
78 }
79 for _, opt := range opts {
80 opt(o)
81 }
82 if o.Profile < 0 {
83 o.Profile = o.EnvColorProfile()
84 }
85
86 return o
87}
88
89// WithEnvironment returns a new OutputOption for the given environment.
90func WithEnvironment(environ Environ) OutputOption {
91 return func(o *Output) {
92 o.environ = environ
93 }
94}
95
96// WithProfile returns a new OutputOption for the given profile.
97func WithProfile(profile Profile) OutputOption {
98 return func(o *Output) {
99 o.Profile = profile
100 }
101}
102
103// WithColorCache returns a new OutputOption with fore- and background color values
104// pre-fetched and cached.
105func WithColorCache(v bool) OutputOption {
106 return func(o *Output) {
107 o.cache = v
108
109 // cache the values now
110 _ = o.ForegroundColor()
111 _ = o.BackgroundColor()
112 }
113}
114
115// WithTTY returns a new OutputOption to assume whether or not the output is a TTY.
116// This is useful when mocking console output.
117func WithTTY(v bool) OutputOption {
118 return func(o *Output) {
119 o.assumeTTY = v
120 }
121}
122
123// WithUnsafe returns a new OutputOption with unsafe mode enabled. Unsafe mode doesn't
124// check whether or not the terminal is a TTY.
125//
126// This option supersedes WithTTY.
127//
128// This is useful when mocking console output and enforcing ANSI escape output
129// e.g. on SSH sessions.
130func WithUnsafe() OutputOption {
131 return func(o *Output) {
132 o.unsafe = true
133 }
134}
135
136// ForegroundColor returns the terminal's default foreground color.
137func (o *Output) ForegroundColor() Color {
138 f := func() {
139 if !o.isTTY() {
140 return
141 }
142
143 o.fgColor = o.foregroundColor()
144 }
145
146 if o.cache {
147 o.fgSync.Do(f)
148 } else {
149 f()
150 }
151
152 return o.fgColor
153}
154
155// BackgroundColor returns the terminal's default background color.
156func (o *Output) BackgroundColor() Color {
157 f := func() {
158 if !o.isTTY() {
159 return
160 }
161
162 o.bgColor = o.backgroundColor()
163 }
164
165 if o.cache {
166 o.bgSync.Do(f)
167 } else {
168 f()
169 }
170
171 return o.bgColor
172}
173
174// HasDarkBackground returns whether terminal uses a dark-ish background.
175func (o *Output) HasDarkBackground() bool {
176 c := ConvertToRGB(o.BackgroundColor())
177 _, _, l := c.Hsl()
178 return l < 0.5 //nolint:mnd
179}
180
181// TTY returns the terminal's file descriptor. This may be nil if the output is
182// not a terminal.
183//
184// Deprecated: Use Writer() instead.
185func (o Output) TTY() File {
186 if f, ok := o.w.(File); ok {
187 return f
188 }
189 return nil
190}
191
192// Writer returns the underlying writer. This may be of type io.Writer,
193// io.ReadWriter, or *os.File.
194func (o Output) Writer() io.Writer {
195 return o.w
196}
197
198func (o Output) Write(p []byte) (int, error) {
199 return o.w.Write(p) //nolint:wrapcheck
200}
201
202// WriteString writes the given string to the output.
203func (o Output) WriteString(s string) (int, error) {
204 return o.Write([]byte(s))
205}