diff --git a/.gitignore b/.gitignore index 690b419..ca124a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /bin # /.vscode *.csv +*.tsv *.md !example.csv diff --git a/.vscode/launch.json b/.vscode/launch.json index f3cc9e8..27bf946 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,10 +9,21 @@ "type": "go", "request": "launch", "mode": "auto", - "program": "main.go" - // "args": [ - // "example.csv", - // ] + "program": "main.go", + "args": [ + "-f=example.csv", + ] + }, + { + "name": "Debug csv2md (tsv)", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "main.go", + "args": [ + "-t", + "-f=example.tsv", + ] } ] -} \ No newline at end of file +} diff --git a/README.md b/README.md index 4932359..f61c16e 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,63 @@ # csv2md -Stupidly simple tool to convert csv-file to [markdown](https://spec-md.com/) table. +Stupidly simple tool to convert csv/tsv to [markdown](https://spec-md.com/) table. Outputs result in stdout. ## Usage ```shell -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 - -...anything is possible with redirection and piping +csv2md [-help|--help] [-t] [-f ] ``` -> **IMPORTANT:** -> * input must be valid csv -> * whitespaces allowed only between double-quotes +Available arguments: +* `-help` or `--help` - get help +* `-f=` or `-f ` - convert specified `FILE` +* `-t` - convert input as tsv -Examples can be found here: https://people.sc.fsu.edu/~jburkardt/data/csv/csv.html +Available `FILE` formats: +* csv (default); +* tsv (with `-t` argument). + +Path to `FILE` may be presented as: +* absolute path; +* path relative to current working directory; +* path relative to user home directory (~). + +Also if `PATH` contains whitespaces then you should double-quote it. + +To save result as separate file you can use piping. + +> **IMPORTANT:** +> 1. Input data must be valid csv/tsv +> 2. Whitespaces allowed only between double-quotes +> 3. Due to markdown spec first line of result table will always be presented as header. +> So if your raw data hasn't one you'll should add it before conversion or edit later in ready md. + +### Examples + +``` +csv2md - paste or type csv to stdin and then + press Ctrl+D to view result in stdout +csv2md -t > example.md - paste or type tsv to stdin and then + press Ctrl+D to write result in new file +csv2md -f example.csv - convert csv from file and view result in stdout +csv2md -t < example.tsv - convert tsv from stdin and view result in stdout +csv2md -t < example.tsv > example.md - convert tsv from stdin and write result in new file +cat example.csv | csv2md - convert csv from stdin and view result in stdout +csv2md -t -f=example.tsv > example.md - convert tsv from file and write result in new file +csv2md -f example.csv | less - convert csv from file and view result in stdout using pager + +...anything is possible with redirection and piping, e.g. grep, sed, awk, etc. +``` + +You can generate some examples here: [csv](https://onlinerandomtools.com/generate-random-csv), [tsv](https://onlinerandomtools.com/generate-random-tsv) ## 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. +3) Run `make help` to get help about compilation or `go run . [ARGS...]` to build and run temporary binary. ## License diff --git a/example.tsv b/example.tsv new file mode 100644 index 0000000..351df8c --- /dev/null +++ b/example.tsv @@ -0,0 +1,6 @@ +impossible political hand larger upward during +shade tip opinion star keep aside +wrong heat line pool song just +she slowly gain snow ourselves six +race thrown get yard nearest swam +though than teacher away dirt escape diff --git a/main.go b/main.go index c1b0202..ec9b26b 100644 --- a/main.go +++ b/main.go @@ -11,47 +11,40 @@ import ( "strings" ) -const VERSION = "1.1.0" +const VERSION = "1.2.0" func main() { 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) + filepath := flag.String("f", "", "File path") + is_help := flag.Bool("help", false, "Get help") + as_tsv := flag.Bool("t", false, "Parse as tsv") + flag.Parse() - 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) - } + if *is_help { + 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) - } + if *filepath == "" { + data, err := readRawCsv(*as_tsv) + if err != nil { + log.Fatal(err) + } + print(data) + } else { + src, err := ExpandPath(*filepath) + 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) + data, err := readCsvFile(src, *as_tsv) + if err != nil { + log.Fatal(err) + } + print(data) } } @@ -77,7 +70,7 @@ func ExpandPath(path string) (string, error) { } // readRawCsv read data from file -func readCsvFile(filePath string) ([][]string, error) { +func readCsvFile(filePath string, as_tsv bool) ([][]string, error) { f, err := os.Open(filePath) if err != nil { return nil, errors.New("Failed to open file '" + filePath + "': " + err.Error()) @@ -85,21 +78,30 @@ func readCsvFile(filePath string) ([][]string, error) { defer f.Close() csvReader := csv.NewReader(f) + if as_tsv { + csvReader.Comma = '\t' + } + records, err := csvReader.ReadAll() if err != nil { - return nil, errors.New("Failed to parse CSV from file '" + filePath + "': " + err.Error()) + return nil, errors.New("Failed to parse file '" + filePath + "': " + err.Error()) } return records, nil } // readRawCsv read data from stdin -func readRawCsv() ([][]string, error) { +func readRawCsv(as_tsv bool) ([][]string, error) { csvReader := csv.NewReader(os.Stdin) + if as_tsv { + csvReader.Comma = '\t' + } + records, err := csvReader.ReadAll() if err != nil { - return nil, errors.New("Failed to parse CSV from stdin: " + err.Error()) + return nil, errors.New("Failed to parse input from stdin: " + err.Error()) } + return records, nil } @@ -127,20 +129,25 @@ func convert(records [][]string) []string { func usage(writer *os.File) { usage := []string{ "csv2md v" + VERSION, - "Anthony Axenov (c) 2022, MIT license", + "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", + "\tcsv2md [-help|--help] [-t] [-f ]", + "", + "Available arguments:", + "\t-help|--help - get this help", + "\t-f=|-f - convert specified FILE", + "\t-t - convert input as TSV", + "", + "FILE formats supported:", + "\t- csv (default)", + "\t- tsv (with -t flag)", + "", + "Path to FILE may be presented as:", + "\t- absolute", + "\t- relative to current working directory", + "\t- relative to user home directory (~)", } for _, str := range usage { fmt.Fprintln(writer, str)