checkpoint: add configuration

This commit is contained in:
Dan Anglin 2024-06-24 09:01:28 +01:00
parent 63f0526f39
commit bb71d2988c
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
6 changed files with 138 additions and 35 deletions

View file

@ -10,6 +10,7 @@ import (
"os" "os"
"strconv" "strconv"
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
"codeflow.dananglin.me.uk/apollo/enbas/internal/executor" "codeflow.dananglin.me.uk/apollo/enbas/internal/executor"
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer" "codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
) )
@ -29,22 +30,11 @@ func main() {
func run() error { func run() error {
var ( var (
configDir string configDir string
cacheDir string noColor *bool
pager string
imageViewer string
videoPlayer string
maxTerminalWidth int
noColor *bool
) )
flag.StringVar(&configDir, "config-dir", "", "Specify your config directory") flag.StringVar(&configDir, "config-dir", "", "Specify your config directory")
flag.StringVar(&cacheDir, "cache-dir", "", "Specify your cache directory")
flag.StringVar(&pager, "pager", "", "Specify your preferred pager to page through long outputs. This is disabled by default.")
flag.StringVar(&imageViewer, "image-viewer", "", "Specify your favourite image viewer.")
flag.StringVar(&videoPlayer, "video-player", "", "Specify your favourite video player.")
flag.IntVar(&maxTerminalWidth, "max-terminal-width", 80, "Specify the maximum terminal width when displaying resources on screen.")
flag.BoolFunc("no-color", "Disable ANSI colour output when displaying text on screen", func(value string) error { flag.BoolFunc("no-color", "Disable ANSI colour output when displaying text on screen", func(value string) error {
boolVal, err := strconv.ParseBool(value) boolVal, err := strconv.ParseBool(value)
if err != nil { if err != nil {
@ -80,7 +70,15 @@ func run() error {
command := flag.Arg(0) command := flag.Arg(0)
args := flag.Args()[1:] args := flag.Args()[1:]
printer := printer.NewPrinter(*noColor, pager, maxTerminalWidth) config, err := config.NewConfigFromFile(configDir)
if err != nil {
printer := printer.NewPrinter(*noColor, "", 80)
printer.PrintFailure("unable to load the configuration: " + err.Error() + ".")
return err
}
printer := printer.NewPrinter(*noColor, config.Integrations.Pager, config.LineWrapMaxWidth)
executorMap := map[string]executor.Executor{ executorMap := map[string]executor.Executor{
executor.CommandAccept: executor.NewAcceptOrRejectExecutor( executor.CommandAccept: executor.NewAcceptOrRejectExecutor(
@ -175,10 +173,8 @@ func run() error {
), ),
executor.CommandShow: executor.NewShowExecutor( executor.CommandShow: executor.NewShowExecutor(
printer, printer,
&config,
configDir, configDir,
cacheDir,
imageViewer,
videoPlayer,
executor.CommandShow, executor.CommandShow,
executor.CommandSummaryLookup(executor.CommandShow), executor.CommandSummaryLookup(executor.CommandShow),
), ),

103
internal/config/config.go Normal file
View file

@ -0,0 +1,103 @@
package config
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
)
const (
configFileName = "config.json"
)
type Config struct {
CacheDirectory string `json:"cacheDirectory"`
LineWrapMaxWidth int `json:"lineWrapMaxWidth"`
HTTP HTTPConfig `json:"http"`
Integrations Integrations `json:"integrations"`
}
type HTTPConfig struct {
Timeout int `json:"timeout"`
MediaTimeout int `json:"mediaTimeout"`
}
type Integrations struct {
Browser string `json:"browser"`
Editor string `json:"editor"`
Pager string `json:"pager"`
ImageViewer string `json:"imageViewer"`
VideoPlayer string `json:"videoPlayer"`
}
func NewConfigFromFile(configDir string) (Config, error) {
path := configFile(configDir)
fileExists, err := utilities.FileExists(path)
if err != nil {
return Config{}, fmt.Errorf("unable to check if the config file exists: %w", err)
}
if !fileExists {
if err := saveDefaultConfigToFile(path); err != nil {
return Config{}, fmt.Errorf("unable to save the default config to file: %w", err)
}
}
file, err := os.Open(path)
if err != nil {
return Config{}, fmt.Errorf("unable to open %s: %w", path, err)
}
defer file.Close()
var config Config
if err := json.NewDecoder(file).Decode(&config); err != nil {
return Config{}, fmt.Errorf("unable to decode the JSON data: %w", err)
}
return config, nil
}
func configFile(configDir string) string {
return filepath.Join(utilities.CalculateConfigDir(configDir), configFileName)
}
func saveDefaultConfigToFile(path string) error {
file, err := os.Create(path)
if err != nil {
return fmt.Errorf("unable to create the file at %s: %w", path, err)
}
defer file.Close()
config := defaultConfig()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
if err := encoder.Encode(config); err != nil {
return fmt.Errorf("unable to save the JSON data to the config file: %w", err)
}
return nil
}
func defaultConfig() Config {
return Config{
CacheDirectory: "",
HTTP: HTTPConfig{
Timeout: 5,
MediaTimeout: 30,
},
LineWrapMaxWidth: 80,
Integrations: Integrations{
Browser: "",
Editor: "",
Pager: "",
ImageViewer: "",
VideoPlayer: "",
},
}
}

View file

@ -109,7 +109,7 @@ func NewCredentialsConfigFromFile(configDir string) (CredentialsConfig, error) {
file, err := os.Open(path) file, err := os.Open(path)
if err != nil { if err != nil {
return CredentialsConfig{}, fmt.Errorf("unable to open %s, %w", path, err) return CredentialsConfig{}, fmt.Errorf("unable to open %s: %w", path, err)
} }
defer file.Close() defer file.Close()
@ -127,9 +127,8 @@ func saveCredentialsConfigFile(authConfig CredentialsConfig, configDir string) e
file, err := os.Create(path) file, err := os.Create(path)
if err != nil { if err != nil {
return fmt.Errorf("unable to open %s: %w", path, err) return fmt.Errorf("unable to create the file at %s: %w", path, err)
} }
defer file.Close() defer file.Close()
encoder := json.NewEncoder(file) encoder := json.NewEncoder(file)

View file

@ -102,3 +102,11 @@ type NotFollowingError struct {
func (e NotFollowingError) Error() string { func (e NotFollowingError) Error() string {
return "you are not following " + e.Account return "you are not following " + e.Account
} }
type UnknownMediaAttachmentError struct {
AttachmentID string
}
func (e UnknownMediaAttachmentError) Error() string {
return "unknown media attachment '" + e.AttachmentID + "'"
}

View file

@ -11,6 +11,7 @@ import (
"strings" "strings"
"codeflow.dananglin.me.uk/apollo/enbas/internal/client" "codeflow.dananglin.me.uk/apollo/enbas/internal/client"
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
"codeflow.dananglin.me.uk/apollo/enbas/internal/model" "codeflow.dananglin.me.uk/apollo/enbas/internal/model"
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer" "codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities" "codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
@ -20,12 +21,12 @@ type ShowExecutor struct {
*flag.FlagSet *flag.FlagSet
printer *printer.Printer printer *printer.Printer
config *config.Config
myAccount bool myAccount bool
skipAccountRelationship bool skipAccountRelationship bool
showUserPreferences bool showUserPreferences bool
showInBrowser bool showInBrowser bool
configDir string configDir string
cacheRoot string
resourceType string resourceType string
accountName string accountName string
statusID string statusID string
@ -34,21 +35,17 @@ type ShowExecutor struct {
tag string tag string
pollID string pollID string
fromResourceType string fromResourceType string
imageViewer string
videoPlayer string
limit int limit int
attachmentIDs MultiStringFlagValue attachmentIDs MultiStringFlagValue
} }
func NewShowExecutor(printer *printer.Printer, configDir, cacheRoot, imageViewer, videoPlayer, name, summary string) *ShowExecutor { func NewShowExecutor(printer *printer.Printer, config *config.Config, configDir, name, summary string) *ShowExecutor {
showExe := ShowExecutor{ showExe := ShowExecutor{
FlagSet: flag.NewFlagSet(name, flag.ExitOnError), FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
printer: printer, printer: printer,
configDir: configDir, config: config,
cacheRoot: cacheRoot, configDir: configDir,
imageViewer: imageViewer,
videoPlayer: videoPlayer,
} }
showExe.BoolVar(&showExe.myAccount, flagMyAccount, false, "Set to true to lookup your account") showExe.BoolVar(&showExe.myAccount, flagMyAccount, false, "Set to true to lookup your account")
@ -471,7 +468,7 @@ func (s *ShowExecutor) showMediaFromStatus(gtsClient *client.Client) error {
} }
cacheDir := filepath.Join( cacheDir := filepath.Join(
utilities.CalculateCacheDir(s.cacheRoot, utilities.GetFQDN(gtsClient.Authentication.Instance)), utilities.CalculateCacheDir(s.config.CacheDirectory, utilities.GetFQDN(gtsClient.Authentication.Instance)),
"media", "media",
) )
@ -498,7 +495,7 @@ func (s *ShowExecutor) showMediaFromStatus(gtsClient *client.Client) error {
for _, attachmentID := range s.attachmentIDs { for _, attachmentID := range s.attachmentIDs {
mediaObj, ok := attachmentsHashMap[attachmentID] mediaObj, ok := attachmentsHashMap[attachmentID]
if !ok { if !ok {
return fmt.Errorf("unknown media attachment: %s", attachmentID) return UnknownMediaAttachmentError{AttachmentID: attachmentID}
} }
split := strings.Split(mediaObj.url, "/") split := strings.Split(mediaObj.url, "/")
@ -533,13 +530,13 @@ func (s *ShowExecutor) showMediaFromStatus(gtsClient *client.Client) error {
} }
if len(imageFiles) > 0 { if len(imageFiles) > 0 {
if err := utilities.OpenMedia(s.imageViewer, imageFiles); err != nil { if err := utilities.OpenMedia(s.config.Integrations.ImageViewer, imageFiles); err != nil {
return fmt.Errorf("unable to open the image viewer: %w", err) return fmt.Errorf("unable to open the image viewer: %w", err)
} }
} }
if len(videoFiles) > 0 { if len(videoFiles) > 0 {
if err := utilities.OpenMedia(s.videoPlayer, videoFiles); err != nil { if err := utilities.OpenMedia(s.config.Integrations.VideoPlayer, videoFiles); err != nil {
return fmt.Errorf("unable to open the video player: %w", err) return fmt.Errorf("unable to open the video player: %w", err)
} }
} }

View file

@ -20,7 +20,7 @@ func CalculateConfigDir(configDir string) string {
configRoot, err := os.UserConfigDir() configRoot, err := os.UserConfigDir()
if err != nil { if err != nil {
configRoot = "." return filepath.Join(os.Getenv("HOME"), "."+internal.ApplicationName, "config")
} }
return filepath.Join(configRoot, internal.ApplicationName) return filepath.Join(configRoot, internal.ApplicationName)
@ -33,7 +33,7 @@ func CalculateCacheDir(cacheDir, instanceFQDN string) string {
cacheRoot, err := os.UserCacheDir() cacheRoot, err := os.UserCacheDir()
if err != nil { if err != nil {
cacheRoot = "." return filepath.Join(os.Getenv("HOME"), "."+internal.ApplicationName, "cache")
} }
return filepath.Join(cacheRoot, internal.ApplicationName, instanceFQDN) return filepath.Join(cacheRoot, internal.ApplicationName, instanceFQDN)