// Debby is a command line tool that links your dolt database to visidata package main import ( "fmt" "os" "os/exec" "strings" "sync" ) 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: ") fmt.Scanln(&query) } // Table name will be empty. Run query RunSQL(query, "") case 2: if table == "" { // List tables sequentially fmt.Print("Available tables: ") for _, t := range tables { fmt.Printf(" [ %s ]", t) } // Request table name fmt.Print("Enter your table name: ") fmt.Scanln(&table) } // 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 := "" fmt.Scanln(&argstr) args = strings.Split(argstr, " ") } // Execute command ExecuteDoltCommand(args...) case 4: // Exit return } // If noninteractive, exit if noninteractive { return } // End of display fmt.Print("Press enter to continue...") fmt.Scanln() // Clear screen fmt.Print("\033[H\033[2J") // Reset choice choice = 0 // Print menu fmt.Println("1. Run query") fmt.Println("2. Edit table") fmt.Println("3. Execute Dolt command") fmt.Println("4. Exit") fmt.Print("Enter your choice: ") // Read user input fmt.Scanln(&choice) // Reset all variables except choice table = "" query = "" args = []string{} } } 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") lines = lines[1:] for i, line := range lines { lines[i] = strings.TrimSpace(line) } return lines } 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() }