schnutibox/vendor/github.com/bufbuild/buf/internal/pkg/app/app.go
Marvin Steadfast ae35d9ab41
Some checks failed
continuous-integration/drone/push Build is failing
uses buf for compiling proto files and implements the server service
2021-05-05 11:14:17 +02:00

334 lines
8.8 KiB
Go

// Copyright 2020-2021 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package app provides application primitives.
package app
import (
"context"
"fmt"
"io"
"os"
"sort"
"github.com/bufbuild/buf/internal/pkg/interrupt"
)
// EnvContainer provides envionment variables.
type EnvContainer interface {
// Env gets the environment variable value for the key.
//
// Returns empty string if the key is not set or the value is empty.
Env(key string) string
// ForEachEnv iterates over all non-empty environment variables and calls the function.
//
// The value will never be empty.
ForEachEnv(func(string, string))
}
// NewEnvContainer returns a new EnvContainer.
//
// Empty values are effectively ignored.
func NewEnvContainer(m map[string]string) EnvContainer {
return newEnvContainer(m)
}
// NewEnvContainerForOS returns a new EnvContainer for the operating system.
func NewEnvContainerForOS() (EnvContainer, error) {
return newEnvContainerForEnviron(os.Environ())
}
// NewEnvContainerWithOverrides returns a new EnvContainer with the values of the input
// EnvContainer, overridden by the values in overrides.
//
// Empty values are effectively ignored. To unset a key, set the value to "" in overrides.
func NewEnvContainerWithOverrides(envContainer EnvContainer, overrides map[string]string) EnvContainer {
m := EnvironMap(envContainer)
for key, value := range overrides {
m[key] = value
}
return newEnvContainer(m)
}
// StdinContainer provides stdin.
type StdinContainer interface {
// Stdin provides stdin.
//
// If no value was passed when Stdio was created, this will return io.EOF on any call.
Stdin() io.Reader
}
// NewStdinContainer returns a new StdinContainer.
func NewStdinContainer(reader io.Reader) StdinContainer {
return newStdinContainer(reader)
}
// NewStdinContainerForOS returns a new StdinContainer for the operating system.
func NewStdinContainerForOS() StdinContainer {
return newStdinContainer(os.Stdin)
}
// StdoutContainer provides stdout.
type StdoutContainer interface {
// Stdout provides stdout.
//
// If no value was passed when Stdio was created, this will return io.EOF on any call.
Stdout() io.Writer
}
// NewStdoutContainer returns a new StdoutContainer.
func NewStdoutContainer(writer io.Writer) StdoutContainer {
return newStdoutContainer(writer)
}
// NewStdoutContainerForOS returns a new StdoutContainer for the operatoutg system.
func NewStdoutContainerForOS() StdoutContainer {
return newStdoutContainer(os.Stdout)
}
// StderrContainer provides stderr.
type StderrContainer interface {
// Stderr provides stderr.
//
// If no value was passed when Stdio was created, this will return io.EOF on any call.
Stderr() io.Writer
}
// NewStderrContainer returns a new StderrContainer.
func NewStderrContainer(writer io.Writer) StderrContainer {
return newStderrContainer(writer)
}
// NewStderrContainerForOS returns a new StderrContainer for the operaterrg system.
func NewStderrContainerForOS() StderrContainer {
return newStderrContainer(os.Stderr)
}
// ArgContainer provides the arguments.
type ArgContainer interface {
// NumArgs gets the number of arguments.
NumArgs() int
// Arg gets the ith argument.
//
// Panics if i < 0 || i >= Len().
Arg(i int) string
}
// NewArgContainer returns a new ArgContainer.
func NewArgContainer(args ...string) ArgContainer {
return newArgContainer(args)
}
// NewArgContainerForOS returns a new ArgContainer for the operating system.
func NewArgContainerForOS() ArgContainer {
return newArgContainer(os.Args)
}
// Container contains environment variables, args, and stdio.
type Container interface {
EnvContainer
StdinContainer
StdoutContainer
StderrContainer
ArgContainer
}
// NewContainer returns a new Container.
func NewContainer(
env map[string]string,
stdin io.Reader,
stdout io.Writer,
stderr io.Writer,
args ...string,
) Container {
return newContainer(
NewEnvContainer(env),
NewStdinContainer(stdin),
NewStdoutContainer(stdout),
NewStderrContainer(stderr),
NewArgContainer(args...),
)
}
// NewContainerForOS returns a new Container for the operating system.
func NewContainerForOS() (Container, error) {
envContainer, err := NewEnvContainerForOS()
if err != nil {
return nil, err
}
return newContainer(
envContainer,
NewStdinContainerForOS(),
NewStdoutContainerForOS(),
NewStderrContainerForOS(),
NewArgContainerForOS(),
), nil
}
// NewContainerForArgs returns a new Container with the replacement args.
func NewContainerForArgs(container Container, newArgs ...string) Container {
return newContainer(
container,
container,
container,
container,
NewArgContainer(newArgs...),
)
}
// StdioContainer is a stdio container.
type StdioContainer interface {
StdinContainer
StdoutContainer
StderrContainer
}
// EnvStdinContainer is an environment and stdin container.
type EnvStdinContainer interface {
EnvContainer
StdinContainer
}
// EnvStdoutContainer is an environment and stdout container.
type EnvStdoutContainer interface {
EnvContainer
StdoutContainer
}
// EnvStderrContainer is an environment and stderr container.
type EnvStderrContainer interface {
EnvContainer
StderrContainer
}
// EnvStdioContainer is an environment and stdio container.
type EnvStdioContainer interface {
EnvContainer
StdioContainer
}
// Environ returns all environment variables in the form "KEY=VALUE".
//
// Equivalent to os.Enviorn.
//
// Sorted.
func Environ(envContainer EnvContainer) []string {
var environ []string
envContainer.ForEachEnv(func(key string, value string) {
environ = append(environ, key+"="+value)
})
sort.Strings(environ)
return environ
}
// EnvironMap returns all environment variables in a map.
//
// No key will have an empty value.
func EnvironMap(envContainer EnvContainer) map[string]string {
m := make(map[string]string)
envContainer.ForEachEnv(func(key string, value string) {
// This should be done anyways per the EnvContainer documentation but just to make sure
if value != "" {
m[key] = value
}
})
return m
}
// Args returns all arguments.
//
// Equivalent to os.Args.
func Args(argList ArgContainer) []string {
numArgs := argList.NumArgs()
args := make([]string, numArgs)
for i := 0; i < numArgs; i++ {
args[i] = argList.Arg(i)
}
return args
}
// IsDevStdin returns true if the path is the equivalent of /dev/stdin.
func IsDevStdin(path string) bool {
return path != "" && path == DevStdinFilePath
}
// IsDevStdout returns true if the path is the equivalent of /dev/stdout.
func IsDevStdout(path string) bool {
return path != "" && path == DevStdoutFilePath
}
// IsDevStderr returns true if the path is the equivalent of /dev/stderr.
func IsDevStderr(path string) bool {
return path != "" && path == DevStderrFilePath
}
// IsDevNull returns true if the path is the equivalent of /dev/null.
func IsDevNull(path string) bool {
return path != "" && path == DevNullFilePath
}
// Main runs the application using the OS Container and calling os.Exit on the return value of Run.
func Main(ctx context.Context, f func(context.Context, Container) error) {
container, err := NewContainerForOS()
if err != nil {
printError(container, err)
os.Exit(GetExitCode(err))
}
os.Exit(GetExitCode(Run(ctx, container, f)))
}
// Run runs the application using the container.
//
// The run will be stopped on interrupt signal.
// The exit code can be determined using GetExitCode.
func Run(ctx context.Context, container Container, f func(context.Context, Container) error) error {
ctx, cancel := interrupt.WithCancel(ctx)
defer cancel()
if err := f(ctx, container); err != nil {
printError(container, err)
return err
}
return nil
}
// NewError returns a new Error that contains an exit code.
//
// The exit code cannot be 0.
func NewError(exitCode int, message string) error {
return newAppError(exitCode, message)
}
// NewErrorf returns a new error that contains an exit code.
//
// The exit code cannot be 0.
func NewErrorf(exitCode int, format string, args ...interface{}) error {
return newAppError(exitCode, fmt.Sprintf(format, args...))
}
// GetExitCode gets the exit code.
//
// If err == nil, this returns 0.
// If err was created by this package, this returns the exit code from the error.
// Otherwise, this returns 1.
func GetExitCode(err error) int {
if err == nil {
return 0
}
if appError, ok := err.(*appError); ok {
return appError.exitCode
}
return 1
}