diff --git a/.gitignore b/.gitignore index 4033d3e..690b419 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /bin # /.vscode *.csv +*.md !example.csv diff --git a/.vscode/launch.json b/.vscode/launch.json index 9ceee76..f3cc9e8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,19 +2,17 @@ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - { - "version": "0.2.0", - "configurations": [ - { - "name": "Launch Package", - "type": "go", - "request": "launch", - "mode": "auto", - "program": "${fileBasename}", - "args": [ - "example.csv", - ] - } - ] - } + "version": "0.2.0", + "configurations": [ + { + "name": "Debug csv2md", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "main.go" + // "args": [ + // "example.csv", + // ] + } + ] } \ No newline at end of file diff --git a/README.md b/README.md index 8c624ae..4932359 100644 --- a/README.md +++ b/README.md @@ -4,20 +4,35 @@ Stupidly simple tool to convert csv-file to [markdown](https://spec-md.com/) tab Outputs result in stdout. -Building: +## Usage ```shell -make help -``` - -Usage: +csv2md (-h|-help|--help|help) # to get this help +csv2md example.csv # convert data from file and write result in stdout +csv2md < example.csv # convert data from stdin and write result in stdout +cat example.csv | csv2md # convert data from stdin and write result in stdout +csv2md example.csv > example.md # convert data from file and write result in new file +csv2md example.csv | less # convert data from file and write result in stdout using pager +csv2md # paste or type data to stdin by hands + # press Ctrl+D to view result in stdout +csv2md > example.md # paste or type data to stdin by hands + # press Ctrl+D to write result in new file -```shell -csv2md example.csv > example.md # makes new file -csv2md example.csv | less # view result using *pager* ...anything is possible with redirection and piping ``` -> **IMPORTANT:** input must be valid csv and whitespaces are allowed only between double-quotes. +> **IMPORTANT:** +> * input must be valid csv +> * whitespaces allowed only between double-quotes Examples can be found here: https://people.sc.fsu.edu/~jburkardt/data/csv/csv.html + +## Compilation + +1) [Install go](https://go.dev/learn/). +2) Download this repo via zip or `git clone`. +3) Run `make help` to get help or `go run . ` to build and run temporary binary. + +## License + +[MIT](LICENSE) diff --git a/example.csv b/example.csv index 338cc90..5995bdb 100644 --- a/example.csv +++ b/example.csv @@ -1,14 +1,50 @@ -"Month","1958","1959","1960" -"JAN",340,360,417 -"FEB",318,342,391 -"MAR",362,406,419 -"APR",348,396,461 -"MAY",363,420,472 -"JUN",435,472,535 -"JUL",491,548,622 -"AUG",505,559,606 -"SEP",404,463,508 -"OCT",359,407,461 -"NOV",310,362,390 -"DEC",337,405,432 - +"Name","Team","Position","Height(inches)","Weight(lbs)","Age" +"Adam Donachie","BAL","Catcher",74,180,22.99 +"Paul Bako","BAL","Catcher",74,215,34.69 +"Ramon Hernandez","BAL","Catcher",72,210,30.78 +"Kevin Millar","BAL","First Baseman",72,210,35.43 +"Chris Gomez","BAL","First Baseman",73,188,35.71 +"Brian Roberts","BAL","Second Baseman",69,176,29.39 +"Miguel Tejada","BAL","Shortstop",69,209,30.77 +"Melvin Mora","BAL","Third Baseman",71,200,35.07 +"Aubrey Huff","BAL","Third Baseman",76,231,30.19 +"Adam Stern","BAL","Outfielder",71,180,27.05 +"Jeff Fiorentino","BAL","Outfielder",73,188,23.88 +"Freddie Bynum","BAL","Outfielder",73,180,26.96 +"Nick Markakis","BAL","Outfielder",74,185,23.29 +"Brandon Fahey","BAL","Outfielder",74,160,26.11 +"Corey Patterson","BAL","Outfielder",69,180,27.55 +"Jay Payton","BAL","Outfielder",70,185,34.27 +"Jay Gibbons","BAL","Designated Hitter",72,197,30 +"Erik Bedard","BAL","Starting Pitcher",73,189,27.99 +"Hayden Penn","BAL","Starting Pitcher",75,185,22.38 +"Adam Loewen","BAL","Starting Pitcher",78,219,22.89 +"Daniel Cabrera","BAL","Starting Pitcher",79,230,25.76 +"Steve Trachsel","BAL","Starting Pitcher",76,205,36.33 +"Jaret Wright","BAL","Starting Pitcher",74,230,31.17 +"Kris Benson","BAL","Starting Pitcher",76,195,32.31 +"Scott Williamson","BAL","Relief Pitcher",72,180,31.03 +"John Parrish","BAL","Relief Pitcher",71,192,29.26 +"Danys Baez","BAL","Relief Pitcher",75,225,29.47 +"Chad Bradford","BAL","Relief Pitcher",77,203,32.46 +"Jamie Walker","BAL","Relief Pitcher",74,195,35.67 +"Brian Burres","BAL","Relief Pitcher",73,182,25.89 +"Kurt Birkins","BAL","Relief Pitcher",74,188,26.55 +"James Hoey","BAL","Relief Pitcher",78,200,24.17 +"Sendy Rleal","BAL","Relief Pitcher",73,180,26.69 +"Chris Ray","BAL","Relief Pitcher",75,200,25.13 +"Jeremy Guthrie","BAL","Relief Pitcher",73,200,27.9 +"A.J. Pierzynski","CWS","Catcher",75,245,30.17 +"Toby Hall","CWS","Catcher",75,240,31.36 +"Paul Konerko","CWS","First Baseman",74,215,30.99 +"Tadahito Iguchi","CWS","Second Baseman",69,185,32.24 +"Juan Uribe","CWS","Shortstop",71,175,27.61 +"Alex Cintron","CWS","Shortstop",74,199,28.2 +"Joe Crede","CWS","Third Baseman",73,200,28.85 +"Josh Fields","CWS","Third Baseman",73,215,24.21 +"Ryan Sweeney","CWS","Outfielder",76,200,22.02 +"Brian N. Anderson","CWS","Outfielder",74,205,24.97 +"Luis Terrero","CWS","Outfielder",74,206,26.78 +"Pablo Ozuna","CWS","Outfielder",70,186,32.51 +"Scott Podsednik","CWS","Outfielder",72,188,30.95 +"Jermaine Dye","CWS","Outfielder",77,220,33.09 diff --git a/main.go b/main.go index d5d5821..c1b0202 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,8 @@ package main import ( "encoding/csv" + "errors" + "flag" "fmt" "log" "os" @@ -9,33 +11,62 @@ import ( "strings" ) -const VERSION = "1.0.0" +const VERSION = "1.1.0" func main() { - if len(os.Args) < 2 { - usage() - os.Exit(1) - } + log.SetFlags(0) + switch len(os.Args) { + case 1: // first we read data from stdin and then convert it + data, err := readRawCsv() + if err != nil { + log.Fatal(err) + } + print(data) - src, err := ExpandPath(os.Args[1]) - if err != nil { - panic(err) - } - if _, err := os.Stat(src); err != nil { - panic(err) + case 2: // but if 2nd arg is present + // probably user wants to get help + help1 := flag.Bool("h", false, "Get help") + help2 := flag.Bool("help", false, "Get help") + flag.Parse() + if os.Args[1] == "help" || *help1 || *help2 { + usage(os.Stdout) + os.Exit(0) + } + + // ...or to convert data from file + src, err := ExpandPath(os.Args[1]) + if err != nil { + log.Fatal(err) + } + if _, err := os.Stat(src); err != nil { + log.Fatal(err) + } + + data, err := readCsvFile(src) + if err != nil { + log.Fatal(err) + } + print(data) + + // otherwise let's show usage help and exit (probably inaccessible code, but anyway) + default: + usage(os.Stdout) + os.Exit(0) } +} - result := convert(readCsv(src)) +// print write converted data to stdout +func print(data [][]string) { + if len(data) == 0 { + usage(os.Stderr) + } + result := convert(data) for _, row := range result { fmt.Println(row) } } -func usage() { - fmt.Fprintln(os.Stderr, "csv2md v" + VERSION) - fmt.Fprintln(os.Stderr, "Usage: csv2md data.csv > data.md") -} - +// ExpandPath return absolute path to file replacing ~ to user's home folder func ExpandPath(path string) (string, error) { homepath, err := os.UserHomeDir() if err != nil { @@ -45,22 +76,34 @@ func ExpandPath(path string) (string, error) { return newpath, err } -func readCsv(filePath string) [][]string { +// readRawCsv read data from file +func readCsvFile(filePath string) ([][]string, error) { f, err := os.Open(filePath) if err != nil { - log.Fatal("Unable to read input file " + filePath, err) + return nil, errors.New("Failed to open file '" + filePath + "': " + err.Error()) } defer f.Close() csvReader := csv.NewReader(f) records, err := csvReader.ReadAll() if err != nil { - log.Fatal("Unable to parse file as CSV for " + filePath, err) + return nil, errors.New("Failed to parse CSV from file '" + filePath + "': " + err.Error()) } - return records + return records, nil +} + +// readRawCsv read data from stdin +func readRawCsv() ([][]string, error) { + csvReader := csv.NewReader(os.Stdin) + records, err := csvReader.ReadAll() + if err != nil { + return nil, errors.New("Failed to parse CSV from stdin: " + err.Error()) + } + return records, nil } +// convert format data from file or stdin as markdown func convert(records [][]string) []string { var result []string for idx, row := range records { @@ -79,3 +122,27 @@ func convert(records [][]string) []string { } return result } + +// usage print help into writer +func usage(writer *os.File) { + usage := []string{ + "csv2md v" + VERSION, + "Anthony Axenov (c) 2022, MIT license", + "https://github.com/anthonyaxenov/csv2md", + "", + "Usage:", + "\tcsv2md (-h|-help|--help|help) # to get this help", + "\tcsv2md example.csv # convert data from file and write result in stdout", + "\tcsv2md < example.csv # convert data from stdin and write result in stdout", + "\tcat example.csv | csv2md # convert data from stdin and write result in stdout", + "\tcsv2md example.csv > example.md # convert data from file and write result in new file", + "\tcsv2md example.csv | less # convert data from file and write result in stdout using pager", + "\tcsv2md # paste or type data to stdin by hands", + "\t # press Ctrl+D to view result in stdout", + "\tcsv2md > example.md # paste or type data to stdin by hands", + "\t # press Ctrl+D to write result in new file", + } + for _, str := range usage { + fmt.Fprintln(writer, str) + } +}