package main import ( "bufio" "encoding/csv" "fmt" "os" "os/user" "path/filepath" "strings" ) var swPath, fPath string /////////////////////////////// // Startup & helpers ////////////////////////////// func ifErrExit(e error, msg string) { if e != nil { fmt.Fprintf(os.Stderr, "%s: %s\n", msg, e.Error()) os.Exit(1) } } func earlyExit(msg string) { fmt.Fprintf(os.Stderr, "Error: %s\n", msg) os.Exit(1) } // validateDataPaths makes sure that the necessary data folders exist // for the user running spacewalk. It builds them if they are not // present and does an error exit if there is an error. func validateDataPaths() { xdgDataHome := os.Getenv("XDG_DATA_HOME") if xdgDataHome == "" { usr, _ := user.Current() xdgDataHome = filepath.Join(usr.HomeDir, "/.local/share") } neededFolders := filepath.Join(xdgDataHome, "spacewalk") neededFolders, _ = filepath.Abs(neededFolders) swPath = neededFolders neededFolders = filepath.Join(neededFolders, "flights") fPath = neededFolders err := os.MkdirAll(neededFolders, 0755) ifErrExit(err, "Error validation data paths") p := filepath.Join(swPath, "flight-manifest") f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) ifErrExit(err, "Could not create flight manifest") f.Close() } func displayUsage() { fmt.Println("spacewalk [stuff] [things]") os.Exit(0) } // parseArgs is the main entry point into scbm and will run the appropriate functions // based on the arguments passed in at run time. func parseArgs() { a := os.Args if len(a) == 1 { displayUsage() } switch a[1] { case "create": if len(a) == 3 { create(a[2]) } else { earlyExit("Missing flight name.\n- spacewalk create \033[3mflight\033[0m") } case "update": if len(a) == 3 { update(a[2]) } else { earlyExit("Missing flight name.\n- spacewalk update \033[3mflight\033[0m") } case "remove": if len(a) == 3 { remove(a[2]) } else { earlyExit("Missing flight name.\n- spacewalk remove \033[3mflight\033[0m") } case "add": if len(a) == 5 { add(a[2], a[3], a[4]) } else { earlyExit("Incorrect syntax.\n- spacewalk add \033[3mflight url title\033[0m") } case "delete": if len(a) == 4 { del(a[2], a[3]) } else { earlyExit("Incorrect syntax.\n- spacewalk delete \033[3mflight url|title\033[0m") } case "launch": if len(a) == 2 { launchFlights() } else if len(a) == 3 { launchFlight(a[2]) } else { earlyExit("Incorrect syntax.\n- spacewalk launch [\033[3mflight\033[0m]") } case "show": if len(a) == 2 { showFlights("") } else if len(a) == 3 { showFlights(a[2]) } else { earlyExit("Incorrect syntax.\n- spacewalk show [\033[3mflight\033[0m]") } default: earlyExit(fmt.Sprintf("Unknown command %q", a[1])) } } func getLine(prefix string) (string, error) { reader := bufio.NewReader(os.Stdin) fmt.Print(prefix) text, err := reader.ReadString('\n') if err != nil { return "", err } return text[:len(text)-1], nil } func readManifest() [][]string { p := filepath.Join(swPath, "flight-manifest") f, err := os.Open(p) ifErrExit(err, "Could not open flight manifest") defer f.Close() r := csv.NewReader(f) records, err := r.ReadAll() ifErrExit(err, "Could not read from flight manifest") return records } /////////////////////////////// // Command Execution ////////////////////////////// func create(flight string) { var lp, hp, fp string var err error records := readManifest() for _, r := range records { if r[0] == flight { earlyExit(fmt.Sprintf("There is already a flight with the name %q", flight)) } } fmt.Printf("Creating flight: %q\n", flight) happy := false for !happy { lp, err = getLine("Enter the launch path (output path including file name): ") ifErrExit(err, "Error reading from stdin") if lp == "" { fmt.Println("Launch path cannot be empty") continue } hp, err = getLine("Enter the header path, or leave blank for none: ") ifErrExit(err, "Error reading from stdin") if hp == "" { hp = "none" } fp, err = getLine("Enter the footer path, or leave blank for none: ") ifErrExit(err, "Error reading from stdin") if fp == "" { fp = "none" } fmt.Println("Are you happy with the following:") fmt.Printf("Launch path: %s\nHeader path: %s\nFooter path: %s\n", lp, hp, fp) yesNo, err := getLine("Type 'yes' to accept, anything else to redo: ") ifErrExit(err, "Error reading from stdin") if strings.ToLower(yesNo) == "yes" { happy = true } } p := filepath.Join(swPath, "flight-manifest") f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) ifErrExit(err, "Could not open flight manifest") defer f.Close() ln := fmt.Sprintf("%s, %s, %s, %s\n", flight, lp, hp, fp) _, err = f.WriteString(ln) ifErrExit(err, "Unable to save new flight to data file") fpath := filepath.Join(fPath, flight) ff, err := os.OpenFile(fpath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) ifErrExit(err, fmt.Sprintf("Could not create flight log for %s", flight)) ff.Close() } func update(flight string) { fmt.Println(flight) } func remove(flight string) { fmt.Println(flight) } func add(flight, url, title string) { p := filepath.Join(fPath, "flights", flight) f, err := os.OpenFile(p, os.O_APPEND|os.O_WRONLY, 0644) ifErrExit(err, fmt.Sprintf("Could not open flight log for %s", flight)) defer f.Close() ln := fmt.Sprintf("%s, %s, %s, %s\n", title, url, "nil", "0") _, err = f.WriteString(ln) ifErrExit(err, "Unable to save new capsule to data file") fmt.Printf("Capsule %q added to %s's flight log", title, flight) } func del(flight, item string) { fmt.Println(flight, item) } func launchFlights() { fmt.Println("Hello") } func launchFlight(flight string) { fmt.Println(flight) } func showFlights(flight string) { records := readManifest() for _, row := range records { if flight != "" && row[0] != flight { continue } fmt.Printf("\033[1mFlight Name:\033[0m %s\n", row[0]) fmt.Printf("\033[1mLaunch Path:\033[0m %s\n", row[1]) fmt.Printf("\033[1mHeader Path:\033[0m %s\n", row[2]) fmt.Printf("\033[1mFooter Path:\033[0m %s\n--\n", row[3]) } } func main() { validateDataPaths() parseArgs() }