// Debby is a command line tool that links your dolt database to visidata package main import ( "fmt" "os" "os/exec" "strings" "sync" "bufio" ) var once = sync.Once{} func main() { var choice int var table string var query string var args []string var noninteractive bool if len(os.Args) > 1 { noninteractive = true switch os.Args[1] { case "edit": // Import table choice = 2 table = os.Args[2] case "run": // Run query choice = 1 query = strings.Join(os.Args[2:], " ") case "help": // Print help str := `debby is a command line tool that links your dolt database to visidata Usage: debby [command] [arguments] The commands are: edit Import table run Run query help Print help Runnning debby without any arguments will start debby in interactive mode. Running debby with any other arguments will run it as a dolt command.` fmt.Println(str) return } } // Menu loop. 1. Run query 2. Edit a table 3. Execute Dolt command 4. Exit for { tables := ReadTableNames() // Execute choice switch choice { case 1: if query == "" { // Request query fmt.Print("Enter your query: ") query = Readline() } // Table name will be empty. Run query RunSQL(query, "") case 2: if table == "" { // Request table name fmt.Print("Enter your table name: ") table = Readline() } // Run query RunSQL("select * from "+table+";", table) case 3: if len(args) == 0 { // Request command. Make dolt bold fmt.Print("\033[1mdolt\033[0m ") argstr := Readline() args = strings.Split(argstr, " ") } // Execute command ExecuteDoltCommand(args...) case 4: // Exit return } // If noninteractive, exit if noninteractive { return } choice = 0 // Print menu // List tables sequentially fmt.Print("Tables: ") for _, t := range tables { fmt.Printf(" %s -", t) } fmt.Println() fmt.Print("1. Run query ") fmt.Print("2. Edit table ") fmt.Print("3. Execute Dolt command ") fmt.Print("4. Exit ") fmt.Print("- Enter your choice: ") // Read user input choicestr := Readline() choice = int(choicestr[0] - '0') // Reset all variables except choice table = "" query = "" args = []string{} } } func Readline() string { // Read a line from stdin reader := bufio.NewReader(os.Stdin) text, _ := reader.ReadString('\n') return text } func ExecuteDoltCommand(args ...string) { cmd := exec.Command("dolt", args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin cmd.Run() } func SaveToTable(table, file string) error { // Run dolt table import -r table file cmd := exec.Command("dolt", "table", "import", "-r", table, file) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin return cmd.Run() } func ReadTableNames() []string { // Run dolt ls, ignore the first line and strip spaces for each following line cmd := exec.Command("dolt", "ls") cmd.Stderr = os.Stderr out, err := cmd.Output() if err != nil { return []string{} } lines := strings.Split(string(out), "\n") tables := []string{} for i, line := range lines { line = strings.TrimSpace(line) if i == 0 || line == "" { continue } tables = append(tables, line) } return tables } func RunSQL(query, table string) error { // Create a temporary file to open in visidata f, err := os.CreateTemp("", "debby-*.csv") if err != nil { return err } defer os.Remove(f.Name()) // Run the query and write the output to the file cmd := exec.Command("dolt", "sql", "-q", query, "-r", "csv") cmd.Stdout = f err = cmd.Run() if err != nil { return err } // Open the file in visidata OpenVisidata(f.Name(), "-f", "csv") // If table is not empty, save the file to the table if table != "" { fmt.Println("Saving to table...") err = SaveToTable(table, f.Name()) if err != nil { return err } } // Read the file back into memory return nil } func OpenVisidata(args ...string) { vdcmd := "echo No visidata found, trying to run: vd " once.Do(func() { // run with --version and check if it returns saul.pw/VisiData, if not try the same with visidata instead for _, cmd := range []string{"vd", "visidata"} { out, err := exec.Command(cmd, "--version").Output() if err != nil { continue } if strings.Contains(string(out), "saul.pw/VisiData") { vdcmd = cmd break } } }) cmd := exec.Command(vdcmd, args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin cmd.Run() }