Initial commit
This commit is contained in:
commit
8c92ace89a
|
@ -0,0 +1,244 @@
|
|||
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()
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
# spacewalk
|
||||
|
||||
This is intended to be a golang based moku-pona-_like_ program for gemini.
|
||||
|
||||
|
||||
## commands
|
||||
|
||||
### create
|
||||
|
||||
Adds a new flight to the system's spacewalk flight list.
|
||||
|
||||
- spacewalk create _flight_
|
||||
|
||||
### update
|
||||
|
||||
Updates the settings for a flight
|
||||
|
||||
- spacewalk update _flight_
|
||||
|
||||
### remove
|
||||
|
||||
Removes an existing flight.
|
||||
|
||||
- spacewalk remove _flight_
|
||||
|
||||
### add
|
||||
|
||||
Adds a capsule to the a flight's feed. Requires the URL and a display name.
|
||||
|
||||
- spacewalk add flight _url_ _title_
|
||||
|
||||
### delete
|
||||
|
||||
Removes a capsule from the feed. Pass either a url or a display name.
|
||||
|
||||
- spacewalk delete flight _url|title_
|
||||
|
||||
### show
|
||||
|
||||
When called without further arguments, will list the available flights. When called with a flight as an argument will list the capsules for that flight.
|
||||
|
||||
- spacewalk show [flight]
|
||||
|
||||
### launch
|
||||
|
||||
Checks for updates and builds the directory capsule. Takes a flight name to launch just that flight or will launch all flights when not passed a flight name.
|
||||
|
||||
- spacewalk launch [flight]
|
||||
|
||||
|
||||
## files
|
||||
|
||||
### flight config
|
||||
|
||||
Paths: $XDG\_DATA\_HOME/spacewalk/flight-config || ~/.local/share/spacewalk/flight-config
|
||||
Format: csv
|
||||
Structure: flight-name, flight-slug, launch-path, header-path, footer-path
|
||||
|
||||
### flight data
|
||||
|
||||
Paths: $XDG\_DATA\_HOME/spacewalk/flights/{flight-slug} || ~/.local/share/spacewalk/flights/{flight-slug}
|
||||
Format: csv
|
||||
Structure: capsule-name, capsule-url, checksum, timestamp
|
||||
|
||||
|
||||
## glossary
|
||||
|
||||
capsule : a gemini site/page (a gemini URL)
|
||||
flight : a collection of capsules for which spacewalk can generate directory capsules.
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue