<commit_before>package commands

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"runtime"
	"sort"
	"strings"

	"gnd.la/app"
	"gnd.la/internal/runtimeutil"
	"gnd.la/signal"
	"gnd.la/tasks"
	"gnd.la/util/stringutil"
)

var (
	commands = map[string]*command{}
	executed = false
)

type command struct {
	handler app.Handler
	opts    options
}

// Register registers a new command with the
// given function and options.
func Register(f app.Handler, opts ...Option) error {
	o := options{}
	for _, f := range opts {
		o = f(o)
	}
	if o.Name == "" {
		qname := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
		p := strings.Split(qname, ".")
		o.Name = p[len(p)-1]
		if o.Name == "" {
			return fmt.Errorf("could not determine name for function %v. Please, provide a name using Name().", f)
		}
		o.Name = stringutil.CamelCaseToLower(o.Name, "-")
	}
	if _, ok := commands[o.Name]; ok {
		return fmt.Errorf("duplicate command name %q", o.Name)
	}
	commands[o.Name] = &command{
		handler: f,
		opts:    o,
	}
	return nil
}

// Remove eliminates a previously registered command.
func Remove(name string) {
	for k := range commands {
		if k == name {
			delete(commands, k)
			break
		}
	}
}

// MustRegister works like Register, but panics
// if there's an error
func MustRegister(f app.Handler, opts ...Option) {
	if err := Register(f, opts...); err != nil {
		panic(err)
	}
}

// Error stops the command and prints the
// given error.
func Error(args ...interface{}) {
	panic(fmt.Sprint(args...))
}

// Errorf works like Error, but accepts a format parameter.
func Errorf(format string, args ...interface{}) {
	panic(fmt.Sprintf(format, args...))
}

// UsageError stops the command and prints the
// given error followed by the command usage.
func UsageError(args ...interface{}) {
	usageErrors(fmt.Sprint(args...))
}

// UsageErrorf works like UsageError, but accepts
// a format parameter.
func UsageErrorf(format string, args ...interface{}) {
	usageErrors(fmt.Sprintf(format, args...))
}

type usageError string

func (e usageError) Error() string {
	return string(e)
}

func usageErrors(s string) {
	err := usageError(s)
	panic(err)
}

func executeCommand(name string, cmd *command, args []string, a *app.App) (err error) {
	// Parse command flags
	set := flag.NewFlagSet(name, flag.ContinueOnError)
	set.Usage = func() {
		commandHelp(name, -1, os.Stderr)
	}
	flags := map[string]interface{}{}
	for _, arg := range cmd.opts.Flags {
		switch arg.typ {
		case typBool:
			var b bool
			set.BoolVar(&b, arg.name, arg.def.(bool), arg.help)
			flags[arg.name] = &b
		case typInt:
			var i int
			set.IntVar(&i, arg.name, arg.def.(int), arg.help)
			flags[arg.name] = &i
		case typString:
			var s string
			set.StringVar(&s, arg.name, arg.def.(string), arg.help)
			flags[arg.name] = &s
		default:
			panic("invalid arg type")
		}
	}
	// Print error/help messages ourselves
	set.SetOutput(ioutil.Discard)
	err = set.Parse(args)
	if err != nil {
		if err == flag.ErrHelp {
			return
		}
		if strings.Contains(err.Error(), "provided but not defined") {
			flagName := strings.TrimSpace(strings.Split(err.Error(), ":")[1])
			fmt.Fprintf(os.Stderr, "command %s does not accept flag %s\n", name, flagName)
			return
		}
		return err
	}
	var params []string
	paramValues := make(map[string]string)
	for _, arg := range cmd.opts.Flags {
		params = append(params, arg.name)
		paramValues[arg.name] = fmt.Sprintf("%v", reflect.ValueOf(flags[arg.name]).Elem().Interface())
	}
	provider := &contextProvider{
		args:        set.Args(),
		params:      params,
		paramValues: paramValues,
	}
	defer func() {
		if r := recover(); r != nil {
			if e, ok := r.(error); ok {
				err = e
			} else {
				err = fmt.Errorf("%v", r)
			}
			if file, line, ok := runtimeutil.PanicLocation(); ok {
				err = fmt.Errorf("%v (at %s:%d)", err, file, line)
			}
		}
	}()
	ctx := a.NewContext(provider)
	defer a.CloseContext(ctx)
	cmd.handler(ctx)
	return
}

// Execute tries to run a command<commit_msg>Allow disabling recovering during commands via env variable<commit_after>package commands

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"runtime"
	"sort"
	"strings"

	"gnd.la/app"
	"gnd.la/internal/runtimeutil"
	"gnd.la/signal"
	"gnd.la/tasks"
	"gnd.la/util/stringutil"
)

var (
	commands = map[string]*command{}
	executed = false
)

type command struct {
	handler app.Handler
	opts    options
}

// Register registers a new command with the
// given function and options.
func Register(f app.Handler, opts ...Option) error {
	o := options{}
	for _, f := range opts {
		o = f(o)
	}
	if o.Name == "" {
		qname := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
		p := strings.Split(qname, ".")
		o.Name = p[len(p)-1]
		if o.Name == "" {
			return fmt.Errorf("could not determine name for function %v. Please, provide a name using Name().", f)
		}
		o.Name = stringutil.CamelCaseToLower(o.Name, "-")
	}
	if _, ok := commands[o.Name]; ok {
		return fmt.Errorf("duplicate command name %q", o.Name)
	}
	commands[o.Name] = &command{
		handler: f,
		opts:    o,
	}
	return nil
}

// Remove eliminates a previously registered command.
func Remove(name string) {
	for k := range commands {
		if k == name {
			delete(commands, k)
			break
		}
	}
}

// MustRegister works like Register, but panics
// if there's an error
func MustRegister(f app.Handler, opts ...Option) {
	if err := Register(f, opts...); err != nil {
		panic(err)
	}
}

// Error stops the command and prints the
// given error.
func Error(args ...interface{}) {
	panic(fmt.Sprint(args...))
}

// Errorf works like Error, but accepts a format parameter.
func Errorf(format string, args ...interface{}) {
	panic(fmt.Sprintf(format, args...))
}

// UsageError stops the command and prints the
// given error followed by the command usage.
func UsageError(args ...interface{}) {
	usageErrors(fmt.Sprint(args...))
}

// UsageErrorf works like UsageError, but accepts
// a format parameter.
func UsageErrorf(format string, args ...interface{}) {
	usageErrors(fmt.Sprintf(format, args...))
}

type usageError string

func (e usageError) Error() string {
	return string(e)
}

func usageErrors(s string) {
	err := usageError(s)
	panic(err)
}

func executeCommand(name string, cmd *command, args []string, a *app.App) (err error) {
	// Parse command flags
	set := flag.NewFlagSet(name, flag.ContinueOnError)
	set.Usage = func() {
		commandHelp(name, -1, os.Stderr)
	}
	flags := map[string]interface{}{}
	for _, arg := range cmd.opts.Flags {
		switch arg.typ {
		case typBool:
			var b bool
			set.BoolVar(&b, arg.name, arg.def.(bool), arg.help)
			flags[arg.name] = &b
		case typInt:
			var i int
			set.IntVar(&i, arg.name, arg.def.(int), arg.help)
			flags[arg.name] = &i
		case typString:
			var s string
			set.StringVar(&s, arg.name, arg.def.(string), arg.help)
			flags[arg.name] = &s
		default:
			panic("invalid arg type")
		}
	}
	// Print error/help messages ourselves
	set.SetOutput(ioutil.Discard)
	err = set.Parse(args)
	if err != nil {
		if err == flag.ErrHelp {
			return
		}
		if strings.Contains(err.Error(), "provided but not defined") {
			flagName := strings.TrimSpace(strings.Split(err.Error(), ":")[1])
			fmt.Fprintf(os.Stderr, "command %s does not accept flag %s\n", name, flagName)
			return
		}
		return err
	}
	var params []string
	paramValues := make(map[string]string)
	for _, arg := range cmd.opts.Flags {
		params = append(params, arg.name)
		paramValues[arg.name] = fmt.Sprintf("%v", reflect.ValueOf(flags[arg.name]).Elem().Interface())
	}
	provider := &contextProvider{
		args:        set.Args(),
		params:      params,
		paramValues: paramValues,
	}
	if os.Getenv("GONDOLA_COMMANDS_NO_RECOVER") == "" {
		defer func() {
			if r := recover(); r != nil {
				if e, ok := r.(error); ok {
					err = e
				} else {
					err = fmt.Errorf("%v", r)
				}
				if file, line, ok := runtimeutil.PanicLocation(); ok {
					err = fmt.Errorf("%v (at %s:%d)", err, file, line)
				}
			}
		}()
	}
	ctx := a.NewContext(provider)
	defer a.CloseContext(ctx)
	cmd.handler(ctx)
	return
}

// Execute tries to run a command
{'fraction_non_alphanumeric': 0.1598077870872351, 'fraction_numerical': 0.0007392804337111878, 'mean_word_length': 3.315257841573631, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2183607', 'n_tokens_mistral': 3085, 'n_tokens_neox': 2877, 'n_words': 786}