752 lines
20 KiB
Go
752 lines
20 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"github.com/ghodss/yaml"
|
|
"github.com/lucasb-eyer/go-colorful"
|
|
"io"
|
|
"io/ioutil"
|
|
"math/rand"
|
|
"os"
|
|
"os/exec"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"text/template"
|
|
"time"
|
|
)
|
|
|
|
// ConfigData is used to to load the json data.
|
|
type ConfigData struct {
|
|
Files []string `json:"files"` // A list of files that the config uses.
|
|
Current string `json:"current"` // The current config being used.
|
|
}
|
|
|
|
// Arguments is a struct I use to pass into each command.
|
|
type Arguments struct {
|
|
Param string // The second argument passed to the script.
|
|
Files []string // The rest of the arguments passed. This is always a list of files.
|
|
Force bool // Used to force the loading of a config
|
|
}
|
|
|
|
// Template is data that will be passed to the template function.
|
|
type Template struct {
|
|
Data map[string]interface{} // A mapping of key, value pairs for config info.
|
|
Name string // The name of the config you're loading.
|
|
}
|
|
|
|
// FillDefaults will compare a second Template struct and fill in any that are missing in the original Template.
|
|
func (t *Template) FillDefaults(other Template) {
|
|
for name, value := range other.Data {
|
|
_, ok := t.Data[name]
|
|
if !ok {
|
|
t.Data[name] = value
|
|
}
|
|
}
|
|
}
|
|
|
|
// RGB is just a simple struct that contains RGB information.
|
|
type RGB struct {
|
|
R float64
|
|
G float64
|
|
B float64
|
|
}
|
|
|
|
// Command is information and a reference to the commands usable at the command line.
|
|
type Command struct {
|
|
Reference func(Arguments)
|
|
Description string
|
|
Usage string
|
|
}
|
|
|
|
// confDir is the config directory that stores all of the data.
|
|
var confDir = os.ExpandEnv("$HOME/.gorice/")
|
|
|
|
// EDITOR is the bash variable $EDITOR.
|
|
var EDITOR = os.Getenv("EDITOR")
|
|
|
|
// commands is a mapping of usable command line commands.
|
|
var commands = map[string]Command{
|
|
"create": Command{
|
|
Reference: createGroup,
|
|
Description: "Creates a group to store configs.",
|
|
Usage: "group_name"},
|
|
"load": Command{
|
|
Reference: loadConfig,
|
|
Description: "Loads a config.",
|
|
Usage: "group_name/config_name"},
|
|
"track": Command{
|
|
Reference: addFiles,
|
|
Description: "Tracks files to be put through the config parser for a group.",
|
|
Usage: "group_name /path/to/file.txt /path/to/other.whatever"},
|
|
"tracked": Command{
|
|
Reference: listFiles,
|
|
Description: "Lists the files the program is tracking for a group.",
|
|
Usage: "group_name"},
|
|
"untrack": Command{
|
|
Reference: removeFiles,
|
|
Description: "Removes files from the tracked list for a group.",
|
|
Usage: "group_name /path/to/file.txt /path.to/other.whatever"},
|
|
"edit": Command{
|
|
Reference: editConfig,
|
|
Description: "Edits a config file.",
|
|
Usage: "group_name/config_name"},
|
|
"list": Command{
|
|
Reference: listConfigs,
|
|
Description: "Lists the configs you have for a group.",
|
|
Usage: "group_name"},
|
|
"reload": Command{
|
|
Reference: reloadConfig,
|
|
Description: "Reloads the current config for a group.",
|
|
Usage: "group_name"},
|
|
"delete": Command{
|
|
Reference: deleteConfig,
|
|
Description: "Removes a config from the group.",
|
|
Usage: "group_name/config_name",
|
|
},
|
|
"dump": Command{
|
|
Reference: dumpGroup,
|
|
Description: "Loads every config and dumps the data into a file.",
|
|
Usage: "group_name /path/to/output.template",
|
|
},
|
|
"check": Command{
|
|
Reference: checkGroup,
|
|
Description: "Checks all configs in the group, if any cannot be loaded it outputs them.",
|
|
Usage: "group_name",
|
|
},
|
|
}
|
|
|
|
// funcmap is a simple mapping of functions that will get passed to the template files.
|
|
var funcmap = template.FuncMap{
|
|
"rgb": hex2rgb,
|
|
"sumInts": sumInts,
|
|
"increment": increment,
|
|
"rgbmax": hex2rgbMax,
|
|
}
|
|
|
|
// Increment increments a color by a certain amount, can be negative.
|
|
func increment(hex string, amount float64) string {
|
|
color, _ := colorful.Hex(hex)
|
|
r, g, b := (color.R*255)+amount, (color.G*255)+amount, (color.B*255)+amount
|
|
|
|
// Not exactly sure if I need pointers for this but whatever.
|
|
colorz := []*float64{&r, &g, &b}
|
|
for c := range colorz {
|
|
if c < 0 {
|
|
c = 0
|
|
}
|
|
if c > 255 {
|
|
c = 255
|
|
}
|
|
}
|
|
new := colorful.Color{R: r / 255, G: g / 255, B: b / 255}
|
|
return new.Hex()
|
|
}
|
|
|
|
// sumInts sums some ints. Simple huh?
|
|
func sumInts(ints ...int) int {
|
|
var total int
|
|
for i := range ints {
|
|
total += i
|
|
}
|
|
return total
|
|
}
|
|
|
|
// hex2rgb congers a hex string to an RGB structure. Used in the templates.
|
|
func hex2rgb(hex string) RGB {
|
|
color, _ := colorful.Hex(hex)
|
|
return RGB{color.R * 255, color.G * 255, color.B * 255}
|
|
}
|
|
|
|
func hex2rgbMax(hex string, max float64) RGB {
|
|
color, _ := colorful.Hex(hex)
|
|
return RGB{color.R * max, color.G * max, color.B * max}
|
|
}
|
|
|
|
// addFiles adds files to the configuration group. Nothing more, nothing less.
|
|
func addFiles(arg Arguments) {
|
|
backupFiles(arg.Files)
|
|
data := loadData(arg.Param)
|
|
data.Files = append(data.Files, arg.Files...)
|
|
dumpData(arg.Param, data)
|
|
}
|
|
|
|
// removeFiles removes files from the configuration group.
|
|
func removeFiles(arg Arguments) {
|
|
data := loadData(arg.Param)
|
|
files := data.Files
|
|
new := []string{}
|
|
for _, name := range files {
|
|
if !isTracking(arg.Files, name) {
|
|
new = append(new, name)
|
|
} else {
|
|
os.Remove(fmt.Sprintf("%s.template", name))
|
|
}
|
|
}
|
|
data.Files = new
|
|
dumpData(arg.Param, data)
|
|
}
|
|
|
|
func isTracking(array []string, data string) bool {
|
|
for _, item := range array {
|
|
if item == data {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// listFiles lists files tracked in the configuration group.
|
|
func listFiles(arg Arguments) {
|
|
data := loadData(arg.Param)
|
|
files := data.Files
|
|
for _, name := range files {
|
|
fmt.Println(name)
|
|
}
|
|
}
|
|
|
|
// dumpData takes a huge data dump in a json file.
|
|
// Trust me, it will feel a lot better once it's done.
|
|
func dumpData(groupName string, data ConfigData) {
|
|
jsonData, err := json.Marshal(data)
|
|
fullpath := fmt.Sprintf("%stemplates/%s/data.json", confDir, groupName)
|
|
if checkError(err, false) {
|
|
return
|
|
}
|
|
fileWrite(fullpath, string(jsonData))
|
|
}
|
|
|
|
// dumpGroup outputs all information from a group into a template.
|
|
func dumpGroup(arg Arguments) {
|
|
groupName := arg.Param
|
|
files := arg.Files
|
|
|
|
output := []Template{}
|
|
dir := configList(groupName)
|
|
groupName = strings.TrimPrefix(groupName, ".")
|
|
for _, name := range dir {
|
|
data, err := loadTemplateData(groupName, name+".yaml")
|
|
checkError(err, true)
|
|
output = append(output, data)
|
|
}
|
|
|
|
for _, file := range files {
|
|
if !strings.HasSuffix(file, ".template") {
|
|
fmt.Printf("File %s is not a template file, skipping.\n", file)
|
|
}
|
|
err := dumpGroupFile(file, output)
|
|
checkError(err, false)
|
|
}
|
|
}
|
|
|
|
// dumpGroupFile outputs information to a single file.
|
|
func dumpGroupFile(templateFile string, info []Template) error {
|
|
var output bytes.Buffer
|
|
|
|
originalFile := strings.TrimSuffix(templateFile, ".template")
|
|
data, err := fileRead(templateFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
template := template.New(originalFile)
|
|
template.Funcs(funcmap)
|
|
tmp, err := template.Parse(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = tmp.Execute(&output, info)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
result := output.String()
|
|
os.Remove(originalFile)
|
|
fileWrite(originalFile, result)
|
|
return nil
|
|
}
|
|
|
|
func checkGroup(arg Arguments) {
|
|
groupName := arg.Param
|
|
files := configList(groupName)
|
|
groupName = strings.TrimPrefix(groupName, ".")
|
|
|
|
for _, name := range files {
|
|
_, err := loadTemplateData(groupName, name+".yaml")
|
|
if err != nil {
|
|
fmt.Println(name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// listConfigs outputs a list of the configs you have in a certain group.
|
|
func listConfigs(arg Arguments) {
|
|
var groupName = string(arg.Param)
|
|
items := configList(groupName)
|
|
for _, item := range items {
|
|
fmt.Println(item)
|
|
}
|
|
}
|
|
|
|
func configList(groupName string) []string {
|
|
showHidden := false
|
|
output := []string{}
|
|
|
|
if strings.HasPrefix(groupName, ".") {
|
|
groupName = strings.TrimPrefix(groupName, ".")
|
|
showHidden = true
|
|
}
|
|
|
|
folderPath := fmt.Sprintf("%stemplates/%s", confDir, groupName)
|
|
dir, err := ioutil.ReadDir(folderPath)
|
|
checkError(err, true)
|
|
|
|
for _, file := range dir {
|
|
name := file.Name()
|
|
if (strings.HasPrefix(name, ".") && !showHidden) || !strings.HasSuffix(name, ".yaml") {
|
|
continue
|
|
}
|
|
output = append(output, strings.TrimSuffix(name, ".yaml"))
|
|
}
|
|
return output
|
|
}
|
|
|
|
// loadData loads one of the config groups data.json file
|
|
func loadData(groupName string) ConfigData {
|
|
var config ConfigData
|
|
fullPath := fmt.Sprintf("%stemplates/%s/data.json", confDir, groupName)
|
|
file, err := os.Open(fullPath)
|
|
|
|
if !checkError(err, false) {
|
|
data, _ := ioutil.ReadAll(file)
|
|
json.Unmarshal(data, &config)
|
|
return config
|
|
}
|
|
return ConfigData{}
|
|
}
|
|
|
|
// watchConfig watches a config file for changes while you are editing it.
|
|
// This is used to reload it on the fly without quitting the editor.
|
|
func watchConfig(group string, name string, channel chan bool) {
|
|
fullpath := fmt.Sprintf("%stemplates/%s/%s.yaml", confDir, group, name)
|
|
originalStat, _ := os.Stat(fullpath)
|
|
|
|
watch := true
|
|
for watch {
|
|
newStat, _ := os.Stat(fullpath)
|
|
if newStat.ModTime() != originalStat.ModTime() {
|
|
originalStat = newStat
|
|
configName := fmt.Sprintf("%s/%s", group, name)
|
|
loadConfig(Arguments{Param: configName, Files: nil, Force: true})
|
|
}
|
|
select {
|
|
case check := <-channel:
|
|
watch = check
|
|
default:
|
|
watch = true
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
}
|
|
}
|
|
|
|
// editConfig loads a specified configuration in your favorite editor! If you have $EDITOR set.
|
|
func editConfig(arg Arguments) {
|
|
var name string
|
|
var group string
|
|
var fullpath string
|
|
|
|
split := strings.Split(arg.Param, "/")
|
|
group = split[0]
|
|
name = split[1]
|
|
jsonData := loadData(group)
|
|
|
|
if name == "reload" {
|
|
fullpath = fmt.Sprintf("%stemplates/%s/reload.sh.template", confDir, group)
|
|
|
|
} else if name == "current" {
|
|
fullpath = fmt.Sprintf("%stemplates/%s", confDir, jsonData.Current)
|
|
name = strings.Split(jsonData.Current, "/")[1]
|
|
name = strings.TrimSuffix(name, ".yaml")
|
|
arg.Param = fmt.Sprintf("%s/%s", group, name)
|
|
} else {
|
|
fullpath = fmt.Sprintf("%stemplates/%s/%s.yaml", confDir, group, name)
|
|
}
|
|
|
|
defaultConfig := fmt.Sprintf("%stemplates/%s/default.yaml", confDir, group)
|
|
_, err := os.Stat(fullpath)
|
|
if err != nil {
|
|
fileCopy(defaultConfig, fullpath)
|
|
}
|
|
|
|
watchChan := make(chan bool)
|
|
wait := false
|
|
if jsonData.Current == fmt.Sprintf("%s/%s.yaml", group, name) {
|
|
go watchConfig(group, name, watchChan)
|
|
wait = true
|
|
}
|
|
|
|
cmd := exec.Command(EDITOR, fullpath)
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Run()
|
|
|
|
if wait {
|
|
watchChan <- false
|
|
}
|
|
|
|
defaultData, _ := fileRead(defaultConfig)
|
|
currentData, _ := fileRead(fullpath)
|
|
if defaultData == currentData && name != "default" {
|
|
os.Remove(fullpath)
|
|
}
|
|
}
|
|
|
|
// createGroup creates a config group and sets up all of the default files.
|
|
func createGroup(arg Arguments) {
|
|
var group string
|
|
var files []string
|
|
var groupPath string
|
|
|
|
files = arg.Files
|
|
group = arg.Param
|
|
groupPath = fmt.Sprintf("%stemplates/%s/", confDir, group)
|
|
|
|
ok := os.Mkdir(groupPath, 0777)
|
|
if ok == nil {
|
|
fmt.Printf("Creating %s\n", groupPath)
|
|
os.Create(groupPath + "default.yaml")
|
|
os.Create(groupPath + "reload.sh.template")
|
|
os.Chmod(groupPath+"reload.sh", 0755)
|
|
confFile, _ := os.OpenFile(groupPath+"data.json", os.O_RDWR|os.O_CREATE, 0666)
|
|
|
|
data := ConfigData{Files: files}
|
|
bytes, err := json.Marshal(data)
|
|
if !checkError(err, false) {
|
|
confFile.Write(bytes)
|
|
backupFiles(files)
|
|
}
|
|
}
|
|
}
|
|
|
|
func deleteConfig(arg Arguments) {
|
|
split := strings.Split(arg.Param, "/")
|
|
group := split[0]
|
|
config := split[1]
|
|
fullpath := fmt.Sprintf("%stemplates/%s/%s.yaml", confDir, group, config)
|
|
err := os.Remove(fullpath)
|
|
checkError(err, true)
|
|
}
|
|
|
|
// reloadConfig reloads the active config.
|
|
// You only need to specify the group name for this to work.
|
|
func reloadConfig(arg Arguments) {
|
|
name := arg.Param
|
|
data := loadData(name)
|
|
config := strings.Replace(data.Current, ".yaml", "", 1)
|
|
args := Arguments{Param: config, Force: true, Files: nil}
|
|
loadConfig(args)
|
|
}
|
|
|
|
// loadConfig loads a config from a group. If the group/name matches
|
|
// the current config it will not load anything.
|
|
// If it loads the config it will then attempt to reload the config by
|
|
// running its respective reload.sh
|
|
func loadConfig(arg Arguments) {
|
|
split := strings.Split(arg.Param, "/")
|
|
group := split[0]
|
|
name := split[1]
|
|
|
|
// Check if the config you're loading is the current one.
|
|
// Unless the arg.Force is true execution will stop here.
|
|
configData := loadData(group)
|
|
if configData.Current == fmt.Sprintf("%s/%s.yaml", group, name) && arg.Force == false {
|
|
return
|
|
}
|
|
|
|
currentSplit := strings.Split(configData.Current, "/")
|
|
ignoreName := ""
|
|
if len(currentSplit) > 1 {
|
|
ignoreName = currentSplit[1]
|
|
}
|
|
|
|
// Find the name, possibly from a random list.
|
|
switch name {
|
|
case "random":
|
|
folderPath := fmt.Sprintf("%stemplates/%s", confDir, group)
|
|
dir, _ := ioutil.ReadDir(folderPath)
|
|
files := shuffleFolder(dir, ignoreName, false)
|
|
name = files[0].Name()
|
|
case ".random":
|
|
folderPath := fmt.Sprintf("%stemplates/%s", confDir, group)
|
|
dir, _ := ioutil.ReadDir(folderPath)
|
|
files := shuffleFolder(dir, ignoreName, true)
|
|
name = files[0].Name()
|
|
default:
|
|
name = name + ".yaml"
|
|
}
|
|
|
|
tstruct, err := loadTemplateData(group, name)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Iterate through the files and load the template data then copy them.
|
|
for _, filepath := range configData.Files {
|
|
templatePath := filepath + ".template"
|
|
err := templateFile(filepath, templatePath, tstruct)
|
|
checkError(err, false)
|
|
}
|
|
|
|
// Attempt to reload the config.
|
|
reloadPath := fmt.Sprintf("%stemplates/%s/reload.sh", confDir, group)
|
|
err = templateFile(reloadPath, reloadPath+".template", tstruct)
|
|
if !checkError(err, false) {
|
|
cmd := exec.Command("/bin/sh", reloadPath)
|
|
cmd.Run()
|
|
}
|
|
|
|
// Update the 'Current' config.
|
|
jsonData := loadData(group)
|
|
jsonData.Current = fmt.Sprintf("%s/%s", group, name)
|
|
dumpData(group, jsonData)
|
|
}
|
|
|
|
// LoadTemplateData loads the template information for a group / name combo.
|
|
func loadTemplateData(group string, name string) (Template, error) {
|
|
var config map[string]interface{}
|
|
var defaultConfig map[string]interface{}
|
|
|
|
// Concat the fullpath of the file
|
|
fullpath := fmt.Sprintf("%stemplates/%s/%s", confDir, group, name)
|
|
data, err := fileRead(fullpath)
|
|
if err != nil {
|
|
return Template{}, err
|
|
}
|
|
|
|
// Load the YAML data
|
|
err = yaml.Unmarshal([]byte(data), &config)
|
|
if err != nil {
|
|
return Template{}, err
|
|
}
|
|
items := parseYAML(config)
|
|
tstruct := Template{Data: items, Name: fmt.Sprintf("%s/%s", group, name)}
|
|
|
|
// Load the default YAML
|
|
defaultPath := fmt.Sprintf("%stemplates/%s/default.yaml", confDir, group)
|
|
defaultData, err := fileRead(defaultPath)
|
|
|
|
// If there arent any errors, implement the default data.
|
|
if !checkError(err, false) {
|
|
err = yaml.Unmarshal([]byte(defaultData), &defaultConfig)
|
|
if err != nil {
|
|
return Template{}, err
|
|
}
|
|
ditems := parseYAML(defaultConfig)
|
|
dstruct := Template{Data: ditems}
|
|
tstruct.FillDefaults(dstruct)
|
|
}
|
|
return tstruct, nil
|
|
}
|
|
|
|
// templateFile runs a certain file through the templating engine.
|
|
func templateFile(filepath string, templatePath string, templateStruct Template) error {
|
|
var replaced bytes.Buffer
|
|
fileData, err := fileRead(templatePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newTemplate := template.New(filepath)
|
|
newTemplate.Funcs(funcmap)
|
|
tmp, _ := newTemplate.Parse(fileData)
|
|
|
|
tmp.Execute(&replaced, templateStruct)
|
|
result := replaced.String()
|
|
os.Remove(filepath)
|
|
err = fileWrite(filepath, result)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// backupFiles iterates through a slice of files and copies them to a backup.
|
|
func backupFiles(files []string) {
|
|
for _, directory := range files {
|
|
newpath := directory + ".template"
|
|
fileCopy(directory, newpath)
|
|
}
|
|
}
|
|
|
|
// A simple filecopy. Not that much to see here.
|
|
func fileCopy(source string, dest string) {
|
|
sfile, err := os.Open(source)
|
|
checkError(err, true)
|
|
defer sfile.Close()
|
|
|
|
dfile, err := os.Create(dest)
|
|
checkError(err, true)
|
|
defer dfile.Close()
|
|
|
|
_, err = io.Copy(dfile, sfile)
|
|
checkError(err, true)
|
|
|
|
dfile.Sync()
|
|
checkError(err, true)
|
|
}
|
|
|
|
// checkError is a simple function that checks an error, returns true if
|
|
// there was an error and false otherwise.
|
|
// The user can specify a fatal param, if this is true the code will stop.
|
|
func checkError(err error, fatal bool) bool {
|
|
if err != nil {
|
|
fmt.Println("error:", err)
|
|
if fatal {
|
|
os.Exit(0)
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// shuffleFolder randomly shuffles a list of files in a folder.
|
|
// Used when the user loads a random config.
|
|
// TODO: Replace this with a random choice rather than random shuffle.
|
|
func shuffleFolder(data []os.FileInfo, ignore string, hidden bool) []os.FileInfo {
|
|
var output []os.FileInfo
|
|
for _, file := range data {
|
|
name := file.Name()
|
|
if (strings.HasPrefix(name, ".") && !hidden) || !strings.HasSuffix(name, ".yaml") || name == "default.yaml" || name == ignore {
|
|
continue
|
|
}
|
|
output = append(output, file)
|
|
}
|
|
rand.Seed(time.Now().UnixNano())
|
|
for i := range output {
|
|
j := rand.Intn(i + 1)
|
|
output[i], output[j] = output[j], output[i]
|
|
}
|
|
return output
|
|
}
|
|
|
|
// fileWrite writes to a file. Pretty simple.
|
|
func fileWrite(filepath string, data string) error {
|
|
os.Remove(filepath)
|
|
file, err := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE, 0777)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
_, err = file.Write([]byte(data))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// fileRead reads a file. Pretty simple.
|
|
func fileRead(filepath string) (string, error) {
|
|
file, err := os.Open(filepath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer file.Close()
|
|
data, err := ioutil.ReadAll(file)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(data), nil
|
|
}
|
|
|
|
// parseYAML parses the YAML configs via the flatten function,
|
|
// this will flatten all the elements into simple map[string]interface{}
|
|
// structure.
|
|
func parseYAML(config map[string]interface{}) map[string]interface{} {
|
|
items := make(map[string]interface{})
|
|
flatten(config, []string{}, items)
|
|
return items
|
|
}
|
|
|
|
// Do the heavy lifting of parsing and flattening of the YAML config files.
|
|
func flatten(level map[string]interface{}, path []string, items map[string]interface{}) {
|
|
for name, value := range level {
|
|
path = append(path, name)
|
|
fullpath := strings.Join(path, "_")
|
|
switch value.(type) {
|
|
case string:
|
|
items[fullpath] = value.(string)
|
|
case float64:
|
|
items[fullpath] = strconv.Itoa(int(value.(float64)))
|
|
case map[string]interface{}:
|
|
flatten(value.(map[string]interface{}), path, items)
|
|
case []interface{}:
|
|
values := value.([]interface{})
|
|
items[fullpath] = values
|
|
default:
|
|
fmt.Println(reflect.TypeOf(value))
|
|
}
|
|
path = path[:len(path)-1]
|
|
}
|
|
}
|
|
|
|
// Usage prints out the usage for this program.
|
|
func Usage() {
|
|
fmt.Println("Usage: gorice [-f] command [args]")
|
|
fmt.Println()
|
|
|
|
flag.PrintDefaults()
|
|
padding := 0
|
|
for command := range commands {
|
|
if len(command) > padding {
|
|
padding = len(command) + 1
|
|
}
|
|
}
|
|
|
|
fmt.Println("\nList of commands: ")
|
|
for command := range commands {
|
|
info := commands[command]
|
|
length := len(command)
|
|
|
|
strPadding := strings.Repeat(" ", (padding - length))
|
|
fmt.Printf(" info: %s%s- %s\n usage: %s %s\n\n", command, strPadding, info.Description, command, info.Usage)
|
|
}
|
|
fmt.Println()
|
|
os.Exit(0)
|
|
}
|
|
|
|
func main() {
|
|
flag.Usage = Usage
|
|
var command string
|
|
var param string
|
|
var files []string
|
|
var force bool
|
|
|
|
flag.BoolVar(&force, "f", false, "Forces a config to be reloaded.")
|
|
flag.Parse()
|
|
|
|
// Create the folder structure
|
|
os.MkdirAll(fmt.Sprintf("%stemplates/", confDir), 0777)
|
|
|
|
args := flag.Args()
|
|
|
|
if len(args) < 2 {
|
|
fmt.Println("Not enough arguments.")
|
|
return
|
|
}
|
|
|
|
command = args[0]
|
|
param = args[1]
|
|
files = args[2:]
|
|
|
|
// Call the function if it exists
|
|
fn, ok := commands[command]
|
|
if ok {
|
|
fn.Reference(Arguments{Param: param, Files: files, Force: force})
|
|
} else {
|
|
fmt.Println("Unknown command", command)
|
|
}
|
|
}
|