package main
import (
"ccat/stack"
"embed"
"errors"
"io/fs"
"os"
"os/user"
"path/filepath"
"regexp"
"runtime"
"strings"
"gopkg.in/yaml.v2"
)
//go:embed config
var storedConfigs embed . FS // Embed the folder containing config files
func runningOnWindows ( ) bool {
return runtime . GOOS == "windows"
}
// generateDefaultConfigs is used to generate a folder of default config files
// for common languages. These default config files are embedded into the program, and will
// be outputted into a directory.
//
// If there is an error encountered, the error is returned.
func generateDefaultConfigs ( ) error {
var configOutputPath string // Location of config files, depends on OS
if runningOnWindows ( ) {
configOutputPath = "%APPDATA%\\ccat"
} else {
currentUser , err := user . Current ( )
if err != nil {
panic ( err )
}
configOutputPath = filepath . Join ( "/home/" + currentUser . Username + "/.config/ccat/" )
}
err := os . MkdirAll ( configOutputPath , 0755 )
if err != nil {
if os . IsExist ( err ) {
return errors . New ( "Directory already exists." )
} else {
return errors . New ( "Unable to create directory." )
}
}
// Copy each folder from the embedded filesystem, into the destination path
err = fs . WalkDir ( storedConfigs , "config" , func ( path string , d fs . DirEntry , err error ) error {
if err != nil {
return err
}
if d . IsDir ( ) { // Skip directories
return nil
}
relPath , _ := filepath . Rel ( "config" , path )
dstPath := filepath . Join ( configOutputPath , relPath ) // Destination path
data , err := os . ReadFile ( path )
if err != nil {
return err
}
if err := os . WriteFile ( dstPath , data , 0644 ) ; err != nil {
return err
}
return nil
} )
return nil
}
// loadConfig takes in the filename of a config file. It reads the file,
// and returns a stack of RegColors, with the item at the bottom being the one that
// was read first. This ensures that, _when accessing the RegColors in the stack, the last
// one (ie. the one that was read first) has highest precedence_.
// If there is an error compiling the regular expressions, the error is returned.
func loadConfig ( configFilename string ) ( stack . Stack [ regColor ] , error ) {
configFile , err := os . ReadFile ( configFilename )
if err != nil {
return * stack . NewStack [ regColor ] ( 0 ) , err
}
// Here, I create a MapSlice. This is a slice of key-value pairs, and will
// store the results of unmarshalling the YAML file.
tempMapSlice := yaml . MapSlice { }
if err := yaml . Unmarshal ( configFile , & tempMapSlice ) ; err != nil {
return * stack . NewStack [ regColor ] ( 0 ) , err
}
// Here, I create the stack which will eventually be returned.
// Each element of the MapSlice (created above) stores the key and value of a line
// in the file.
// Each regex string is compiled, and if there is an error, that error is
// returned.
regColorStack := stack . NewStack [ regColor ] ( len ( strings . Split ( string ( configFile ) , "\n" ) ) ) // The stack will have the same size as the number of lines in the file
for _ , item := range tempMapSlice {
re := regexp . MustCompile ( item . Key . ( string ) )
clr , err := newColor ( item . Value . ( string ) )
if err != nil {
return * stack . NewStack [ regColor ] ( 0 ) , err
}
// If we got past the errors, then the color _must_ be valid.
regColorStack . Push ( regColor { re , clr } )
}
return * regColorStack , nil
}