parent
a06f28f122
commit
2bb7fc611d
@ -0,0 +1,7 @@
|
||||
# CSV to Markdown Table Formatter
|
||||
|
||||
Takes a csv-like file as input and outputs a formatted markdown table with it.
|
||||
|
||||
## Usage
|
||||
|
||||
Run `md-fmt -help` for the list of command line arguments.
|
@ -0,0 +1,87 @@
|
||||
// md-fmt takes a csv or tsv input file and outputs a formatted markdown table
|
||||
// with the data.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
sourcePath = flag.String("source", "", "path to the input file")
|
||||
separator = flag.String("sep", ",", "separator character to use when reading the csv file")
|
||||
lazyQuotes = flag.Bool("lazy-quotes", false, "controls the lazy-quotes setting on the csv reader")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
fd, err := os.Open(*sourcePath)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open source file %s: %s\n", *sourcePath, err)
|
||||
}
|
||||
|
||||
sep := []rune(*separator)[0]
|
||||
read := csv.NewReader(fd)
|
||||
read.Comma = sep
|
||||
read.TrimLeadingSpace = true
|
||||
read.LazyQuotes = *lazyQuotes
|
||||
|
||||
records, err := read.ReadAll()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to load records in memory: %s\n", err)
|
||||
}
|
||||
|
||||
widths := make([]int, len(records[0]))
|
||||
for _, row := range records {
|
||||
for i, col := range row {
|
||||
widths[i] = max(widths[i], len(col))
|
||||
}
|
||||
}
|
||||
|
||||
c := make([]string, len(records[0]))
|
||||
log.Println(records[0], len(c))
|
||||
for i := 0; i < len(c); i++ {
|
||||
c[i] = " %-*s "
|
||||
}
|
||||
pattern := fmt.Sprintf("|%s|\n", strings.Join(c, "|"))
|
||||
log.Println(pattern)
|
||||
|
||||
sb := strings.Builder{}
|
||||
|
||||
// Format header row
|
||||
curr := make([]any, 0, 2*len(widths))
|
||||
for i := range widths {
|
||||
curr = append(curr, widths[i], records[0][i])
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(pattern, curr...))
|
||||
|
||||
// Format header separator row
|
||||
curr = curr[:0] // empty slice but preserve capacity
|
||||
for i := range widths {
|
||||
curr = append(curr, widths[i], strings.Repeat("-", widths[i]))
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(pattern, curr...))
|
||||
|
||||
// Format rest of records
|
||||
for i := 1; i < len(records); i++ {
|
||||
curr = curr[:0]
|
||||
for j := range widths {
|
||||
curr = append(curr, widths[j], records[i][j])
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(pattern, curr...))
|
||||
}
|
||||
|
||||
fmt.Print(sb.String())
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
Loading…
Reference in new issue