summaryrefslogtreecommitdiff
path: root/internal/app/flags.go
blob: d83dae888cdf392a5fb4cd02a29e9751d6064dba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package app

import (
	"context"
	"fmt"
	"log/slog"
	"os"
	"strings"
	"time"

	"github.com/ChausseBenjamin/rafta/internal/logging"
	"github.com/ChausseBenjamin/rafta/internal/pb"
	"github.com/urfave/cli/v3"
)

const (
	FlagListenPort   = "port"
	FlagLogLevel     = "log-level"
	FlagLogFormat    = "log-format"
	FlagLogOutput    = "log-output"
	FlagDBPath       = "database"
	FlagGraceTimeout = "grace-timeout"
)

func flags() []cli.Flag {
	return []cli.Flag{
		// Logging {{{
		&cli.StringFlag{
			Name:    FlagLogFormat,
			Aliases: []string{"f"},
			Value:   "plain",
			Usage:   "plain, json",
			Sources: cli.EnvVars("LOG_FORMAT"),
			Action:  validateLogFormat,
		},
		&cli.StringFlag{
			Name:    FlagLogOutput,
			Aliases: []string{"o"},
			Value:   "stdout",
			Usage:   "stdout, stderr, file",
			Sources: cli.EnvVars("LOG_OUTPUT"),
			Action:  validateLogOutput,
		},
		&cli.StringFlag{
			Name:    FlagLogLevel,
			Aliases: []string{"l"},
			Value:   "info",
			Usage:   "debug, info, warn, error",
			Sources: cli.EnvVars("LOG_LEVEL"),
			Action:  validateLogLevel,
		}, // }}}
		// gRPC server {{{
		&cli.IntFlag{
			Name:    FlagListenPort,
			Aliases: []string{"p"},
			Value:   1157, // list in leetspeek :P
			Sources: cli.EnvVars("LISTEN_PORT"),
			Action:  validateListenPort,
		},
		&cli.DurationFlag{
			Name:    FlagGraceTimeout,
			Aliases: []string{"t"},
			Value:   5 * time.Second,
			Sources: cli.EnvVars("GRACEFUL_TIMEOUT"),
		}, // }}}
		// Database {{{
		&cli.StringFlag{
			Name:    FlagDBPath,
			Aliases: []string{"d"},
			Value:   "store.db",
			Usage:   "database file",
			Sources: cli.EnvVars("DATABASE_PATH"),
		}, // }}}
	}
}

func validateLogOutput(ctx context.Context, cmd *cli.Command, s string) error {
	switch {
	case s == "stdout" || s == "stderr":
		return nil
	default:
		// assume file
		f, err := os.OpenFile(s, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			slog.ErrorContext(
				ctx,
				fmt.Sprintf("Error creating/accessing provided log file %s", s),
			)
			return err
		}
		defer f.Close()
		return nil
	}
}

func validateLogLevel(ctx context.Context, cmd *cli.Command, s string) error {
	for _, lvl := range []string{"deb", "inf", "warn", "err"} {
		if strings.Contains(strings.ToLower(s), lvl) {
			return nil
		}
	}
	slog.ErrorContext(
		ctx,
		fmt.Sprintf("Unknown log level provided: %s", s),
	)
	return logging.ErrInvalidLevel
}

func validateLogFormat(ctx context.Context, cmd *cli.Command, s string) error {
	s = strings.ToLower(s)
	if s == "json" || s == "plain" {
		return nil
	}
	return nil
}

func validateListenPort(ctx context.Context, cmd *cli.Command, p int64) error {
	if p < 1024 || p > 65535 {
		slog.ErrorContext(
			ctx,
			fmt.Sprintf("Out-of-bound port provided: %d", p),
		)
		return pb.ErrOutOfBoundsPort
	}
	return nil
}