Refactor error messages and command line flags
This commit is contained in:
parent
19b4c30578
commit
56173fb6d4
125
main.go
125
main.go
|
@ -1,22 +1,26 @@
|
||||||
/*
|
/*
|
||||||
gfu - gophermap format utility
|
gfu - gophermap format utility
|
||||||
|
|
||||||
`gfu` manipulates gophermaps (gopher menus). It is intended to be used as part of an automation chain for managing a gopherhole using maps as the main doctype, easily allowing any document to contain links.
|
`gfu` manipulates gophermaps (gopher menus). It is intended to be used as part
|
||||||
|
of an automation chain for managing a gopherhole using maps as the main
|
||||||
|
doctype, easily allowing any document to contain links.
|
||||||
|
|
||||||
`gfu` can:
|
`gfu` can:
|
||||||
- Convert all lines in a file that are not valid gopher links into gopher info text (item type 'i') lines
|
- Convert all lines in a file that are not valid gopher links into gopher info
|
||||||
- Deconstructing all gopher info text lines in a file back to plain text for easy editing
|
text (item type 'i') lines
|
||||||
|
- Deconstruct all gopher info text lines in a file back to plain text for easy
|
||||||
|
editing
|
||||||
|
|
||||||
There are also plans to include additional features, such as:
|
There are also plans to include additional features, such as:
|
||||||
- Adding the contents of a header file into the gophermap
|
- Adding the contents of a header file into the gophermap
|
||||||
- Adding the contents of a footer file into the gophermap
|
- Adding the contents of a footer file into the gophermap *Please note - many
|
||||||
*Please note - Many servers already support includes, so the above may not be needed. If you are interested in this feature, you may want to check your server documentation first.*
|
servers already support includes, so the above may not be needed. If you are
|
||||||
*
|
interested in this feature, you may want to check your server documentation
|
||||||
|
first.*
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
|
|
||||||
For information on using `gfu`, see the help information:
|
For information on using `gfu`, see the help information: gfu --help
|
||||||
gfu --help
|
|
||||||
|
|
||||||
Information on building, downloading and installing `gfu` can be found at:
|
Information on building, downloading and installing `gfu` can be found at:
|
||||||
https://tildegit.org/sloum/gfu
|
https://tildegit.org/sloum/gfu
|
||||||
|
@ -32,19 +36,22 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var re = regexp.MustCompile(`.+\t.*\t.*\t.*`)
|
//command line flag global variables
|
||||||
|
var (
|
||||||
|
deconstructInfoTextLines bool
|
||||||
|
header string
|
||||||
|
footer string
|
||||||
|
stdout bool
|
||||||
|
)
|
||||||
|
|
||||||
func errorExit(e error, msg string) {
|
//regex global variable that identifies the format of gopher menu lines
|
||||||
if e != nil {
|
var reGopherLines = regexp.MustCompile(`.+\t.*\t.*\t.*`)
|
||||||
fmt.Print(msg)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildInfoText(ln string) string {
|
func buildInfoText(ln string) string {
|
||||||
var out strings.Builder
|
var out strings.Builder
|
||||||
|
@ -64,21 +71,35 @@ func deconstructInfoText(ln string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFile(path string, build bool) bytes.Buffer {
|
//takes a string representing the file path, reads the file, then passes the
|
||||||
|
//data for processing. returns data as a bytes.Buffer and any error
|
||||||
|
//information.
|
||||||
|
func readFile(path string) (outFile bytes.Buffer, err error) {
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
errorExit(err, fmt.Sprintf("Unable to open file for reading: %s\n", path))
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
return processFile(file, build)
|
|
||||||
|
outFile, err = processFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return outFile, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func processFile(file io.Reader, build bool) bytes.Buffer {
|
//takes data as a bytes.Buffer, reads and processes each line according to the
|
||||||
|
//deconstructInfoTextLines flag. returns processed data as a bytes.Buffer and
|
||||||
|
//any error information.
|
||||||
|
func processFile(file io.Reader) (outFile bytes.Buffer, err error) {
|
||||||
scanner := bufio.NewScanner(file)
|
scanner := bufio.NewScanner(file)
|
||||||
var outFile bytes.Buffer
|
|
||||||
|
|
||||||
if build {
|
if !deconstructInfoTextLines {
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
l := scanner.Text()
|
l := scanner.Text()
|
||||||
if exp := re.MatchString(l); exp {
|
if exp := reGopherLines.MatchString(l); exp {
|
||||||
outFile.WriteString(l)
|
outFile.WriteString(l)
|
||||||
} else {
|
} else {
|
||||||
outFile.WriteString(buildInfoText(l))
|
outFile.WriteString(buildInfoText(l))
|
||||||
|
@ -88,7 +109,7 @@ func processFile(file io.Reader, build bool) bytes.Buffer {
|
||||||
} else {
|
} else {
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
l := scanner.Text()
|
l := scanner.Text()
|
||||||
if exp := re.MatchString(l); exp && l[0] == 'i' {
|
if exp := reGopherLines.MatchString(l); exp && l[0] == 'i' {
|
||||||
outFile.WriteString(deconstructInfoText(l))
|
outFile.WriteString(deconstructInfoText(l))
|
||||||
} else {
|
} else {
|
||||||
outFile.WriteString(l)
|
outFile.WriteString(l)
|
||||||
|
@ -96,19 +117,34 @@ func processFile(file io.Reader, build bool) bytes.Buffer {
|
||||||
outFile.WriteString("\n")
|
outFile.WriteString("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := scanner.Err(); err != nil {
|
if err = scanner.Err(); err != nil {
|
||||||
errorExit(err, fmt.Sprintf("Error while reading file\n"))
|
return
|
||||||
}
|
}
|
||||||
return outFile
|
|
||||||
|
return outFile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFile(path string, outFile bytes.Buffer) {
|
//takes a file path as a string and some data as bytes.Buffer. writes the data
|
||||||
|
//to the specified file. returns any error information.
|
||||||
|
func writeFile(path string, outFile bytes.Buffer) (err error) {
|
||||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0)
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0)
|
||||||
errorExit(err, fmt.Sprintf("Unable to open file for writing: %s\n", path))
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
file.Write(outFile.Bytes())
|
file.Write(outFile.Bytes())
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//configure command line flags
|
||||||
|
func init() {
|
||||||
|
flag.BoolVar(&deconstructInfoTextLines, "d", false, "Deconstruct a gophermap's info text lines back to plain text")
|
||||||
|
flag.StringVar(&header, "head", "", "Path to a file containing header content")
|
||||||
|
flag.StringVar(&footer, "foot", "", "Path to a file containing footer content")
|
||||||
|
flag.BoolVar(&stdout, "stdout", false, "Instead of writing changes to a file, return them to stdout")
|
||||||
|
}
|
||||||
|
|
||||||
|
//PrintHelp produces a nice display message when the --help flag is used
|
||||||
func PrintHelp() {
|
func PrintHelp() {
|
||||||
art := `gfu - gophermap formatting utility
|
art := `gfu - gophermap formatting utility
|
||||||
|
|
||||||
|
@ -119,41 +155,44 @@ example: gfu -d ~/gopher/phlog/gophermap
|
||||||
default
|
default
|
||||||
Convert plain text lines to gophermap info text (item type 'i') lines
|
Convert plain text lines to gophermap info text (item type 'i') lines
|
||||||
`
|
`
|
||||||
fmt.Fprint(os.Stderr, art)
|
fmt.Fprint(os.Stdout, art)
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
//command-line flags and responses
|
//process command line flags
|
||||||
deconstructInfoTextLines := flag.Bool("d", false, "Deconstruct a gophermap's info text lines back to plain text")
|
|
||||||
header := flag.String("head", "", "Path to a file containing header content")
|
|
||||||
footer := flag.String("foot", "", "Path to a file containing footer content")
|
|
||||||
stdout := flag.Bool("stdout", false, "Instead of writing changes to a file, return them to stdout")
|
|
||||||
flag.Usage = PrintHelp
|
flag.Usage = PrintHelp
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
args := flag.Args()
|
args := flag.Args()
|
||||||
|
|
||||||
if l := len(args); l != 1 {
|
if l := len(args); l != 1 {
|
||||||
fmt.Printf("Incorrect number of arguments. Expected 1, got %d\n", l)
|
fmt.Fprintf(os.Stderr, "Incorrect number of arguments. Expected 1, got %d\n", l)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if header != "" {
|
||||||
//main program
|
|
||||||
if *header != "" {
|
|
||||||
fmt.Println("Header functionality is not built yet, proceeding with general gophermap conversion...")
|
fmt.Println("Header functionality is not built yet, proceeding with general gophermap conversion...")
|
||||||
}
|
}
|
||||||
|
|
||||||
if *footer != "" {
|
if footer != "" {
|
||||||
fmt.Println("Footer functionality is not built yet, proceeding with general gophermap conversion...")
|
fmt.Println("Footer functionality is not built yet, proceeding with general gophermap conversion...")
|
||||||
}
|
}
|
||||||
|
|
||||||
outFile := readFile(args[0], !*deconstructInfoTextLines)
|
//read and process file
|
||||||
|
outFile, err := readFile(args[0])
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Error while reading file -", err)
|
||||||
|
}
|
||||||
|
|
||||||
if *stdout {
|
//output data to stdout or file
|
||||||
|
if stdout {
|
||||||
fmt.Print(outFile.String())
|
fmt.Print(outFile.String())
|
||||||
} else {
|
} else {
|
||||||
writeFile(args[0], outFile)
|
err = writeFile(args[0], outFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("Error while writing file -", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//the end
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
26
main_test.go
26
main_test.go
|
@ -41,25 +41,25 @@ var processFileTestCases = []struct {
|
||||||
var carriagereturnTestCases = []struct {
|
var carriagereturnTestCases = []struct {
|
||||||
testInput string
|
testInput string
|
||||||
expectedOutput string
|
expectedOutput string
|
||||||
build bool
|
deconstruct bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
//Plaintext to gophermap
|
//Plaintext to gophermap
|
||||||
"A test line with a carriage return\r\n",
|
"A test line with a carriage return\r\n",
|
||||||
"iA test line with a carriage return false null.host 1\n",
|
"iA test line with a carriage return false null.host 1\n",
|
||||||
true,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
//Gophermap to plaintext
|
//Gophermap to plaintext
|
||||||
"iA test line with a cr false null.host 1\r\n",
|
"iA test line with a cr false null.host 1\r\n",
|
||||||
"A test line with a cr\n",
|
"A test line with a cr\n",
|
||||||
false,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
//HTML file
|
//HTML file
|
||||||
"hhttp://tildegit.org/sloum/bombadillo url:http://tildegit.org/sloum/bombadillo colorfield.space 70\r\n",
|
"hhttp://tildegit.org/sloum/bombadillo url:http://tildegit.org/sloum/bombadillo colorfield.space 70\r\n",
|
||||||
"hhttp://tildegit.org/sloum/bombadillo url:http://tildegit.org/sloum/bombadillo colorfield.space 70\n",
|
"hhttp://tildegit.org/sloum/bombadillo url:http://tildegit.org/sloum/bombadillo colorfield.space 70\n",
|
||||||
true,
|
false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,11 @@ func TestBuildInfoText(t *testing.T) {
|
||||||
|
|
||||||
func TestProcessFileBuild(t *testing.T) {
|
func TestProcessFileBuild(t *testing.T) {
|
||||||
for testNum, testCase := range processFileTestCases {
|
for testNum, testCase := range processFileTestCases {
|
||||||
testOutput := processFile(strings.NewReader(testCase.plaintext), true)
|
deconstructInfoTextLines = false
|
||||||
|
testOutput, err := processFile(strings.NewReader(testCase.plaintext))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error occurred: %v", err)
|
||||||
|
}
|
||||||
if testCase.gophermap != testOutput.String() {
|
if testCase.gophermap != testOutput.String() {
|
||||||
t.Errorf(`processFile build test case %d failed. Expected "%s", got "%s"`,
|
t.Errorf(`processFile build test case %d failed. Expected "%s", got "%s"`,
|
||||||
testNum,
|
testNum,
|
||||||
|
@ -93,7 +97,11 @@ func TestProcessFileBuild(t *testing.T) {
|
||||||
|
|
||||||
func TestProcessFileDecon(t *testing.T) {
|
func TestProcessFileDecon(t *testing.T) {
|
||||||
for testNum, testCase := range processFileTestCases {
|
for testNum, testCase := range processFileTestCases {
|
||||||
testOutput := processFile(strings.NewReader(testCase.gophermap), false)
|
deconstructInfoTextLines = true
|
||||||
|
testOutput, err := processFile(strings.NewReader(testCase.gophermap))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error occurred: %v", err)
|
||||||
|
}
|
||||||
if testCase.plaintext != testOutput.String() {
|
if testCase.plaintext != testOutput.String() {
|
||||||
t.Errorf(`processFile decon test case %d failed. Expected "%s", got "%s"`,
|
t.Errorf(`processFile decon test case %d failed. Expected "%s", got "%s"`,
|
||||||
testNum,
|
testNum,
|
||||||
|
@ -107,7 +115,11 @@ func TestProcessFileDecon(t *testing.T) {
|
||||||
|
|
||||||
func TestCarriageReturnsAreExpunged(t *testing.T) {
|
func TestCarriageReturnsAreExpunged(t *testing.T) {
|
||||||
for testNum, testCase := range carriagereturnTestCases {
|
for testNum, testCase := range carriagereturnTestCases {
|
||||||
testOutput := processFile(strings.NewReader(testCase.testInput), testCase.build)
|
deconstructInfoTextLines = testCase.deconstruct
|
||||||
|
testOutput, err := processFile(strings.NewReader(testCase.testInput))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error occurred: %v", err)
|
||||||
|
}
|
||||||
if testCase.expectedOutput != testOutput.String() {
|
if testCase.expectedOutput != testOutput.String() {
|
||||||
t.Errorf(`CR test case %d failed. Expected "%s", got "%s"`,
|
t.Errorf(`CR test case %d failed. Expected "%s", got "%s"`,
|
||||||
testNum,
|
testNum,
|
||||||
|
|
Loading…
Reference in New Issue